TS - 高级类型

175 阅读3分钟

交叉类型

交叉类型是将多个类型合并为一个类型。用 & 表示。

interface IName {
  name: string;
}

interface IAge {
  age: number;
}

type Person = IName & IAge;

// person 同时有 IName 和 IAge 所有属性
const person: Person = {
  name: "aa",
  age: 100,
};

联合类型

联合类型表示一个值可以是几种类型之一。用竖线( | )分隔每个类型。

// 简单类型
function fn1(id: string | number) {
  console.log(id); // id 是 string 或者 number 类型
}

// 复杂类型
interface ITeacher {
  name: string;
  id: string;
  work: boolean;
}

interface IPerson {
  name: string;
  id: number;
}

// 如果一个值是联合类型,我们只能访问此联合类型的所有类型里共有的成员。
function fn(p: ITeacher | IPerson) {
  console.log(p.id); // p.id 是 string 或者 number 类型
  console.log(p.work); // 报错
}

type 类型别名

类型别名会给一个类型起个新名字。

起别名不会新建一个类型 - 它创建了一个新名字来引用那个类型。

type Str = string;

const s1: Str = "123";

类型别名和接口的区别

  1. 在检查类型的时候,接口会显示自己的类型名称,而类型别名则显示引用的那个类型的名称。
  2. 类型别名不能被 extends 和 implements,也不能 extends 和 implements 其它类型。应该尽量使用接口代替类型别名

字符串字面量类型

type Directions = "top" | "right" | "bottom" | "left";

// direction 只能从 IDirections 中选值
const direction: Directions = "top";

索引类型

keyof T 索引类型查询操作符

interface IPerson {
  name: string;
  age: number;
}

// 将接口 IPerson 的属性组成一个字符串字面量类型
type PersonProps = keyof IPerson;

const prop: PersonProps = "name";

T[K] 索引访问操作符

function getProperty<T, K extends keyof T>(o: T, name: K): T[K] {
  return o[name];
}

映射类型

TypeScript 提供了从旧类型中创建新类型的一种方式 — 映射类型。 在映射类型里,新类型以相同的形式去转换旧类型里每个属性。

创建一个类型的只读版本

interface IPerson {
  name: string;
  age: number;
}

// 创建一个 IPerson 的只读版本
type ReadonlyPerson = {
  readonly [P in keyof IPerson]: IPerson[P];
};
let p1: ReadonlyPerson;

// 通过泛型 创建一个通用的只读版本
type TReadonly<T> = {
  readonly [P in keyof T]: T[P];
};
let p2: TReadonly<IPerson>;

它的语法与索引签名的语法类型,内部使用了 for .. in。 具有三个部分:

  1. 类型变量 K,它会依次绑定到每个属性。
  2. 字符串字面量联合的 Keys,它包含了要迭代的属性名的集合。
  3. 属性的结果类型。

其他常用类型

Exclude<T, U>

从类型 T 中排除可以指定为 U 的类型。

type Directions = "top" | "right" | "bottom" | "left";
const d1: Exclude<Directions, "top"> = "top"; // 报错

// Exclude 的源码实现
type Exclude<T, U> = T extends U ? never : T;

Extract<T, U>

与 Exclude 相反,从 T 中提取可以指定为 U 的类型

type Directions = "top" | "right" | "bottom" | "left";
const d2: Extract<Directions, "top"> = "top"; // d2 只能被赋值为 top

// Extract 的源码实现
type Extract<T, U> = T extends U ? T : never;

NonNullable<T>

从类型 T 中排除 null 和 undefined

type Mvalues = string | number | null | undefined;
const m: NonNullable<Mvalues> = null; // 报错,不能将 null 和 undefined 分配给 m

Omit<T, K>

构造一个除类型 K 以外具有 T 属性的类型。

interface IPerson {
  name: string;
  age: number;
}
const p: Omit<IPerson, "age"> = { name: "aa" };

// Omit 的源码实现
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

Partial<T>

将对象类型 T 中所有的属性变成可选的。

interface IPerson {
  name: string;
  age: number;
}
let p: Partial<IPerson> = { name: "aa" };

// Partial 的源码实现
type Partial<T> = {
  [P in keyof T]?: T[P];
};

Pick<T, K>

从对象类型 T 中选择那些在类型 K 中的属性。

interface IPerson {
  name: string;
  age: number;
}
// 现在 p 只能有 name 一个属性
let p: Pick<IPerson, "name"> = { name: "aa" };

// Pick 的源码实现
type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
};

Record<K, T>

构造一个对象类型,属性是 K 和 类型是 T

type Directions = "top" | "right" | "bottom" | "left";

type DValues = "1" | "2" | "3" | "4";

const d1: Record<Directions, DValues> = {
  top: "1",
  right: "2",
  bottom: "3",
  left: "4",
};

// Record 的源码实现
type Record<K extends keyof any, T> = {
  [P in K]: T;
};

ReturnType<T>

获取函数返回值的类型。

let res1: ReturnType<(s: string) => string[]>; // let res1: string[]

// ReturnType<T> 的源码实现
type ReturnType<T extends (...args: any) => any> = T extends (
  ...args: any
) => infer R
  ? R
  : any;