Skip to content

映射类型的属性过滤

上面我们通过Pick + Exclude实现了Omit类型工具,那我们能不能完全自己实现,不借助已有的类型工具呢?也可以,不过我们需要掌握一个技巧:通过as + never实现属性过滤的效果

typescript
type User = {
  readonly id: number;
  name: string;
  tel: string;
  address?: string;
};

type MyOmit<T, K extends keyof T> = {
  [P in keyof T as P extends K ? never : P]: T[P];
};

type A = MyOmit<User, 'tel' | 'address'>; //  {readonly id: number; name: string}

在例子中,映射 K in keyof T 获取类型 T 的每一个属性以后,后面紧跟着as其实是可以为键重新映射命名的

不过现在,它的键名重映射 as P extends K ? never : P,使用了条件运算符,又会触发分布式处理

原始属性 P逻辑判断 (P extends K)映射结果
"id"false"id"
"name"false"name"
"tel"truenever
"address"truenever

最终合并键集合"id" | "name" | never | never => "id" | "name"

我们还能再升级一下,比如:只保留User值类型是string类型的,生成新的类型

typescript
type PickStringValueType<T> = {
  [K in keyof T as T[K] extends string ? K : never]: T[K];
};
type FilteredUser = PickStringValueType<User>; //{name:string, tel:string}

当然,你想反过来,去掉值类型是string类型的,将Knever换个位置就行了

其实上面做的更加普遍性一些,就完全可以写成一个类型工具:

typescript
type PickByType<T, U> = {
  [P in keyof T as T[P] extends U ? P : never]: T[P];
};

type B = PickByType<User, number>; // { readonly id: number }