TS 一些工具泛型的使用

81 阅读4分钟

infer

  • 在条件类型语句中,可以用 infer 声明一个类型变量并且对它进行使用。
  • 以下代码中 infer R 就是声明一个变量来承载传入函数签名的返回值类型,简单说就是用它取到函数返回值的类型方便之后使用。
type ReturnType<T> = T extends (
  ...args: any[]
) => infer R ? R : any;

复制代码

in

  • 用来遍历枚举类型:
type Keys = "a" | "b" | "c"

type Obj =  {
  [p in Keys]: any
} // -> { a: any, b: any, c: any }
复制代码

typeof

  • typeof 的主要用途是在类型上下文中获取变量或者属性的类型
interface Person {
  name: string;
  age: number;
}
const user: Person = { name: "张三", age: 30 };
type User = typeof user; // type User = Person
const lolo: User = { name: "lolo", age: 5 }
复制代码
  • 此外,typeof 操作符除了可以获取对象的结构类型之外,它也可以用来获取函数对象的类型,比如:
function toArray(x: number): Array<number> {
  return [x];
}
type Func = typeof toArray; // -> (x: number) => number[]

复制代码

keyof

  • 将一个类型的属性名全部提取出来当做联合类型
// key of 使用
interface People {
   name: string;
   age: number;
}
// keyof 取 interface 的键
// type keys = "name" | "age"
type keys = keyof People;
// 假设有一个 object 如下所示,
// 我们需要使用 typescript 实现一个 get 函数来获取它的属性值
const xiaozhang:People = {
   name: '小张',
   age: 12
}

function get<T extends object, K extends keyof T>(o: T, name: K): T[K] {
   return o[name]
}
console.log(get(xiaozhang,'age')) // 12
console.log(get(xiaozhang,'name')) // 小张
// error 类型“"address"”的参数不能赋给类型“keyof People”的参数。
console.log(get(xiaozhang,'address'))

复制代码

extends

  • 表示继承/拓展的含义
 class Dog extends Animal{
 }
 // 继承/扩展类型
  interface Animal {
  kind: string;
}
interface Dog extends Animal {
  bark(): void;
}
// Dog => { name: string; bark(): void }
复制代码
  • 表示约束的含义

在书写泛型的时候,我们往往需要对类型参数作一定的限制,比如希望传入的参数都有 name 属性的数组我们可以这么写:

function getCnames<T extends { name: string }>(entities: T[]):string[] {
 return entities.map(entity => entity.cname)
}

this.getCnames([{name:'123'}])
复制代码
  • 表示分配的含义

extends 还有一大用途就是用来判断一个类型是不是可以分配给另一个类型,这在写高级类型的时候非常有用

  type Human = {
    name: string;
  }
  type Duck = {
    name: string;
  }
  type Bool = Duck extends Human ? 'yes' : 'no'; // Bool => 'yes'

    type Human = {
    name: string;
    occupation: string;
  }
  type Duck = {
    name: string;
  }
  type Bool = Duck extends Human ? 'yes' : 'no'; // Bool => 'no'
复制代码

Record

  • 此类型包含一组指定的属性且都是必填。
  • 具体的复杂业务场景中,一般会接合 Pick 、Partial 等组合使用,从而过滤和重组出新的类型定义。
type Coord = Record<'x' | 'y', number>;

// 等同于
type Coord = {
	x: number;
	y: number;
}
复制代码

Partial

  • 将类型定义的所有属性都修改为可选。
type Coord = Partial<Record<'x' | 'y', number>>;

// 等同于
type Coord = {
	x?: number;
	y?: number;
}
复制代码

Readonly

  • 将所有属性定义为只读。
type Coord = Readonly<Record<'x' | 'y', number>>;

// 等同于
type Coord = {
    readonly x: number;
    readonly x: number;
}

// 如果进行了修改,则会报错:
const c: Coord = { x: 1, y: 1 };
c.x = 2; // Error: Cannot assign to 'x' because it is a read-only property.
复制代码

Pick

  • 从类型定义的属性中,选取指定一组属性,返回一个新的类型定义
type Coord = Record<'x' | 'y', number>;
type CoordX = Pick<Coord, 'x'>;

// 等用于
type CoordX = {
	x: number;
}
复制代码

Required< T >

  • Partial<T> 程序类型的作用相反,将类型属性都变成必填。
type Coord = Required<{ x: number, y?:number }>;

// 等同于
type Coord = {
x: number;
y: number;
}

复制代码

Exclude<T, U>

  • 排除一个 联合类型 中指定的子类型:
  • 作用:如果 T 是 U 的子类型则返回 never 不是则返回 T
type T0 = Exclude<'a' | 'b' | 'c', 'b'>   // 'a' | 'c'
type T1 = Exclude<string | number | boolean, boolean>  // string | number
复制代码

Extract<T, U>

  • Exclude<T, U>完全相反的功能,用于提取指定的 联合类型,如果不存在提取类型,则返回 never。可以用在判断一个复杂的 联合类型 中是否包含指定子类型:
type T0 = Extract<'a' | 'b' | 'c', 'a'> // 'a'
type T1 = Extract<string | number | boolean, boolean> // boolean
复制代码

Omit<T, K extends keyof any>

  • 排除接口中指定的属性:
interface I1 {
	a: number;
	b: string;
	c: boolean;
}
type AC = Omit<I1, 'b'>;     // { a:number; c:boolean }
type C = Omit<I1, 'a' |'b'>  // { c: boolean }
复制代码

NonNullable< T >

  • 过滤掉 联合类型 中的 null 和 undefined 类型:
type T1 = NonNullable<string | null | undefined>; // string
复制代码
  • 额外说明下,因为 null 和 undefined 类型的特殊性,他们可以赋值给任何类型,这往往会带来意料之外的错误。当你开启 --strictNullChecks 设置后,TS 就会严格检查,只有被声明 null 后才能被赋值:
// 关闭 --strictNullChecks
let s: string = "foo";
s = null; // 正常

// 开启 --strictNullChecks
s = null; // Error: Type 'null' is not assignable to type 'string'.
复制代码

Parameters<T extends (...args: any) => any>

  • 获取函数的全部参数类型,以 元组类型 返回:
type F1 = (a: string, b: number) => void;
type F1ParamTypes = Parameters(F1);  // [string, number]