【问题标题】:Typescript: Conditional return type based on arguments props打字稿:基于参数道具的条件返回类型
【发布时间】:2021-03-08 01:06:58
【问题描述】:

我有一个函数,我希望它根据其参数 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

【问题讨论】:

  • 能否请您在 TS 操场上分享完全可重现的示例,这样会更容易提供帮助
  • 原因很简单——类型加宽。如果你检查你的函数调用,你会发现参数不是一个字面量,而是一个推断的{ currentPage: number; perPage: number;},显然,它不是{ currentPage: 1 }{ isFromStart: true } 的子类型,而是第一个的超类型,因此extends 子句失败,编译器将其解析为IBasePagination。解决方法:在字面上使用as const
  • @captain-yossarian - 谢谢,我还注意到我夸大了类型是 both 的超类型,当然它还没有更正。跨度>
  • @OlegValter,谢谢你的解释!我可以把它作为 const 放在函数定义中的某个地方吗?这些方法和类型是我正在维护的库的一部分
  • 只读参数有效!谢谢@OlegValter

标签: typescript typescript-generics


【解决方案1】:

正如@OlegValter 在 cmets 中解释的那样,当将对象传递给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

【讨论】:

    猜你喜欢
    • 2019-11-25
    • 2021-09-05
    • 2021-09-18
    • 2021-09-17
    • 1970-01-01
    • 1970-01-01
    • 2022-07-07
    • 2019-07-15
    • 1970-01-01
    相关资源
    最近更新 更多