用ts定义一个属性二选一的对象

当我们不单单要明确定义参数的类型,而且如果参数为 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
}
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容