TypeScript入门
TypeScript
1. 初识TypeScript
1.1 优化编译
-
npm i typescript -g:全局安装tsc指令用于执行ts文件。
-
tsc --init:初始化TS配置文件。
-
tsc --watch:启动热更新。
1.2 基本类型
-
基元类型
String,Number和Boolean。tsxlet str: string = 'hello World' let num: number = 123 let bool: boolean = true
1.3 数组类型
-
数组类型通过
Array<type>和type[]两种方式定义。tsxlet arr1: number[] = [1, 2, 3] let arr2: Array<number> = [1, 2, 3]
1.4 any类型
- any可以是任意类型的数据。
- any可以重新赋值为任意类型的数据。
1.5 函数类型
-
函数参数可以设置类型。
-
函数的返回值可以设置类型。
tsxfunction greet(name: string): number { console.log(`Hello, ${name}`.toLocaleUpperCase()); return 0 } greet('123')
- 可以限制函数的参数和返回值。
1.6 对象类型
-
对象类型可以为单个属性设置类型。
-
对象类型的属性可以设置为选填类型。
typescriptfunction printCoord(point: { x: number, y: number }) { console.log(`坐标点为(${point.x},${point.y})`); } printCoord({ x: 3, y: 7 }) function printName(obj: { first: string, last?: string }) { console.log(`我的名字是${obj.first}${obj.last?.toLocaleUpperCase()}`); } printName({ first: 'zhou', last: 'kai' })
1.7 联合类型
-
通过
|符号可以设置联合类型。tsxfunction printId(id: number | string) { //console.log(`Your ID is ${id}`); if (typeof id === 'number') { console.log(`Your ID is ${id}`); } else { console.log(`Your ID is ${id.toLocaleUpperCase()}`); } } printId(123) printId('zhou')
- 注意:使用联合类型需要判断类型后再使用对应的方法。
1.8 类型别名
-
可以通过
type关键字设置类型别名方便复用。tsxtype Point = { x: number, y: number } function printCoord(point: Point) { console.log(`坐标点为(${point.x},${point.y})`); } printCoord({ x: 3, y: 7 }) type ID = number | string function printId(id: ID) { //console.log(`Your ID is ${id}`); if (typeof id === 'number') { console.log(`Your ID is ${id}`); } else { console.log(`Your ID is ${id.toLocaleUpperCase()}`); } } printId(123) printId('zhou')
1.9 类型扩展
-
可以使用
interface和extends对接口类型进行扩展。tsx/// 扩展接口 interface Animal { name: string } interface Bear extends Animal { say: boolean } const bear: Bear = { name: 'anmi', say: false } -
可以通过
&符号对type类型别名进行拓展。tsxtype Animal = { name: string } type Bear = Animal & { say: boolean } const bear : Bear = { name: 'anmi', say: true }
1.10 类型断言
-
通过
as关键字或者在值前面通过<type>来断言变量的类型。tsxconst myCanvas = document.getElementById('main_canvas') as HTMLCanvasElement const myCanvas2 = <HTMLCanvasElement>document.getElementById('main_canvas') let db = 123 as unknown db = 'zhou'
1.11 字面量类型
-
可以直接使用字面量对数据进行类型设置。
tsxlet str1: 'left' | 'center' | 'right' = 'left' let num1: 1 | 0 | -1 = 0 let bool1: true | 'auto' | 1 = 1 -
通过类型断言可以将对象中属性的类型断言为字面量类型进行传参。
tsxfunction handleRequest(url: string, method: 'GET' | 'POST') { console.log(`请求${url},类型为${method}`); } const req = { url: 'https//example.com', method: 'GET' } as const handleRequest(req.url, req.method)
1.12 null和unfined类型
tsxlet un: undefined = undefined let nu: null = null function liveDangerously(x?: number | null) { console.log(x?.toFixed(2)); }
1.13 bigint和symbol类型
tsxconst oneHundred: bigint = BigInt(100) const antherHundred: bigint = 100n const firstName = Symbol('name') const secondName = Symbol('name')
- 注意:需要在
es2020及以上的版本才可以使用BigInt。
2. 入门TypeScript
2.1 枚举
-
使用
enum关键字进行枚举,枚举内容会根据第一个的值依次递增。tsxenum Direction { Up = 1, Down, Left, Right } console.log(Direction.Up); // 1 console.log(Direction[1]); // Up转义后的JS
jsvar Direction; (function (Direction) { Direction[Direction["Up"] = 1] = "Up"; Direction[Direction["Down"] = 2] = "Down"; Direction[Direction["Left"] = 3] = "Left"; Direction[Direction["Right"] = 4] = "Right"; })(Direction || (Direction = {})); console.log(Direction.Up);
2.2 类型缩小
tsxfunction padLeft(padding: string | number, input: string) { if (typeof padding === 'number') { return new Array(padding + 1).join(' ') + input } return padding + input }
-
通过
typeof对类型进行缩小范围,用于精确表示类型。 -
可以通过
!符号进行真值缩小,用于判断是否为null或undefined。tsxfunction multiplyAll( values: Array<number> | undefined, factor: number ) { if (!values) { return values } else { return values.map((v) => { return v * factor }) } } console.log(multiplyAll([1, 2, 3, 4], 5)); -
可以通过
=符号判断类型是否相等或者不等来缩小类型范围。 -
可以使用
in操作符缩小对象属性中类型范围。tsxtype Fish = { swim: Function } type Bird = { fly: () => string } type Human = { swim?: () => void; fly?: () => void } function move(animal: Fish | Bird | Human) { if ('swim' in animal) { return (animal as Fish).swim() } return (animal as Bird).fly() } console.log(move({ fly() { console.log('fly'); return "zhoukai" } })); -
可以使用
instance操作符来检验原型链对类型进行缩小。 -
可以使用
never类型进行穷尽性检查,用于是否排除干净所有类型。
2.3 类型谓词
-
is关键字一般用于函数返回值类型中,判断参数是否属于某一类型,并根据结果返回对应的布尔类型。tsxtype Fish = { name: string, swim: Function } type Bird = { name: string, fly: Function } function isFish(pet: Fish | Bird): pet is Fish { return (pet as Fish).swim !== undefined } function getSmallPet(): Fish | Bird { let fish: Fish = { name: 'sharkey', swim() { console.log('my is sharkey'); } } let bird: Bird = { name: 'sparrow', fly() { console.log('my is sparrow'); } } return Math.random() < 0.5 ? fish : bird } let pet = getSmallPet() if (isFish(pet)) { pet.swim() } else { pet.fly() } const zoo: (Fish | Bird)[] = [getSmallPet(), getSmallPet(), getSmallPet()] const underWater1: Fish[] = zoo.filter(isFish) const underWater2: Fish[] = zoo.filter(isFish) as Fish[] console.log(zoo, underWater1);
is关键字经常用来封装”类型保护函数”,通过和函数返回值的比较,从而缩小参数的类型范围,所以类型谓词 is 也是一种类型保护。
2.4 有歧义的联合类型
当接口有多个属性存在不确定时,可能出现歧义。如下:
jsinterface Shape { kind: 'circle' | 'square', radius?: number, sideLength?: number } function getArea(shape: Shape) { switch (shape.kind) { case 'circle': return Math.PI * (shape.radius) ** 2 // “shape.radius”可能为“未定义”。 case 'square': return (shape.sideLength) ** 2 // “shape.sideLength”可能为“未定义”。 } }
解决方法:
方法一:真值校验
jsinterface Shape { kind: 'circle' | 'square', radius?: number, sideLength?: number } function getArea(shape: Shape) { switch (shape.kind) { case 'circle': return Math.PI * (shape.radius || 0) ** 2 // 对shape.radius进行真值校验 case 'square': return (shape.sideLength || 0) ** 2 // 对shape.sideLength进行真值校验 } }
方法二:接口进行拆分处理
jsinterface Circle { kind: 'circle', radius: number } interface Square { kind: 'square', sideLength: number } type Shape = Circle | Square function getArea(shape: Shape) { switch (shape.kind) { case 'circle': return Math.PI * shape.radius ** 2 case 'square': return shape.sideLength ** 2 } }
2.5 never类型的穷尽性检查
never类型表示的是那些永不存在的值的类型。
jsinterface Circle { kind: 'circle' radius: number } interface Square { kind: 'square' sideLength: number } interface Triangle { kind: 'triangle' height: number width: number } type Shape = Circle | Square | Triangle function assertNever(x: never): never { throw new Error("Unexpected object: " + x); } function getArea(shape: Shape) { switch (shape.kind) { case 'circle': return Math.PI * shape.radius ** 2 case 'square': return shape.sideLength ** 2 //case 'triangle': return shape.width * shape.height / 2 default: return assertNever(shape) // 类型“Triangle”的参数不能赋给类型“never”的参数。 } }
2.6 函数
2.6.1 基本写法
函数可以声明类型,可以给每个参数添加类型之后再为函数本身添加返回值类型。
jstype GreetFunction = (a: string) => void function greeter(fn: GreetFunction) { fn('Hello World!') } function printToConsole(s: string) { console.log(s); } greeter(printToConsole)
2.6.2 函数签名
使用type声明函数类型。
tsxtype DescribableFunction = { description: string (someArg: number): boolean } function doSomething(fn: DescribableFunction) { console.log(fn.description + ' returned ' + fn(5)); } function fn1(n: number) { console.log(n) return true } fn1.description = 'Hello' doSomething(fn1)
编译后:
jsfunction doSomething(fn) { console.log(fn.description + ' returned ' + fn(5)); } function fn1(n) { console.log(n); return true; } fn1.description = 'Hello'; doSomething(fn1);
2.6.3 构造签名
通过new关键字进行构造签名。
tsxclass Ctor { s: string constructor(s: string) { this.s = s } } interface SomeConstructor { new(s: string): Ctor } function fn(ctor: SomeConstructor) { return new ctor('hello') } console.log(fn(Ctor).s); // hello