打字稿:基于参数道具的条件返回类型

时间:2021-03-07 12:43:34

标签: typescript typescript-generics

我有一个函数,我希望它根据参数 props 返回 2 种不同的类型。

interface IPaginateParams {
  perPage: number;
  currentPage: number;
  isFromStart?: boolean;
}

interface IWithPagination<Data, TParams extends IPaginateParams = IPaginateParams> {
  data: Data;
  pagination: IPagination<TParams>;
}

type IPagination<TParams> = TParams extends
  | { currentPage: 1 }
  | { isFromStart: true }
  | { isLengthAware: true }
  ? ILengthAwarePagination
  : IBasePagination;

interface IBasePagination {
  currentPage: number;
  perPage: number;
  from: number;
  to: number;
}

interface ILengthAwarePagination extends IBasePagination {
  total: number;
  lastPage: number;
}

function paginate<TData = any[], TParams extends IPaginateParams = IPaginateParams>(
  options: TParams
): IWithPagination<TData, TParams>;

这个想法是,如果您传递 currentPage: 1isFromStart: true,它应该向 pagination 对象添加 2 个额外的类型。

奇怪的是 IWithPagination 按预期工作,

const data = {} as IWithPagination<any, {perPage: 2, currentPage: 1}>;

expectType<ILengthAwarePagination>(data.pagination);

但是当我使用调用时,它总是返回 IBasePagination

const data = paginate({perPage: 2, currentPage: 1});

expectType<ILengthAwarePagination>(data.pagination) // fails

// or

const data = paginate({perPage: 2, currentPage: 2, isFromStart: true});

expectType<ILengthAwarePagination>(data.pagination) // fails

Playground

1 个答案:

答案 0 :(得分:1)

正如@OlegValter 在评论中所解释的,当将对象传递给 paginate 时,它推断为扩展类型,例如:

{perPage: 2, currentPage: 2, isFromStart: true} // inferred as {perPage: number; currentPage: number; isFromStart: boolean}

因此,返回类型的检查总是回退到 IBasePagination 类型(else 子句)。

我们需要做的就是指定函数的参数是readonly

declare function paginate<TData = any[], TParams extends IPaginateParams = IPaginateParams>(
  options: Readonly<TParams>
  // ---------^ this is what made the input as a narrow type
): IWithPagination<TData, TParams>;

工作example