我有一个简单的 api 调用函数,它可以获取数据并根据参数标志有条件地映射结果。
作为获取结果,我得到了条件类型或联合类型,但我无法在这里使用类型保护,因为这两种类型不共享共同的可区分属性,并且我应该根据外部来区分它们类型。我如何告诉打字稿这两种类型依赖于另一种类型?是不是这样就需要再添加一层间接层呢?
// prerequisites
declare function apiService(...a: any[]): any;
declare function mapExternalFoo(x: IExternalFoo): IFoo;
declare function mapExternalBar(x: IExternalBar): IBar;
interface IFoo { a: any; }
interface IBar { b: any; }
interface IExternalFoo { c: any; }
interface IExternalBar { d: any; }
interface IProps<T extends boolean> {
isExtended?: T;
}
// I want it to look like this, but it fails with a compile error
function fetchData<T extends boolean>(params: IProps<T>): Promise<T extends true ? IFoo : IBar> {
return apiService('/user/data', params).then(
(data: T extends true ? IExternalFoo[] : IExternalBar[]) =>
// Cannot invoke an expression whose type lacks a call signature.
params.isExtended ? data.map(mapExternalFoo) : data.map(mapExternalBar)
);
}
Run Code Online (Sandbox Code Playgroud)
我能够让它与类型转换一起使用,并通过函数调用获得正确的返回类型,但这很麻烦,我觉得这不是正确的方法
function fetchData(params: IProps<true>): Promise<IFoo[]>;
function fetchData(params: IProps<false>): Promise<IBar[]>;
function fetchData<T extends boolean>(params: IProps<T>) {
return apiService('/user/data', params)
.then(
(data: IExternalFoo[] | IExternalBar[]) =>
params.isExtended
? (data as IExternalFoo[]).map(mapExternalFoo)
: (data as IExternalBar[]).map(mapExternalBar)
);
}
Run Code Online (Sandbox Code Playgroud)
为条件类型添加类型别名有助于消除缺少调用签名错误,但我却收到了类型不匹配错误
type INarrow<T extends boolean> = T extends true ? IExternalFoo[] : IExternalBar[];
Run Code Online (Sandbox Code Playgroud)
TypeScript 并没有真正将其控制流分析分布在联合类型上,并且对条件类型的处理更不优雅。在这种情况下,我希望您可以告诉编译器手动遍历表达式的某些可能的缩小范围,并且当且仅当所有这些缩小范围都被视为类型安全时才能成功。唉,这并不存在,而且很难说这个痛点在可预见的未来是否会得到改善。
考虑到这种不幸的情况,最直接的方法可能是在函数实现中使用类型断言(例如您的data as IExternalFoo[]),再加上函数签名的重载(您只需要一个带有条件类型的重载签名...以及一个带有联合的实现签名)。这几乎正是你所做的,所以除了“这也是我在这种情况下所做的”之外,我没有太多建议给你。条件类型对于函数调用者来说比对于函数实现者有用得多。
所以我唯一的建议是您可以将条件类型保留在重载签名中:
function fetchData<T extends boolean>(params: IProps<T>): Promise<T extends true ? IFoo[] : IBar[]>;
function fetchData(params: IProps<boolean>): Promise<IFoo[] | IBar[]> {
return apiService('/user/data', params)
.then(
(data: IExternalFoo[] | IExternalBar[]) =>
params.isExtended
? (data as IExternalFoo[]).map(mapExternalFoo)
: (data as IExternalBar[]).map(mapExternalBar)
);
}
const promseIFooArray = fetchData({isExtended: true});
const promseIBarArray = fetchData({isExtended: false});
const promiseEitherArray = fetchData({isExtended: Math.random()<0.5});
Run Code Online (Sandbox Code Playgroud)
哦,好吧,希望有帮助。祝你好运!
| 归档时间: |
|
| 查看次数: |
1256 次 |
| 最近记录: |