-
类型系统
更确切地说,TypeScript的类型系统是结构类型系统(Structural type system),任两个以相同结构所描述的值的类型都是等价的。(与之相反的是,标明类型系统Nominative type system,表示类型若要相等,就必须具有相同的“名字”)
-
never类型
这个类型在其他语言里比较少见,Java8里没有这个类型。 never代表永远不会发生的类型的值,和throw搭配使用,表示它后续的代码将unreachable,即不可到达。
never特性:
- 特性1: 通常在方法里和抛出异常throw搭配使用
- 特性2: never是所有类型的子类型,因此可以把never类型值赋给任意类型变量。
- 特性3: never没有子类型,因此所有类型的值都不能赋给never类型变量 注:在ts中,父类型不能赋值给子类型,但是子类型可以赋值给父类型
-
交叉类型(融合了多个类型): &
interface Bird { name: string; fly(): void; } interface Person { name: string; talk(): void; } type BirdPerson = Bird & Person; let p: BirdPerson = { name: "zhufeng", fly() {}, talk() {} }; -
联合类型
interface Bird { name: string; fly(): void; } interface Person { name: string; talk(): void; } type BirdPerson = Bird | Person; let p: BirdPerson = { name: "zfeng", fly() {} }; let p1: BirdPerson = { name: "zfeng", talk() {} }; -
别名类型: type
type JavaCoder = { name: string, age: number, language: 'Java' }type Container<T> = { value: T }; 别名类型不像interface、class、字面量类型那样,它不是新建的类型,新建的是名称。定义一个新的名称和已有的类型对应起来,然后已有的类型就有了新的类型名字。 -
内置条件类型
type Extract<T, U> = T extends U ? T : never; type Exclude<T, U> = T extends U ? never : T; type NonNullable<T> = T extends null | undefined ? never : T; type N = NonNullable<string | number | null | undefined>;// 删除null和undifined; type E = Exclude<string | number, string>; // 排除关系 输出 string; type I = Extract<string | number, string>; // 包含关系 输出 number; -
索引类型
包括两个方面,类型里的字段名(键)和字段值的类型(值)的访问。对类型的键的访问使用keyof操作符,对类型的字段值类型的访问类似通过对象属性名访问值的操作。
访问索引键 -- keyof
keyof实现:生成以类型属性名为字符串字面量类型的联合类型
class Task {
name: string;
createTime: string;
taskStatus: number;
taskType: number;
private duration: number;
}
type TaskKey = keyof Task;
type TaskKey2 = 'name' | 'createTime' | 'taskStatus' | 'taskType';
访问索引值
type TaskKey = keyof Task;
type TaskValueType = Task[TaskKey]; // string | number
type TaskValueType2 = Task[keyof Task];
一个特殊的例子
interface Dictionary {
name: string;
type: string;
[key: string]: string
}
let keys: keyof Dictionary;
keys的类型是什么?直观感觉是string,但是变量keys实际的类型是string|number。TypeScript编译器之所以给出string|number联合类型,是因为考虑到转成JavaScript后,访问对象属性时,键的类型可以是string或者number,比如obj['key'], array[3],为此做了兼容。
-
映射类型
需要使用操作符: in, 映射类型是循环访问类型内部的字段名,然后按照一定的条件生成新类型,也就是以相同的形式去转换旧类型的每个属性。
type Person = {
name:string,
age:number
}
type PersonKey = keyof Person;
type PersonValueType = Person[PersonKey] //string|number
type PersonMapKey = { [K in PersonKey]: PersonValueType }
type Person2 = {
name: string |number;
age: string | number;
}
//PersonMapKey和Person2等效
在类型中,对in操作符的使用和在for..in..遍历的使用相同,引入了变量K,只是这里的K代表的是类型变量。
上述PersonMapKey的效果是Person2,和Person存在差异。
type PersonKey = keyof Person;
type PersonMapKey3 = { [K in PersonKey]: Person[PersonKey] }
type PersonMapKey4 = { [K in keyof Person]: Person[K] }
PersonMapKey4和Person效果一样的,PersonMapKey4就是对Person的映射类型。
简单应用
定义一个Person类型的所有属性都为可选属性的类型,如何实现?
type PersonWrapper5 = { [K in keyof Task]?: Task[K] }
再拓展一步,现在不是对特定的Person类型,而是对任意某个类型里的所有属性都变为可选属性,如何对这样的类型的做定义呢?
任意类型,即要求类型是输入参数(类型做参数化),因此需引入泛型:
type PersonWrapper<T> = { [K in keyof T]?: T[K] }
内置的映射类型
Pick
从 T 中取出 一系列 K 的属性
type Pick<T, K extends keyof T> = { [P in K]: T[P] };
interface Person {
name: string;
age: number;
visiable: boolean;
}
type Person1 = Pick<Person, 'name'|'age'> ; // Person{name:string;age:number}
Omit(反向)
interface Person {
name: string;
age: number;
visiable: boolean;
}
type Exclude<T, U> = T extends U ? never : T;
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
type Person2 = Omit<Person, "age">; //Person{age:number}
infer关键字
用来修饰待推断的类型变量,并定义为参数。看个例子
type ParamType<T> = T extends (param: infer P) => any ? P : T;
如果泛型是函数类型,则返回入参类型;否则返回泛型本身。infer强大之处在于提供了另一种声明类型参数的方式。
类型体操
在讲解类型编程之前,先提出一个问题:
类型在类元素里扮演什么角色?
一个类的构成元素:
1.属性字段
- 修饰词
-
- readonly
- private, protected, public
- static
- 属性类型
- 默认值
2.方法
1).种类
- 构造方法
- 静态方法
- 实例方法
2).组成成分
- 修饰词- 权限修饰词(private,protected, public), static, super
- 方法名
- 方法入参,参数类型
- 返回类型
- 方法体
由此可知,类型在类的属性字段,方法入参,方法返回这三处出现,类型扮演对数据分类的作用。
处理类型的常用操作符一览表:
- instanceof:实例判断
- typeof:类型判断
- as: 类型强制转换
- is:断言返回布尔类型
- ?:条件类型
- keyof:键名索引
- in:映射
- infer:声明待推断的类型
- <>:泛型
- type:别名
- |: 联合类型
- &: 交叉类型
什么是TypeScript的类型编程?
通过TypeScript操作符,把类型当作参数进行逻辑处理,从而获得新的类型的过程称为类型编程。
把类型声明为参数,有三种方法:
- 泛型<>
- in映射
- infer
其中大部分都是通过泛型来引入,因此泛型是TS类型编程的基础。