TS 的一些高级类型 及 部分使用技巧

150 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情

一些高级类型

在很久之前有一次面试的时候, 经常也会有面试官问 ts, ts 的题型真的不错,要么就是现场出一个类型题,一般都是自己构造一个泛型这种,要么就是问一下 interface 和 type 的区别 , 或者 never 这种,但是偶尔会有问高级类型的,不多,但是如果我们知道这些高级类型,无疑会给我们的面试加分不少,虽然平时工作中确实很少用到哈哈哈。 那些面试题我就不放在这里了, 大家可以在网上随便找找。

  • 泛型类型 把原有的对象类型映射成新的对象类型 T,K 是类型参数 keyof 用于获取某种类型的所有key, 返回值为 key | key | key ... 是一个联合类型, K extends 是泛型约束 用于约束类型参数 K 对应的实际类型为 keyof 操作符拿到的所有 键组成的联合类型的子类型。 T[P] 用于获取 T 类型中 P 属性对应的类型,其中类型变量 P 的值会在遍历的过程中不断改变
type MyPick<T,K extends keyof T> = {
  [P in K] : T[P]
}
// 使用场景 通过 User 得到只有 id 和 name 的新类型
type User = {
  id:number;
  name:string;
  address:string
}
type PickedUser = MyPick<User,'id'|'name'> // 得到 { id:number ; name:string }
  • TS infer 类型 infer 只能在条件类型的 extends 子句中使用 infer 声明的类型变量 只在条件类型的 true 分支中可用
type UnpackedArray<T> = T extends(infer U)[] ? U : T ;  // 捕获数组中元素的类型
type UnpackedFn<T> = T extends (...args:any[]) => infer U ? U : T ; // 获取函数的返回值类型
type Unpacked<T> = T extends (infer U)[] ? U : T extends (...args:any[]) => infer U ? U : T extends Promise<infer U> ? U : T; // 可获取数组中的类型,函数及promise的返回值类型
// 使用场景 获取T0 数组中 的类型
type T0 = string[];
type U0 = UnpackedArray<T0>;  // string
// 使用场景  获取T1 函数类型的返回值类型, 遇到函数重载的时候, ts将使用最后一个调用签名进行类型推断
type T1 = () => string;
type U1 = UnpackedFn<T1>;
// 工具类型 Unpacked 的使用方法
type Unp0 = Unpacked<string>; // string
type Unp1 = Unpacked<string[]>; // string
type Unp2 = Unpacked<()=>string>; // string
type Unp3 = Unpacked<Promise<string>>; // string
type Unp4 = Unpacked<Promise<string>[]>; // Promise<string>
type Unp5 = Unpacked<Unpacked<Promise<string>[]>>; // string
  • 映射类型
// 语法: { readonly[P in K] ? : T}   readonly 表示添加只读修饰符, ? 表示移除可选修饰符  默认前面是加号
// 常见映射类型语法:
// {[P in K] : T},{[P in K] ?: T},{[P in K] -?: T},{ readonly [P in K] : T},{ readonly [P in K] ?: T},{ -readonly [P in K] : T}
// 示例:
type Item = { a:string;b:number;c:boolean}
type N1 = { [P in 'X' | 'Y']:number} // {x:number,y:number}
type N2 = { [P in 'X' | 'Y']:P}  // {x:'x',y:'y'}
type N3 = { [P in 'a' | 'b']:Item[P]}  // {a:string,b:number}
type N4 = { [P in keyof Item] ?: Item[P]} // {a:string,b:number,c:boolean}  这里加了问号,意思得到的就全部都是可选类型了
// 可选类型转化工具类型
type Partia<T> = {[P in keyof T] ?: T[P]}
// 同理 只读类型的转化工具类型
type myReadonly<T> = { readonly[P in keyof T] : T[P]}
  • Record Record的内部定义,接收两个泛型参数;Record后面的泛型就是对象键和值的类型 作用 :定义一个对象的 key 和 value 类型
// 实现示例: 
type myRecord<K extends string | number | symbol, T> = {
  [ P in K] : T
}
  • Omit 工具类型 过滤对象类型中不需要的类型 实现 使用了 ts 内置的 pick 和 exclude Exclude 用于过滤掉 从keyof中要排除的类型 再使用pick 工具类型 从原有的对象类型中 挑选出需保留的属性 组合成新的对象类型
type myOmit<T,K extends keyof any> = Pick<T,Exclude<keyof T,K>>;
// 示例:
type User2 = {
  id:string;
  name:string;
  password:string;
  createAt:Date;
  update:Date
}
type myUser2 = myOmit<User2,"id" | "createAt" | "update"> // {name:string,password:string}
  • 模板字面量 类型使用 ${T}+change T为传入的泛型 T 可以是 string | number | boolean | bigInt
// 示例:
type EventName<T extends string> = `${T}Change`;
type Concat<S1 extends string,S2 extends string> = `${S1}-${S2}`;
type Y1 = EventName<'foo'> // 'fooChange'
type Y2 = EventName<'foo' | 'bar' | 'bar2'> // 得到联合类型 'fooChange' | 'barChage' | 'bar2change'
type Y3 = Concat<'top' | 'bottom','left' | 'right'> // 得到联合类型 'top-left' | 'top-right' | 'bottom-left' | bottom-right' 是因为多个占位符的联合类型会解析为叉积

一些使用技巧

平时自己在开发的过程中 偶尔会遇到的一些使用上的小问题,给出的一些解决方案,大家有自己的其他 小 tips 也可以发给我 ,一起成长

  • 获取一个对象的 key 作为类型 这个使用的场景还是比较多的, 可以作为我们接口的返回值类型,或者当我们声明了一个映射类型,在代码中需要使用的时候,会提示我们这个 变量没有类型声明, 就可以使用这种方式
// 我们可能有一个这样的对象
const origin = {
  ti:{
    add:'addTiDone',
    cancel:'cancelTiAdd'
  },
  meterial:{
    add:'addMaterailDone',
    cancel:'cancelMaterialAdd'
  }
}
// 使用 type 定义类型
type originKeyType = keyof typeof origin
// 可以在函数中使用
function a (res:originKeyType){ } 

// 我们也可以直接 使用 keyof 拿到我们 ts 类型的 key, 这样实际上得到的就是这个接口中的 key 组成的一个联合类型
export interface FilterSelect {
    userType: string;
    name: string;
    classId: string;
    wrongQuestions: string;
}
selects[name as keyof FilterSelect] = value