TypeScript-高级类型
class 类
TypeScript 有 class 关键字,并为其添加了类型注解和其他语法
- TS 中的类型推论可以得到 Human 类的实例对象 p 的类型是 Human
- TS 中的 class,不仅提供了 class 的语法功能,也作为一种类型存在
- 声明成员 age,类型为 number(没有初始值)
- 声明成员 gender,没有类型注解,但是设置了初始值,TS 推论为 string 类型
1 | class Human { |
构造函数:
- 成员初始化(例如
age: number
)后,才可以通过this
来访问实例对象 - 需要为构造函数指定类型注解,否则会被定义为 any 类型;构造函数不需要返回值类型
1 | class Person { |
实例方法:方法的类型注释与函数用法一致
1 | class Point { |
只读修饰符 readonly:表示只读,用来防止在构造函数之外属性进行赋值
- 使用 readonly 关键字修饰该属性是只读的,不能修饰方法。
- 属性后面的类型注解如果不加,则类型为字面量类型。 例如下面的 age 不加类型注解,age 的类型为 18
- 接口或者
{}
表示的对象类型,也可以使用 readonly。
1 | class Person { |
类继承
extends 继承父类
- 通过 extends 关键字实现继承
- 子类继承父类,则之类的实例对象就同时拥有父类和子类的所以属性和方法
1 | class Animal { |
implements 实现接口
- 通过 implements 关键字让 class 实现接口
- 类实现接口,要求类中必须提供接口中指定的所以方法和属性
1 | interface Singable { |
类成员可见性
可以使用 TS 来控制 class 的方法和属性对于 class 外的代码是否可见
可见性修饰符包括:public(公有)、protected(受保护的)、private(私有的)
public:公有成员可以被任何地方访问,默认的可见性
- 在类属性或方法前面添加 public 关键字,来修饰属性或方法为公有的
- 因为 public 是默认可见性,所以通常省略不写
protected:受保护的,仅对其声明所在类和子类(非实例对象)当中可见
- protected 修饰,表示该属性或方法是受保护的
- 在子类的方法中可以通过 this 来访问父类中受保护的成员,但是实例不可见
1 | class Animal { |
private:私有的,只在当前类可见
- 添加 private 关键字,表示该属性或方法是私有的
- 私有的属性或方法只能在当前类中可见,对子类或者实例都不可见
1 | class Animal { |
类型兼容性
两种类型系统:
- Structural Type System(结构化类型系统)
- Nominal Type System(标明类型系统)
TS 采用的是结构化类型系统,类型检查关注的是值所具有的形状
1 | class Point { x: number; y: number } |
- Point 和 Point2D 是两个名称不同的类
- 变量 P 的类型被标记为 Point 类型,其值与 Point2D 一致,所以不会报错,因为它们的结构相同
- 如果在标明类型系统(如 C#、Java等),它们就是不同的类,类型无法兼容
对于对象类型来说,成员多的可以赋值给少的
1 | class Point { x: number; y: number } |
接口兼容性:
接口之间的兼容性类似于 class,并且 class 和 interface 之间也可以兼容,属性多的可以赋值给少的
1 | interface Point { x: number; y: number } |
函数兼容性:
函数之间兼容性比较复杂,需考虑:
- 参数个数,参数多的兼容参数少的(参数少的可以赋值给多的),与接口不同
1 | type F1 = (a: number) => void |
- 参数类型,相同位置的参数类型要相同(原始类型)或兼容(对象类型)
1 | type F1 = (a: number) => void |
有一个特殊的情况
1 | class Point { x: number; y: number } |
- 返回值类型,只关注返回值类型本身即可
- 如果是原始类型,需要两个类型相同
- 如果是对象类型,则成员多的可以赋值给成员少的
1 | // 返回值是原始类型 |
交叉类型
交叉类型 &
:功能类似于接口继承 extends,用于组合多个类型为一个类型(常用于对象类型)
1 | interface Person { name: string } |
交叉类型(&)和接口继承(extends)的对比:
- 相同点:都可以实现对象类型的组合。
- 不同点:两种方式实现类型组合时,对于同名属性之间,处理类型冲突的方式不同。
1 | // 接口继承的情况 |
泛型
泛型是可以在保证类型安全的前提下,让函数等与多种类型一起工作,实现复用
泛型函数
创建泛型函数
1 | function callBack<Type>(value: Type): Type { return value } |
- 语法:在函数名称后面加
< >
,添加类型变量 - Type 是类型变量,是一种特殊类型的变量,它能捕获用户提供的类型
- 在这里表示传入参数和返回值同类型
- 类型变量 Type,可以是任意合法的变量名称
调用泛型函数
1 | console.log(callBack<number>(6)); |
- 语法:在函数名称的后面添加
< >
,尖括号中指定具体的类型 - 传入的类型会被 Type 捕获,在这里就是指定类型为 number
简化调用泛型函数
可以利用类型推断机制,自动传入类型
泛型约束
默认情况下,Type 表示多个类型,导致可能无法访问任何属性,比如数组类型的 length 属性,因此需要泛型添加约束来收缩类型
- 指定更加具体的类型
1 | function id<Type>(value: Type[]): Type[] { |
将类型修改为 Type[],这样就可以访问 length 属性了
- 添加约束
1 | interface ILength { length: number } |
创建描述约束的接口,通过 extends 关键字添加约束
传入的参数只要有指定的属性,就可以使用,属于类型兼容
也可以多个类型变量,也可以用类型变量约束类型变量
1 | function getProp<Type, Key extends keyof Type>(obj: Type, key: Key) { |
多个类型变量之间用,
逗号隔开
keyof 关键字接收一个对象类型,生成联合类型,在例子中 Type 获取了对象 person,所以使用 keyof 关键字之后相当于 'name' | 'age'
,就是说 Key 只能是键中的任意一个
泛型接口
接口也可以配合泛型来使用,以增加其灵活性,增强其复用性
1 | interface IdFunc<Type> { |
- 在接口名称后加
<类型变量>
,就是泛型接口 - 接口内的所有成员都可以使用类型变量
- 使用泛型接口时,需要显式指定具体的类型
泛型类
class 也可以配合泛型来使用
创建泛型类
1 | class GenericNumber<NumType> { |
类似泛型接口,在 class 名称后面添加 <类型变量>
,这个类就变成了泛型类
泛型工具类型
泛型工具类型:TS 内置了一些常用的工具类型
Partial<Type>
用来构造一个类型,将 Type 的所有属性设置为可选
1 | interface Props { |
构造出来的新类型 PartialProps 结构和 Props 相同,但是所有属性都变成可选的
Readonly<Type>
用来构造一个类型,将 Type 的所有属性都设置为 readonly(只读)
1 | interface Props { |
构造出来的新类型 ReadonlyProps 结构和 Props 相同,但是所有属性都变成只读的
Pick<Type, Keys>
从 Type 中选择一组属性来构造新类型
1 | interface Props { |
从第一个类型变量 Type 表示选择哪个接口,第二个类型变量 Keys 表示选择该接口中的哪几个属性
Record<Keys, Type>
构造一个对象类型,属性键为 Keys,属性类型为 Type
1 | type RecordObj = Record<'a' | 'b' | 'c', string[]> |
第一个参数表示对象有哪些属性,第二个参数表示对象属性的类型
例子中,新对象类型 RecordObj 有三个属性分别是 a/b/c,属性值的类型都是 string[]
索引签名类型
当无法确定对象中有哪些属性时,可以使用索引签名类型
1 | interface AnyObject { |
- 使用
[key: string]
来约束接口允许出现的属性名称,要求只能是 string 类型的属性名称 - key 只是占用符,可以换成任何合法的变量名称
在 JS 中,数组是特殊的对象,特殊在数组的键是数字类型
1 | interface MyArray<T> { |
映射类型
映射类型:基于旧类型创建新类型(对象类型),减少重复、提高开发效率
1 | type PropKeys = 'x' | 'y' | 'z' |
注意:映射类型只能在类型别名中使用,不能在接口中使用
1 | type Props = { a: number; b: string; c: boolean } |
keyof Props
可以获取对象类型 Props 中所有键的联合类型,即'a' | 'b' | 'c'
泛型工具类型也是通过映射类型实现的,比如Partial<Type>
1 | type Partial<T> = { |
在后面加上问号可以使属性变为可选
索引查询类型:T[P]
在 TS 中叫做索引查询(访问)类型,用来查询属性的类型
1 | type Props = { a: number; b: string; c: boolean} |
Props['a']
表示查询类型 Props 中属性 'a'
对应的类型 number。所以,TypeA 的类型为 number。
索引查询类型的其他使用方式:同时查询多个索引的类型
1 | type Props = { a: number; b: string; c: boolean } |