《深入理解TypeScript》

2022/1/29

# 第一章 入门配置

# 第二章 类型检测

# 第三章 函数类型

# 第四章 类和接口

# 第五章 枚举/promise

# 第六章 类型查找

# 第七章 特殊关键字

# const

  • 将一个值的集合变成readonly
const a = ["1", 2] as const;

a[1] = 3; // error
1
2
3

# Extract

  • 从属性集里面摘出几个属性
type T0 = Extract<"a" | "b" | "c", "a" | "f">;
     
// type T0 = "a"
1
2
3
function getAttr<T, K extends Extract<keyof T, string>>(o: T, p: K) {
  // 对象属性键值原本是 symbol | string | number
  const s: string = p;
  return o[p];
}

getAttr({ name: "1" }, "name");
1
2
3
4
5
6
7

# ReturnType

  • 函数的返回类型
const a = () => {
  return "hello world!"
}

type b = ReturnType<typeof a>
// type b = string
1
2
3
4
5
6

# Enum

  • 枚举类型
enum Res {
  No,
  Yes,
  OR,
}

/* 同 */
// enum Res {
//   No = 0,
//   Yes = 1,
//   OR = 2,
// }

// 直接获取
const no: Res = Res.No;

// 间接获取
const key = "Yes";
const yes = Res[key];

console.log(no);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# typeof

  • 获取已知变量的对应的类型
enum UserResponse {
  No,
  Yes,
  OR,
}

type all = keyof typeof UserResponse;
// type all = "No" | "Yes" | "OR"
1
2
3
4
5
6
7
8

# 索引访问类型

  • 通过索引关系两个类型
type Person = {
  name: string;
  age: number;
  friends: Person[];
}

type Name = Person["name"];
// type Name = "string"

type AgeName = Person["name" | "age"];
// type Name =  string | number

type allPersonKeys = Person[keyof Person];
// type allPersonKeys = string | number | Person[]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  • 索引number访问所有类型生成新类型
const arr = [
  {name: "黄", friends: ["a"]},
  {name: "黄", age: 18},
];

type P = typeof arr[number]
/*
  type P = {
    name: string;
    age?: undefined;
  } | {
    name: string;
    age: number;
  }
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  • 结合const固定类型,将数组类型生成联合类型
const app = ["淘宝", "天猫", "支付宝"] as const;

type App = typeof app[number];
// type App = "淘宝" | "天猫" | "支付宝"
1
2
3
4
  • 枚举索引获取对象所有值类型
const person = {
  name: "黄",
  age: 18,
  books: ["JS", "python"]
} as const;

type P = typeof person[keyof typeof person];
// type P = "黄" | 18 | readonly ["JS", "python"]
1
2
3
4
5
6
7
8

# 三元表达式

type A = {
  id: number;
}
type B = {
  name: string;
}
 
// 三元表达式根据入参数判断返回值类型
function createLabel<T>(id: T): T extends number ? A : B;

function createLabel(id): A | B {
  throw "unimplemented";
}

const a = createLabel(1);
// type a = A
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# Exclude

interface Circle {
  kind: "circle";
  radius: number;
}

type KindlessCircle = {
  [P in keyof Circle as Exclude<P, "kind">]: Circle[P];
}

// type KindlessCircle = {
//   radius: number;
// }
1
2
3
4
5
6
7
8
9
10
11
12
  • 配合泛型制造返回值
  • 返回值比入参少属性
interface Circle {
  kind: "circle";
  radius: number;
}
function a<T, K extends keyof T>(
  o: T,
  p: K
): {
  [P in keyof T as Exclude<P, K>]: T[P];
} {
  delete o[p];
  return o;
}

const c: Circle = {
  kind: "circle",
  radius: 10,
};

const x = a(c, "kind");
// const x: {
//   radius: number;
// }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 映射类型

type isTypeHasFoot<T> = {
[P in keyof T]: T[P] extends number ?  T[P] : never;
}

interface Animal {
  foot: number;
  hand: number;
  head: string;
}

type B = isTypeHasFoot<Animal>;
// type B = {
//   foot: number;
//   hand: number;
//   head: never;
// }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 模板字符串

enum Actions {
  a = "click",
  b = "touchstart",
  c = "touchmove",
}

type enumIds = `on${Capitalize<Actions>}`;
// type enumIds = "onClick" | "onTouchstart" | "onTouchmove"
1
2
3
4
5
6
7
8

# 字符串内置操作符

enum Actions {
  a = "click",
  b = "touchstart",
  c = "touchmove",
}

// 首字母大写
type A = `on${Capitalize<Actions>}`;
// type A = "onClick" | "onTouchstart" | "onTouchmove"

// 首字母小写
type D = `on${Uncapitalize<Actions>}`;
// type D = "onclick" | "ontouchstart" | "ontouchmove"


// 大写
type B = `on${Uppercase<Actions>}`;
// type B = "onCLICK" | "onTOUCHSTART" | "onTOUCHMOVE"

// 小写
type C = `on${Lowercase<Actions>}`;
// type C = "onclick" | "ontouchstart" | "ontouchmove"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# infer

  • 类型推断,在表达式内推断出新类型
type InferTypes<Type> = Type extends () =>  infer Item ? Item : never;

type Fn = () => string;
type R = InferTypes<Fn>;
// type R = string
1
2
3
4
5

# 第八章 工具类型

key 作用
Partial interface属性全部变成可选类型
Required interface属性全部变成必填类型
Readonly interface属性全部变成只读类型
Record 键的集合和值生成新的类型
Pick interface筛选出一部分属性作为新的类型
Omit interface剔除掉一部分属性作为新的类型
Exclude 联合类型剔除掉一部分属性作为新的类型
Extract 联合类型提取出一部分类型作为新的类型
NunNullable 联合类型剔除掉null/undefined类型
Parameters 函数类型提取出参数类型
ConstructorParameters 构造函数的参数类型
ReturnType 函数的返回值类型
InstanceType 的实例类型
PromiseLike 符合Promise的类型

# Partial

  • 将一个属性集全部变成可选类型
interface People {
  name: string;
  age: number;
  friends: People[];
}

type PeopleParams = Partial<People>
// type PeopleParams = {
//   name?: string | undefined;
//   age?: number | undefined;
//   friends?: People[] | undefined;
// }
1
2
3
4
5
6
7
8
9
10
11
12

# Required

  • 将一个属性集全部变成必填类型,与Partial相反
interface People {
  name?: string;
  age?: number;
  friends?: People[];
}

type PeopleParams = Required<People>
// type PeopleParams = {
//   name: string;
//   age: number;
//   friends: People[];
// }
1
2
3
4
5
6
7
8
9
10
11
12

# Readonly

  • 将一个属性集全部变成只读
interface People {
  name: string;
  age: number;
  friends?: People[];
}

type PeopleParams = Readonly<People>
// type PeopleParams = {
//   readonly name: string;
//   readonly age: number;
//   readonly friends?: People[] | undefined;
// }
1
2
3
4
5
6
7
8
9
10
11
12

# Record

  • 将一个键集合映射为一个值的集合,生成一个新类型
interface People {
  name: string;
  age: number;
  friends?: People[];
}

interface Company {
  boss: string;
  marketing: string;
  programer: string;
}

type CompanyPeoples = Record<keyof Company, People>
// type CompanyPeoples = {
//   boss: People;
//   marketing: People;
//   programer: People;
// }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# Pick

  • 将一个类型筛选出一部分类型出来做成一个新类型
interface People {
  name: string;
  age: number;
  friends?: People[];
}

type CompanyPeoples = Pick<People, "name" | "age">;
// type CompanyPeoples = {
//   name: string;
//   age: number;
// }
1
2
3
4
5
6
7
8
9
10
11

# Omit

  • 将一个类型剔除掉一部分做出一个新的类型,和Pick相反
interface People {
  name: string;
  age: number;
  friends?: People[];
}

type SchoolPeoples = Omit<People, "name" | "age">;
// type SchoolPeoples = {
//   friends?: People[] | undefined;
// }
1
2
3
4
5
6
7
8
9
10

# Exclude

  • 将一个联合类型剔除掉一部分做出一个新类型
type People = "boss" | "marketing" | "programer";

type SchoolPeoples = Exclude<People, "boss">;
// type SchoolPeoples = "marketing" | "programer"
1
2
3
4

# Extract

  • 将一个联合类型提取出一部分做出一个新类型
type People = "boss" | "marketing" | "programer";

type SchoolPeoples = Extract<People, "boss">;
// type SchoolPeoples = "boss"
1
2
3
4

# NonNullable

  • 把一个类型剔除掉nullundefined
type People = "boss" | null;

type SchoolPeoples = NonNullable<People>;
// type SchoolPeoples = "boss"
1
2
3
4

# Parameters

  • 合并所有函数的参数类型
type Fn1 = (s: string) => string;
type Fn2 = (n: number) => void;

type SchoolPeoples = Parameters<Fn1 | Fn2>;
// type SchoolPeoples = [s: string] | [n: number]
1
2
3
4
5

# ConstructorParameters

  • 一个构造函数的入参类型
type SchoolPeoples = ConstructorParameters<ErrorConstructor>;
// type SchoolPeoples = [message?: string | undefined, options?: ErrorOptions | undefined]
1
2

# ReturnType

  • 一个函数的返回类型
type Fn1 = (s: string) => string;

type SchoolPeoples = ReturnType<Fn1>;
// type SchoolPeoples = string
1
2
3
4

# InstanceType

  • 获取class的实例类型

直接拿class做类型是一样的,可能调用第三方库的时候有用

class A {
  constructor() {}
}

type B = InstanceType<typeof A>;

const a: B = new A();
1
2
3
4
5
6
7

# PromiseLike

  • 符合Promise规范的表达式中拿到Promise返回值类型
type A<T> = T extends PromiseLike<infer U> ? U : never;

const a = Promise.resolve(10);

type B = A<typeof a>
// type B = number
1
2
3
4
5
6

# 装饰器

  • 将多个class内共有的方法抽出来
@classDecorator
class Greeter {
  property = "property";
  hello: string;
  constructor(m: string) {
    this.hello = m;
  }
}

console.log(new Greeter("world"));

function classDecorator(
  constructor: typeof Greeter,
  context: ClassDecoratorContext<typeof Greeter>
): void | typeof Greeter {
  return class extends constructor {
    newProperty = "new property";
    hello = "override";
  };
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 命名空间

# 追加声明

  • 在一个已有的对象/类/模块内添加声明
function buildLabel(name: string): string {
  return buildLabel.prefix + name + buildLabel.suffix;
}

namespace buildLabel {
  export let suffix = "";
  export let prefix = "Hello, ";
}

console.log(buildLabel("Sam Smith"));

console.dir(buildLabel);
// [Function: buildLabel] { suffix: '', prefix: 'Hello, ' }
1
2
3
4
5
6
7
8
9
10
11
12
13

# 做类型合并

  • 给枚举添加函数类型

直接给枚举添加函数类型是不被通过的,可以通过命名空间追加导出的形式追加其他属性

enum Color {
  red = 1,
  green = 2,
  blue = 4,
}

namespace Color {
  export function mixColor(colorName: string) {
    if (colorName == "yellow") {
      return Color.red + Color.green;
    } else if (colorName == "white") {
      return Color.red + Color.green + Color.blue;
    } else if (colorName == "magenta") {
      return Color.red + Color.blue;
    } else if (colorName == "cyan") {
      return Color.green + Color.blue;
    }
  }
}

console.log(Color);
// {
//   red: 1,
//   green: 2,
//   blue: 4,
//   mixColor: [Function: mixColor]
//   '1': 'red',
//   '2': 'green',
//   '4': 'blue',
// }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# 模块声明文件

  • xxx.d.ts文件追加已有的声明

  • 只在本目录及子目录生效

# 声明扩展

  • 扩展已有说明
declare module "react/jsx-runtime" {
  declare global {
    interface Window {
      ActiveXObject: any;
      isCloseHint: boolean;
    }

    namespace NodeJS {
      interface Global {
        globalBaseUrl: string;
      }
    }
  }
}

// 全局可访问
window.isCloseHint
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# JSX

  • 可以做jsx文件内的类型声明
  • 声明一个标签元素
declare namespace JSX {
  interface IntrinsicElements {
    foo: { bar?: boolean }
  }
}

// `foo`的元素属性类型为`{bar?: boolean}`
<foo bar />;
1
2
3
4
5
6
7
8
上次更新: 11/1/2024