TypeScript 开发备忘录 | 七日打卡

741 阅读4分钟

typescript

前言

在开发中使用 TypeScript 已经一年多了,逃不开“真香定律”哈,日常重度依赖 TypeScript。在组件开发、前后端对接或者定位问题的过程中,类型检查发挥了极大的作用,可以说提高生产力了。这里就总结一些个人的常用方法吧,也算备忘录了。

类型拓展

interface 继承 interface

interface A {
  a: number;
}
interface B {
  b: number;
}
interface C extends A, B {
  c: number;
}

interface 继承 type

interface A {
  a: number;
}
type B = { b: number };
interface C extends A, B {
  c: number;
}

type 交叉类型

type A = { a: number };
type B = { b: number };
type C = A & B & { c: number };

定义函数类型

interface 语法

interface Func {
  (params: number): number;
}

type 语法

type Func = (params: number) => number;

联合类型

interface 语法及解释

interface A {
  a: number;
  c: number;
}
interface B {
  b: number;
  c: string;
}
// 只取有交叉的值 type C = {c: number};
type C = A | B;

type 语法及解释

type A = {
  a: number;
  c: number;
};
type B = {
  b: number;
  c: string;
};
// 真正的 union, type C = {a: number; c: number} | {b: number; c: number}
type C = A | B; 
type D = string | number;

工具泛型

Partial<T>

将接口所有属性置为可选

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

Required<T>

将接口所有属性置为必选

// 实现
type Required<T> = {
    [P in keyof T]-?: T[P];
};

Readonly<T>

将接口所有属性置为只读

// 实现
type Readonly<T> = {
    readonly [P in keyof T]: T[P];
};

Pick<T, K>

从接口 T 中选取所需的键值

// 实现
type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};
type A = { a: number; b:number };
type B = Pick<A, "b">; // {b: number}

Record<K, T>

返回键值为 K,属性值为 T 的类型

// 实现
type Record<K extends keyof any, T> = {
    [P in K]: T;
};
type B = Record<"a" | "b", number>;//{ a: number; b:number }

Record<K, T>

返回键类型为 K,属性值为 T 的类型

// 实现
type Record<K extends keyof any, T> = {
    [P in K]: T;
};
type B = Record<"a" | "b", number>;//{ a: number; b:number }

Exclude<T, U>

从 T 中选取不在 U 中的类型

// 实现
type Exclude<T, U> = T extends U ? never : T;
// 用例
type A = { a: number; b:number };
type B = { b: number }
type C = Exclude<keyof A, keyof B>; // "a"

Extract<T, U>

和 Exclude 相反,从 T 中选取在 U 中的类型

// 实现
type Extract<T, U> = T extends U ? T : never;
// 用例
type A = { a: number; b:number };
type B = { b: number }
type C = Extract<keyof A, keyof B>; // "b"

Omit<T, K>

从类型 T 中,移除指定键值的属性

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

NonNullable<T>

保证类型不为 null 或者 undefined

type NonNullable<T> = T extends null | undefined ? never : T;

Parameters<T>

得到函数的参数类型数组,在请求接口时很常用的一个方法

// 实现
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;

ReturnType

得到函数的返回类型,同样也是在请求接口时很常用的一个方法

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

类型推断

我们知道 TypeScript 支持自动类型推断

自动类型推断

  • typeof
type A = number | string;
let a: A = 0;
if (typeof a === 'number') {
  a++;
}
  • instanceof
let a = 1 > 2 ? new Number(1) : new String('a');
if (a instanceof Number) {
  a = a.toFixed(2);
}
  • 直接声明
let a = 1; // number
let b = "b; // string
//...
  • 浏览器内置的方法
let dom = document.createElement('a');//HTMLAnchorElement

自定义类型推断

有时在代码中我们可能会写一些“ducking”模型的接口,也就是说,当一个动物能发出鸭叫,我们就认为它是鸭子,这个时候 TypeScript 是无法自动推断类型的,因此需要我们明确指定推导得到的是哪个类型,以免类型报错

//示例,我们规定一个元素是 HTMLCanvasElement 只要它拥有 toBlob 方法
function isCanvasElement(el: HTMLElement): el is HTMLCanvasElement {
  return !!(el as HTMLCanvasElement).toBlob;
}

其他技巧

TypeScript 有什么高级技巧呢?其实也没啥特别的,就是利用提供的语法进行组合得到新的类型。

LiteralUnion<T, U>

有时候我们需要定义一些字面量的联合类型,同时我们也想要在编写代码的过程中能够自动补全

type LiteralUnion<T extends U, U> = T | (U & {_: never});
//无自动补全,因为 success 等字面量都 extends 自 string,会被 string 覆盖
type MessageType = 'success' | 'error' | 'warning' | string;
let msg: LiteralUnion<MessageType, string> = "success";

infer

通过 infer 指定我们要推导的类型

// <number>[]
type ElementOf<T> = T extends (infer E)[] ? E : never;
let nums = [0, 1, 2];
type Element = ElementOf<typeof nums>;//number
// Promise<number>
type PromiseOf<T> = T extends Promise<infer E> ? E : never;
let promise = Promise.resolve(0);
type PromiseReturnType = PromiseOf<typeof promise>; //number

函数重载

ts 中的函数重载并不意味着可以重复声明两个同名的函数,而是可以声明两个参数/返回值不同的函数类型,同时函数实现要跟在声明重载函数之后

//示例
function count(a: number, b: number): number;
function count(a: number): number;
function count(...nums: number[]) {
  if (nums.length >= 2) {
    return nums.reduce((a, b) => a + b, 0);
  } else {
    return nums[0] * 2;
  }
}

总结

github 上也有很多优秀的ts库,比如 utility-typestype-fest。进一步学习可以从研究这些源码开始。

参考资料