Typescript 基础
1、什么是Typescript?
编程语言有以下两种类型:
- 动态类型语言: 只有在运行时才能确定变量的类型 (例如
JS/Python) - 静态类型语言: 在编译时就已经确定了变量类型,以及能够检查出错误(例如
c++/c/c#/java)
TS 的特点:
- Javascript that scales (JS的超集)
- 静态类型风格的类型系统
- 从
es6到es10甚至是esnext的语法支持 - 兼容各种浏览器,各种系统,各种服务器,完全开源
2、为什么要使用 TS ?
- 程序更容易理解:容易知道函数的输入输出类型及外部条件,出错几率小
- 编译期间能够发现大部分错误
- 完全兼容JS
3、基础使用
新建 test.ts 文件
const hello = (name:string) => {
return `Hello ${name}`
}
hello('Axton Tang')
使用 tsc 命令进行编译成 js 文件
该函数要求传入的参数为 string 类型,如何传入其他类型,编译则会报错
(1)原始数据类型和 Any 类型
let isDone: boolean = true
let age: number = 20
let firstName: string = 'Axton'
let message: string = `hello ${firstName}`
message = null // null 和 undefined 是任何类型的子类型
let n: null = null
let u: undefined = undefined
let notSure: any = 20 // 当不确定传入的参数是什么类型时,可以用 any (不建议使用)
notSure = 'maybe a string'
notSure = true
notSure.myName
notSure.getName()
(2)数组和元组
let arrOfNumber: number[] = [1,2,3] //定义number类型的数组
arrOfNumber.push(3)
function test () {
console.log(arguments) //类数组
}
let user: [string, number] = ['Axton', 20] // 定义元组,与数据类型只能一一对应
user.push('Tang') // 只能是上面两种数据类型之一
(3)Interface 接口
对对象的形状进行描述
interface Person {
name: string, //定义接口的属性
age?: number //如何该属性可选就可以改为 '?:' 这样如何实现该属性也不会报错
readonly sex: string //设置只读属性
}
let Axton: Person = { //声明变量, 实现这个接口
name: 'Axton Tang', // 必须与接口的属性一致
age: 20,
sex: '男'
}
(4)函数
const add = (x: number, y:number, z?: number): number => {
if (typeof z === 'number') {
return x+y+z
} else {
return x+y
}
}
add(1,2,3)
interface ISum { //用接口来描述函数类型
(x: number, y: number, z?: number): number
}
let add2: ISum = add
(5)类型推论 联合类型和 类型断言
1、类型推论: 在没有明确指明变量的类型时,TS 会根据你第一次的赋值推测出这个变量的类型,之后再进行赋值成其他类型会报错
2、联合类型:
当变量是联合类型时,只能使用联合类型共有的属性和方法
let str = 'string'
// 联合类型
let numberOrString: number | string
numberOrString = 123
numberOrString = 'Axton'
3、类型断言:
// 由于使用联合类型后只能用共有属性和方法,但是有时候想用特有的方法,就可以使用类型断言
// 方法一
function getLength(input: string | number): number {
const str = input as string // 注意: 此处并不是把它转化成这个类型
if (str.length) {
return str.length
} else {
const number = input as number
return number.toString().length
}
}
// 方法二
//type guard
function getLength2(input: string | number): number {
if (typeof input === 'string') { // 使用条件判断会自动缩小范围,可以使用该类型特有方法
return input.length
} else {
return input.toString().length
}
}
(6)类 Class
class Animal {
readonly name: string;
constructor(name) {
this.name = name
}
run() {
return `${this.name} is running`
}
}
const snake = new Animal('lily')
console.log(snake.run())
class Dog extends Animal {
bark() {
return `${this.name} is barking`
}
}
const xiaobao = new Dog('xiaobao')
console.log(xiaobao.run())
console.log(xiaobao.bark())
class Cat extends Animal {
static categories = ['mammal']
constructor(name) {
super(name)
console.log(this.name)
}
run() {
return 'Meow, ' + super.run()
}
}
const maomao = new Cat('maomao')
console.log(maomao.run())
console.log(Cat.categories)
(7)类和接口
interface Redio {
switchRedio(trigger: boolean):void;
}
interface Battery {
checkBatteryStatus(): void;
}
interface RedioWithBattery extends Redio { // 接口继承接口
checkBatteryStatus(): void;
}
class car implements Redio {
switchRedio(trigger: boolean) {
...
}
}
class cellPhone implements RedioWithBattery {
switchRedio(trigger: boolean) {
...
}
checkBatteryStatus() {
...
}
}
(8)枚举(Enum)
enum Direction { // 如果在enum前面加上 const 则为常量枚举
up = 10, // 值默认从 0 开始, 此处赋值后从 10 开始增加
down,
left,
right
}
console.log(Direction.up) // 值为 10
enum Direction2 {
str1 = 'STR1',
str2 = 'STR2'
}
const value = 'STR1'
if (value === Direction2.str1) {
console.log('go str1')
}
(9)泛型(Generics)
function echo<T>(arg: T): T {
return arg
}
const result = echo(true) // 只有当参数传递时才能确定类型,传入什么类型返回什么类型
function swap<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]]
}
const result2 = swap(['string', 123])
约束泛型
由于使用泛型时不确定参数的类型,所以不能直接使用传入参数类型的方法和属性,使用时继承需要用到的接口的方法和属性即可使用。
interface IWithLength {
length: number
}
function echoWithLength<T extends IWithLength> (arg: T): T {
console.log(arg.length)
return arg
}
const str = echoWithLength('str')
const obj = echoWithLength({length: 10, width: 20})
const arr = echoWithLength([1, 2, 3])
泛型在类中的使用
class Queue<T> {
private data = []
push (item: T) {
return this.data.push(item)
}
pop () {
return this.data.shift()
}
}
const queue = new Queue<number>()
queue.push(123)
console.log(queue.pop().toFixed())
泛型在接口中的使用
interface KeyPair<T, U> {
key: T,
value: U
}
let kp1: KeyPair<string, number> = {key: 'string', value: 20}
let kp2: KeyPair<number, string> = {key: 10, value: 'str'}
// 定义 number 类型的数组的两种方法
let arr1: number[] = [1, 2, 3]
let arr2: Array<number> = [1, 2, 3]
(10)类型别名、字面量、交叉类型
// 类型别名
type plusType = (x: number, y: number) => number
let sum: plusType
const result1 = sum(1, 2)
type numOrStr = number | string
let result2: numOrStr = 'str'
result2 = 123
// 字面量
const str: 'name' = 'name'
const number: 1 = 1
type Directions = 'up' | 'down' | 'left' | 'right'
let toWhere: Directions = 'up'
// 交叉类型
interface IName {
name: string
}
type IPerson = IName & {age: number}
let person: IPerson = {name: 'Axton', age: 20}
(11)声明文件
在使用一些第三方库的时候,ts 并不知道我们引入的是什么文件,所以会报错
此时需要定义声明文件
// 声明文件一般放到以 .d.ts 结尾的文件中
declare var jQuery: (selector: string) => any;
// 例:使用
jQuery('#foo')
很多第三方库都会有现成的声明文件,我们通过 npm 安装就好