给一个类型起个新名字:类型别名
类型别名会给一个类型起个新名字。 类型别名有时和接口很像,但是可以作用于原始值,联合类型,元组以及其它任何你需要手写的类型。
type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {...}
类型别名不能被 extends
和 implements
类型字面量
字符串字面量类型
字符串字面量类型允许你指定字符串必须的固定值。 在实际应用中,字符串字面量类型可以与联合类型,类型保护和类型别名很好的配合。 通过结合使用这些特性,你可以实现类似枚举类型的字符串。
使用固定的字面量字符串代替string作为更精确的类型定义
只能从三种允许的字符中选择其一来做为参数传递,传入其它值则会产生错误
type Easing = "ease-in" | "ease-out" | "ease-in-out";
可通过类型保护,实现面向字面量的类型断言
if (easing === "ease-in")
枚举字面量也可作为类型使用,
如我们在 枚举一节里提到的,当每个枚举成员都是用字面量初始化的时候枚举成员是具有类型的。
数字字面量也可作为类型使用。
函数式编程利器:可辨识联合
可辨识联合在函数式编程很有用处。 它具有3个要素:
- 具有普通的单例类型属性— 可辨识的特征。 => 定义多个带有标志位的接口
- 一个类型别名包含了那些类型的联合— 联合。 => 联合类型将多个接口变作一个接口
- 此属性上的类型保护。 => 在类型保护下 根据标志位不同执行不同代码块
interface Square {
kind: "square";
size: number;
}
interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}
interface Circle {
kind: "circle";
radius: number;
}
type Shape = Square | Rectangle | Circle;
在使用时添加类型保护,不同类型执行不同代码块。
function area(s: Shape) {
switch (s.kind) {
case "square": return s.size * s.size;
case "rectangle": return s.height * s.width;
case "circle": return Math.PI * s.radius ** 2;
}
}
上面的例子中,area的case中包含了参数类型Shape可能存在的所有类型,但是如果Shape中的类型增加了,area函数可能因没有符合类型的case而返回undefined,这是不好的,解决此问题我们可使用never类型。
never类型—即为除去所有可能情况后剩下的类型
function assertNever(x: never): never {
throw new Error("Unexpected object: " + x);
}
function area(s: Shape) {
switch (s.kind) {
case "square": return s.size * s.size;
case "rectangle": return s.height * s.width;
case "circle": return Math.PI * s.radius ** 2;
default: return assertNever(s); // error here if there are missing cases
}
}
此方法为没有对应的case时,将变量传入一个函数中,该函数参数类型定义为never,在此函数中对意料外的参数进行其他操作
链式开发原理:多态的this
一个类或对象中的方法返回this,以方便构成链式操作。
面试必问keyof:索引类型
通过 索引类型查询和 索引访问操作符来定义参数类型,面对一个函数中传入两个参数,参数A为参数B的部分索引,用来筛选处参数B的部分内容。
索引类型查询操作符:keyof T
对于任何类型
T
,keyof T
的结果为T
上已知的公共属性名的联合
索引访问操作符:T[K]
function pluck(o, names) {
return names.map(n => o[n]);
}
function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
return names.map(n => o[n]);
}
interface Person {
name: string;
age: number;
}
let person: Person = {
name: 'Jarid',
age: 35
};
let strings: string[] = pluck(person, ['name']); // ok, string[]
interface Map<T> {
[key: string]: T;
}
let keys: keyof Map<number>; // string
let value: Map<number>['foo']; // number
## 类型直接的转换与继承:映射类型
在映射类型里,新类型以相同的形式去转换旧类型里每个属性。 例如,你可以令每个属性成为 readonly
类型或可选的。
type Keys = 'option1' | 'option2';
type Flags = { [K in Keys]: boolean };
// ===
type Flags = {
option1: boolean;
option2: boolean;
}
type Readonly<T> = {
readonly [P in keyof T]: T[P];
}
type Partial<T> = {
[P in keyof T]?: T[P];
}
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
}
type Record<K extends string, T> = {
[P in K]: T;
}
type PersonPartial = Partial<Person>;
type ReadonlyPerson = Readonly<Person>;
type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string>
Readonly
, Partial
和 Pick
是同态的,但 Record
不是。 因为 Record
并不需要输入类型来拷贝属性,所以它不属于同态,非同态类型本质上会创建新的属性,因此它们不会从它处拷贝属性修饰符。
Exclude<T, U>
– 从T
中剔除可以赋值给U
的类型。Extract<T, U>
– 提取T
中可以赋值给U
的类型。NonNullable<T>
– 从T
中剔除null
和undefined
。ReturnType<T>
– 获取函数返回值类型。InstanceType<T>
– 获取构造函数类型的实例类型。