当我们不单单要明确定义参数的类型,而且如果参数为 object 的话,还有可能出现 object 里面某两个属性是冲突,只能“二选一”的情况
interface IMyParams {
a: number;
b: number;
}
// 如果我们需要将一个参数定义为对象,并且其属性 a 或者 b 必须要传递一个的话
function Table(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
}