所谓高级类型,就是
ts
为了保障语言的灵活性所引入的语言特性。这些特性有助于我们应对复杂多变的开发场景。
交叉类型是将多个类型合并为一个类型。它包含了所需的所有类型的特性。 (合集)
我们大多是在混入(mixins
)或其它不适合典型面向对象模型的地方看到交叉类型的使用。
interface DogInterface {
run(): void;
}
interface CatInterface {
jump(): void;
}
let pet: DogInterface & CatInterface = { run() {}, jump() {} };
联合类型表示一个值可以是几种类型之一。
如果一个值是联合类型,我们只能访问此联合类型的所有类型里共有的成员。
let a: number | string = "string"; // ok!
a = 1; // ok!
字面量联合类型
字面量类型:
ts
提供的一个准确变量
let b: 1 | "2" | true = 1; // ok!
b = false; // error!
对象联合类型
class Dog implements DogInterface {
run() {}
eat() {}
}
class Cat implements CatInterface {
jump() {}
eat() {}
}
enum Master {
Boy,
Girl,
}
function getPet(master: Master) {
let pet = master === Master.Boy ? new Dog() : new Cat();
pet.eat(); // ok!
pet.jump(); // error!
return pet;
}
可区分的联合类型
结合了联合类型和字面量类型的类型保护方法。
如果一个类型是多个类型的联合类型,并且多个类型之间有公共属性,那么我们可以凭借这个公共属性创建类型保护区块。
// 计算不同图形的面积
interface Square {
kind: "square";
size: number;
}
interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}
function computedArea(shape: Square | Rectangle) {
switch (shape.kind) {
case "square":
return shape.size * shape.size;
case "rectangle":
return shape.width * shape.height;
}
}
这时,我希望增加一个 圆形
,会出现隐患。
interface Circle {
kind: "circle";
r: number;
}
function computedArea(shape: Square | Rectangle | Circle) {
...
}
computedArea({ kind: "circle", r: 1 }); // undefinded
这时并没有给出错误提示。约束方案有二:
type Shape = Square | Rectangle | Circle;
function computedArea(shape: Shape): number {
switch (shape.kind) {
case "square":
return shape.size * shape.size;
case "rectangle":
return shape.width * shape.height;
case "circle":
return Math.PI * shape.r ** 2;
}
}
never
类型function computedArea(shape: Shape) {
switch (shape.kind) {
case "square":
return shape.size * shape.size;
case "rectangle":
return shape.width * shape.height;
case "circle":
return Math.PI * shape.r ** 2;
default:
return ((e: never) => {
throw new Error(e);
})(shape);
}
}
检查 shape
是否是 never
类型;当 case
完全覆盖时,shape
是 never
类型;反之,需要补全分支。一个常见的 Javascript 模式:从对象中选取属性的子集。
let Obj = {
a: 1,
b: 2,
c: 3,
d: 4,
};
function getValues(obj: any, keys: string[]) {
return keys.map((key) => obj[key]);
}
getValues(Obj, ["a", "b"]);
getValues(Obj, ["e", "f"]);
我们如何来约束传参 keys
是 obj
中存在的索引值呢?
interface Obj {
a: number;
b: number;
c: number;
d: number;
}
let obj: Obj = {
a: 1,
b: 2,
c: 3,
d: 4,
};
function getValues<T, K extends keyof T>(obj: T, keys: K[]): T[K][] {
return keys.map((key) => obj[key]);
}
getValues(obj, ["a", "b"]);
keyof T
:索引类型查询操作符
对于任何类型 T, keyof T 的结果为 T 上已知的公共属性名的联合。
interface Obj {
a: number;
b: number;
c: number;
d: number;
}
let objProps: keyof Obj; // "a" | "b" | "c" | "d"
T[K]
:索引访问操作符
在这里,类型语法反映了表达式语法。
let type: Obj["a"]; // number
Typescript 提供了从旧类型中创建新类型的一种方法。
interface Flags {
option1: boolean;
option2: boolean;
}
type ReadonlyFlags = Readonly<Flags>;
/*
type ReadonlyFlags = {
readonly option1: boolean;
readonly option2: boolean;
}
*/
/**
* lib.es5.d.ts
* Make all properties in T readonly
type Readonly = {
readonly [P in keyof T]: T[P];
};
*/
type PartialFlags = Partial<Flags>;
/*
type PartialFlags = {
option1?: boolean | undefined;
option2?: boolean | undefined;
}
*/
type PickFlags = Pick<Flags, "option1">;
/*
type PickFlags = {
option1: boolean;
}
*/
type PickFlags = Pick<Flags, "option1" | "option2">;
Readonly
, Partial
和 Pick
是同态的,但 Record
不是。因为 Record
并不需要输入类型来拷贝属性,所以它不属于同态:
type twoStringProps = Record<"prop1" | "prop2", string>;
/*
type twoStringProps = {
prop1: string;
prop2: string;
}
*/
TypeScript 2.8 引入了有条件类型,它能够表示非统一的类型。
T extends U ? X : Y
若 T
能够赋值给 U
,那么类型是 X
,否则为 Y
。
type TypeName<T> = T extends string
? "string"
: T extends number
? "number"
: "object";
Exclude \ Extract \ NotNullable
type T1 = Exclude<"a" | "b" | "c", "a" | "e">;
// Exclude<"a", "a" | "e"> | Exclude<"b", "a" | "e"> | Exclude<"c", "a" | "e">
// never | "b" | "c"
// "b" | "c"
type T2 = Extract<"a" | "b" | "c", "a" | "e">;
// T2 = "a"
type T3 = NotNullable<number | string | null>;
// type T3 = string | number
有条件类型中的类型推断: ReturnType
现在在有条件类型的 extends
子语句中,允许出现 infer
声明,它会引入一个待推断的类型变量。
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
type T4 = ReturnType<() => number>;
/**
* Make all properties in T optional
*/
type Partial<T> = {
[P in keyof T]?: T[P];
};
/**
* Make all properties in T required
*/
type Required<T> = {
[P in keyof T]-?: T[P];
};
/**
* Make all properties in T readonly
*/
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
/**
* From T, pick a set of properties whose keys are in the union K
*/
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
/**
* Construct a type with a set of properties K of type T
*/
type Record<K extends keyof any, T> = {
[P in K]: T;
};
/**
* Exclude from T those types that are assignable to U
*/
type Exclude<T, U> = T extends U ? never : T;
/**
* Extract from T those types that are assignable to U
*/
type Extract<T, U> = T extends U ? T : never;
/**
* Construct a type with the properties of T except for those in type K.
*/
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
/**
* Exclude null and undefined from T
*/
type NonNullable<T> = T extends null | undefined ? never : T;
/**
* Obtain the parameters of a function type in a tuple
*/
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
/**
* Obtain the parameters of a constructor function type in a tuple
*/
type ConstructorParameters<T extends new (...args: any) => any> = T extends new (...args: infer P) => any ? P : never;
/**
* Obtain the return type of a function type
*/
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
/**
* Obtain the return type of a constructor function type
*/
type InstanceType<T extends new (...args: any) => any> = T extends new (...args: any) => infer R ? R : any;
/**
* Convert string literal type to uppercase
*/
type Uppercase<S extends string> = intrinsic;
/**
* Convert string literal type to lowercase
*/
type Lowercase<S extends string> = intrinsic;
/**
* Convert first character of string literal type to uppercase
*/
type Capitalize<S extends string> = intrinsic;
/**
* Convert first character of string literal type to lowercase
*/
type Uncapitalize<S extends string> = intrinsic;