Typescript语法篇
1.typescript的原始类型
- boolean
- number
- string
- void(只有
null
和undefined
可以赋给void
) - undefined 和 null(是所有类型的子类型,严格模式下只能赋值给对应的类型或者any)
- symbol
- bigint
2.Typescript 中其他常见类型
- any(为任意类型)
- 变量如果在声明的时候,未指定其类型或者初始化,那么它会被识别为any类型
unknown
较any类型更安全s
该类型变量被确定为某一类型前,不能进行任何操作
1
2
3
4
5
6let value: unknown;
value.foo.bar; // ERROR
value(); // ERROR
new value(); // ERROR
value[0][1]; // ERROR
never
永不存在值的类型
是任何类型的子类型,可以赋值给任何类型
没有类型是 never 的子类型,任意类型都不能赋值给never类型(包括any)
常用在 抛出异常的函数 和 空数组且永远为空数组
数组类型(array)
1
2
3
4// 有两种定义方式
const list: Array<number> = [1, 2, 3] // 泛型
const list: number[] = [1, 2, 3]元组(tuple)
- 已知元素数量及类型的数组
1
let x: [string, number]; // 两个元素,类型顺序也不能变
- ts允许元组使用数组的push方法,但我们访问新加入的元素时会报错
1
2
3
4const tuple: [string, number] = ['a', 1];
tuple.push(2); // ok
console.log(tuple); // ["a", 1, 2] -> 正常打印出来
console.log(tuple[2]); // Tuple type '[string, number]' of length '2' has no element at index '2'- 已知元素数量及类型的数组
object
3.枚举类型
1 | enum Direction { |
不带初始化的枚举 要么放在第一个位置, 要么在被数字常量或其它常量初始化的枚举后面,否则会报错
3.1 枚举的本质
枚举类型被编译为 JavaScript的形式如下所示,是具有双向映射的特性(字符串类型除外)
1 | var Direction; |
3.2 const声明的枚举
1 | const a = Direction.Up; |
会被编译为
1 | var a = 0; |
3.2 联合枚举与枚举成员的类型
如果枚举成员均有字面量类型组成,那么枚举的每个成员和枚举值本身都可以作为类型来使用
- 任何字符串字面量(例如:
"foo"
,"bar"
,"baz"
) - 任何数字字面量(例如:
1
,100
) - 应用了一元
-
符号的数字字面量(例如:-1
,-100
)
1 | declare let a: Direction |
3.3 枚举合并
如果基于之前的Direction再定义了一个枚举类型的Direction,会合并成一整个
1 | enum Direction { |
5.接口(interface)
5.1 属性修饰符
1 | interface User { |
- ?:可选属性
- readonly:只读属性
5.2 属性检查
下面这个程序已经正确地类型化了,因为width
属性是兼容的,不存在color
属性,而且额外的colour
属性是无意义的
1 | interface Config { |
注意我们传入的参数是 colour
,并不是 color
官方文档给了三种方式绕过这种检查
第一种使用类型断言:
1 | let mySquare = createSquare({ colour: "red", width: 100 } as Config); |
第二种添加字符串索引签名:
1 | interface Config { |
这样Config可以有任意数量的属性,并且只要不是width或color,那么就无所谓他们的类型是什么了。
第三种将字面量赋值给另外一个变量:
1 | let options: any = { widdth: 5 }; |
5.3 可索引类型
TypeScript支持两种索引签名:字符串和数字。 可以同时使用两种类型的索引,但是数字索引的返回值必须是字符串索引返回值类型的子类型。 这是因为当使用 number
来索引时,JavaScript会将它转换成string
然后再去索引对象。 也就是说用 100
(一个number
)去索引等同于使用"100"
(一个string
)去索引,因此两者需要保持一致。
1 | class Animal { |
索引类型查询操作符
keyof
,即索引类型查询操作符,我们可以用 keyof 作用于泛型T
上来获取泛型T上的所有 public 属性名构成联合类型。举个例子,有一个Images类,包含src
和alt
两个public属性,我们用keyof
取属性名:
1 | class Images { |
效果如下:
![2019-06-26-06-17-29](D:\OneDrive - mail2.gdut.edu.cn\typora_img\Typescript语法篇\16dbb13efd03fd86)keyof
正是赋予了开发者查询索引类型的能力。
映射类型
映射类型的语法是[K in Keys]
如果我们要把所有的属性成员变为可选类型,那么需要T[K]
取出相应的属性值,最后我们重新生成一个可选的新类型{ [K in keyof T]?: T[K] }
。
用类型别名表示就是:
1 | type partial<T> = { [K in keyof T]?: T[K] } |
5.4 继承接口
1 | interface VIPUser extends User { |
VIPUser具有User所有的属性
6.类(Class)
6.1抽象类
通常作为派生类的基类使用,与接口不同的是抽象类可以包含成员的实现
下面定义了一个Animal抽象类
1 | abstract class Animal { |
如果直接实例化Animal抽象类则会报错,我们可以创建子类继承基类,然后实例化子类
1 | class Cat extends Animal { |
6.2 访问限定符
- public
- 类的成员默认为public
- 可被外部访问
- private
- 只能类内部访问
- protected
- 只能被类的内部以及类的子类访问
6.3 存取器
属性具有get和set修饰符,只带有get
不带有set
的存取器自动被推断为readonly
7. 函数(Function)
7.1 函数类型
在小括号后面声明返回值类型
1 | function add(x: number, y: number): number { |
用接口定义函数的形状
我们也可以使用接口的方式来定义一个函数需要符合的形状:
1 | interface SearchFunc { |
采用函数表达式|接口定义函数的方式时,对等号左侧进行类型限制,可以保证以后对函数名赋值时保证参数个数、参数类型、返回值类型不变。
7.2 可选参数
利用?
或则默认值
设置可选参数,?可选参数必须放最后,默认值没必要放最后,但是不放最后必须使用undefined显式获取默认值
1 | function add(x: number, y?: number): number { |
7.3 剩余参数
剩余参数与JavaScript种的语法类似,需要用...
来表示剩余参数,而剩余参数rest
则是一个由number组成的数组,在本函数中用 reduce 进行了累加求和。
1 | const add = (a: number, ...rest: number[]) => rest.reduce(((a, b) => a + b), a) |
7.4 this参数
如果直接使用this进行一些操作,typescript会进行报错,可以提供一个显式的this
参数,该参数是假的,但是可以使重用变得清晰
1 | interface Deck { |
7.5 重载(Overload)
函数根据传入不同的参数而返回不同类型的数据
查找重载列表,尝试使用第一个重载定义。 如果匹配的话就使用这个
下面的assigned只能传递1、2、4个参数,用重载可以很好的对不同的参数列表进行检测
1 | // 重载 |
8. 泛型(generic)
下图T
为一种类型变量,用于表示一种类型而不是值,我们给identity添加了类型变量T
。 T
帮助我们捕获用户传入的类型(比如:number
)。之后我们再次使用了T
当做返回值类型,现在我们知道identity的参数类型和放回置类型是相同的了。
1 | function identity<T>(arg: T): T { |
8.1 泛型接口
下图的T
为整个接口的一个参数,而再使用GenericIdentityFn
时,还得传入一个类型参数来指定泛型类型(这里是:number
)
1 | interface GenericIdentityFn<T> { |
8.2 泛型类
与接口一样,直接把泛型类型放在类后面
1 | class GenericNumber<T> { |
8.3 泛型约束
下面例子想要访问arg.length属性,但是是编译器并不能证明每种类型都有length
属性,所以就报错了
1 | function loggingIdentity<T>(arg: T): T { |
我们可以定义一个接口来描述约束条件,然后需要传入符合约束类型的值,必须包含必须的属性
1 | interface Lengthwise { |
8.4 多重类型进行泛型约束
用交叉类型&
进行多重约束
1 | interface FirstInterface { |
8.5 泛型里使用类类型
我们假设需要声明一个泛型拥有构造函数,比如:
1 | function create<T>(type: T): T { |
但是这样会报错,因为我们没有声明T
是构造函数,我们需要显式的用new
来声明这个泛型T
是构造函数
1 | function create<T>(type: {new(): T}): T { |
参数type
的类型{new(): T}
就表示此泛型T
是可被构造的,在被实例化后的类型是泛型T
一个更高级的例子,使用原型属性推断并约束构造函数与类实例的关系。
1 | class BeeKeeper { |
9. 类型兼容性
Ts结构化类型系统的基本规则是,如果x
要兼容y
,那么y
至少具有与x
相同的属性,编译器会检查x
中的每个属性,看是否能在y
中也找到对应属性
10. 高级类型
10.1 交叉类型
用&
可以将多个类型合并为一个类型
10.2 联合类型
用|
表示一个值可为几种类型之一
1 | function formatCommandline(command: string[] | string) { |
10.3 类型别名
type
虽然看起来和interface一样,但是可以用在原始类型、联合类型、元组等需要手写的类型,类型别名也可以是泛型
1 | type some = boolean | string |
类型别名和接口的区别
- 类型别名不能被
extends
和implements
(自己也不能extends
和implements
其它类型) - interface 可以实现接口合并声明
10.4 条件类型
1 | T extends U ? X : Y |
上面的代码可以理解为: 若 T
能够赋值给 U
,那么类型是 X
,否则为 Y
,有点类似于JavaScript中的三元条件运算符
11. 类型保护和类型断言
11.1 !
类型检查器认为
null
与undefined
可以赋值给任何类型
可以使用--strictNullChecks
来使变量不自动的包含null
或 undefined
,但是开启后可选参数会被自动地加上| undefined
。
如果编译器不能够去除 null
或 undefined
,你可以使用类型断言手动去除。 语法是添加!
后缀: identifier!
从 identifier
的类型里去除了 null
和 undefined
1 | function broken(name: string | null): string { |
11.2 is进行类型保护
is之后的类型必须是参数类型中的一个,在后续调用改参数时,ts会将变量缩减为那个具体的类型
1 | function isString(test: any): test is string{ |
参考
https://www.tslang.cn/docs/handbook
https://ts.xcatliu.com/basics/type-of-function.html