高级类型
所谓高级类型,就是
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]
:索引访问操作符在这里,类型语法反映了表达式语法。
映射类型
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<T> = {
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;
TypeScript 入门系列
- Hello TypeScript(01)-- 环境搭建
- Hello Typescript(02)-- 枚举类型
- Hello Typescript(03)-- 对象类型接口
- Hello Typescript(04)-- 函数类型接口、混合类型接口、类接口
- Hello Typescript(05)-- 函数
- Hello Typescript(06)-- 类
- Hello Typescript(07)-- 类与接口的关系
- Hello Typescript(08)-- 泛型
- Hello Typescript(09)-- 类型推断、类型兼容性、类型保护
- Hello Typescript(10)-- 交叉类型、联合类型、索引类型、映射类型、条件类型