Typescript 骚操作大全
排除原类型中特定 Key 或者特定 Value
当你要 排除(Omit) 或者 选取(Pick) 某些属性的时候,当然你可以通过 Omit<IOrigin, "key1" | "key2">
,但是如果有没有一种方法可以 批量处理 呢,下面就给你演示骚操作
interface IOrigin { [name: number]: string name: string gender: string age: number getName: () => string getGener: () => string getAge: () => number } /** * 简单 Omit 排除特定 Key 的属性 * { * [name: number]: string; * gender: string; * age: number; * getGener: () => string; * getAge: () => number; * } */ type OmitSimply = Omit<IOrigin, "name" | "getName"> // 通过 infer 排除特定 Key 的属性 type OmitInferKey<T, R> = { [K in keyof T as T extends R ? never : K]: T[K] } /** * 排除 Key 类型为 number 的属性 * { * name: string; * gender: string; * age: number; * getName: () => string; * getGener: () => string; * getAge: () => number; * } */ type OmitInferKeyNumber = OmitInferKey<IOrigin, number> /** * 排除 Key 类型为 `get${string}` 的属性 * { * [name: number]: string; * name: string; * gender: string; * age: number; * } */ type OmitInferKeyRegExp = OmitInferKey<IOrigin, `get${string}`> // 通过 infer 排除特定 Value 的属性 type OmitInferValue<T, R> = { [K in keyof T as T[K] extends R ? never : K]: T[K] } /** * 排除 Value 类型为 Function 的属性 * { * [name: number]: string; * name: string; * gender: string; * age: number; * } */ type OmitInferKeyFunction = OmitInferValue<IOrigin, Function> 复制代码
这里演示下“排除(Omit)”如何实现,你可以举一反三,实现“选取(Pick)”
类型中某些属性只能“二选一”
当我们不单单要明确定义参数的类型,而且如果参数为 object
的话,还有可能出现 object
里面某两个属性是冲突,只能“二选一”的情况。
interface IMyParams { a: number; b: number; } // 如果我们需要将一个参数定义为对象,并且其属性 a 或者 b 必须要传递一个的话 function calc(params: IMyParams): number{ if (params.a) { console.log(params.a + 1); } else { console.log(params.b + 1); } } 复制代码
下面定义了一个 EitherOr
的类型来处理这种情况:
type FilterOptional<T> = Pick< T, Exclude< { [K in keyof T]: T extends Record<K, T[K]> ? K : never; }[keyof T], undefined > >; type FilterNotOptional<T> = Pick< T, Exclude< { [K in keyof T]: T extends Record<K, T[K]> ? never : K; }[keyof T], undefined > >; type PartialEither<T, K extends keyof any> = { [P in Exclude<keyof FilterOptional<T>, K>]-?: T[P] } & { [P in Exclude<keyof FilterNotOptional<T>, K>]?: T[P] } & { [P in Extract<keyof T, K>]?: undefined }; type Object = { [name: string]: any; }; export type EitherOr<O extends Object, L extends string, R extends string> = ( PartialEither<Pick<O, L | R>, L> | PartialEither<Pick<O, L | R>, R> ) & Omit<O, L | R>; 复制代码
使用例子:
// a、b二选一,并且必须传递一个 type RequireOne = EitherOr< { a: number; b: string; }, 'a', 'b' >; // a、b二选一,或者都不传 type RequireOneOrEmpty = EitherOr< { a?: number; b?: string; }, 'a', 'b' >; 复制代码
实际应用:
interface IColumn { title: string; dataIndex: string; render: () => React.ReactNode; } // 熟悉 antd 的同学应该都知道,如果传递了 render 的话,其他 dataIndex 其实就没意义 // 换个角度来说,其实它们两个是“二选一”的属性 interface ITableProps { columns: Array< EitherOr< IColumn, 'dataIndex', 'render' > >; } function Table(props: ITableProps){ // TODO }
作者:tellyourmad
链接:https://juejin.cn/post/7025851349103280164