TypeScript:在 Promise<T> 中包装函数参数
TypeScript: Wrap function parameters in Promise<T>
我尝试创建一个采用函数类型的类型,将函数参数包装在 Promise<>
和 returns 一个新类型中 - 相同的函数但参数与 Promise 相同,例如:
type PromisedFn = PromisedArgs<(a: number, b: string | number) => void>
// should produce (a: Promise<number>, b: Promise<string | number>) => void
我绞尽脑汁想了几个小时才得到这样的代码:
type Func = (id: number, guid: number | string) => number | string;
type FuncParams = Parameters<Func>;
// FuncParams = [id: string, guid: string | number]
type FuncWrappedParams = {
[K in keyof FuncParams as Extract<K, '0'|'1'>]: Promise<FuncParams[K]>
}
// { 0: Promise<number>, 1: Promise<string | number> }
仍然无法将数字索引对象正确应用为数组:
type FuncWrappedParamsArray = [...args: FuncWrappedParams[]];
type WrappedParamsFunc = (...args: FuncWrappedParamsArray) => ReturnType<Func>;
let func: WrappedParamsFunc = (
id: Promise<number>,
guid: Promise<number | string>
) => 'TestString';
// ERROR:
// Type '(id: Promise<number>, guid: Promise<number | string>) => string'
// is not assignable to type 'WrappedParamsFunc'.
// Types of parameters 'id' and 'args' are incompatible.
// Type 'FuncWrappedParams' is missing the following properties from type 'Promise<number>':
// then, catch, [Symbol.toStringTag]
我不知道怎么处理这个。
问题 #1 如上。
问题#2:缺点是我们必须提前知道一些函数参数:[K in keyof FuncParams as Extract<K, '0'|'1'>]
.
你可以这样写PromisedArgs
:
type PromisedArgs<T extends (...args: any) => any> =
T extends (...args: infer A) => infer R ? (
(...args: { [I in keyof A]: Promise<A[I]> }) => R
) : never;
只要你的数组类型是generic type parameter (like A
above), a mapped type over it will produce another array type。在 {[I in keyof A]: ...}
中,键类型 I
本质上只是自动迭代 numeric-like 索引。您不必手动获取 "0" | "1" | "2" | ...
或担心如何将生成的映射类型提升回数组。
使用the infer
keyword in the conditional type check is just a way to get both the parameters and return type of the function at once, instead of using the Parameters<T>
and the ReturnType
utility types separately. If you look at the definitions of Parameters
and of ReturnType
你会发现它们的实现方式是一样的
无论如何,让我们确保它按预期工作:
type Func = (id: number, guid: number | string) => number | string;
type WrappedParamsFunc = PromisedArgs<Func>
/* type WrappedParamsFunc =
(id: Promise<number>, guid: Promise<string | number>) => string | number */
let func: WrappedParamsFunc = (
id: Promise<number>,
guid: Promise<number | string>
) => 'TestString';
看起来不错!
顺便说一句,在 microsoft/TypeScript#27995 上报告了 TypeScript 中的一个错误,这可能会阻止您自己找到此解决方案。事实证明,您正在映射类似数组 通用类型参数 的键是很重要的。如果您尝试映射特定类型,映射将退回到遍历所有键,甚至是数组方法和东西:
type Foo = [1, 2, 3]
type Bar = { [I in keyof Foo]: Promise<Foo[I]> };
/* type Bar = {
[x: number]: Promise<3 | 1 | 2>;
0: Promise<1>;
1: Promise<2>;
2: Promise<3>;
length: Promise<3>;
toString: Promise<() => string>;
...
*/
因为 Foo
是一个特定的类型而不是类型参数,所以你得到上面的混乱。您的 FuncParams
是一个特定的类型,例如 Foo
,并且您尝试手动清理混乱,但效果不佳。好吧!
我尝试创建一个采用函数类型的类型,将函数参数包装在 Promise<>
和 returns 一个新类型中 - 相同的函数但参数与 Promise 相同,例如:
type PromisedFn = PromisedArgs<(a: number, b: string | number) => void>
// should produce (a: Promise<number>, b: Promise<string | number>) => void
我绞尽脑汁想了几个小时才得到这样的代码:
type Func = (id: number, guid: number | string) => number | string;
type FuncParams = Parameters<Func>;
// FuncParams = [id: string, guid: string | number]
type FuncWrappedParams = {
[K in keyof FuncParams as Extract<K, '0'|'1'>]: Promise<FuncParams[K]>
}
// { 0: Promise<number>, 1: Promise<string | number> }
仍然无法将数字索引对象正确应用为数组:
type FuncWrappedParamsArray = [...args: FuncWrappedParams[]];
type WrappedParamsFunc = (...args: FuncWrappedParamsArray) => ReturnType<Func>;
let func: WrappedParamsFunc = (
id: Promise<number>,
guid: Promise<number | string>
) => 'TestString';
// ERROR:
// Type '(id: Promise<number>, guid: Promise<number | string>) => string'
// is not assignable to type 'WrappedParamsFunc'.
// Types of parameters 'id' and 'args' are incompatible.
// Type 'FuncWrappedParams' is missing the following properties from type 'Promise<number>':
// then, catch, [Symbol.toStringTag]
我不知道怎么处理这个。
问题 #1 如上。
问题#2:缺点是我们必须提前知道一些函数参数:[K in keyof FuncParams as Extract<K, '0'|'1'>]
.
你可以这样写PromisedArgs
:
type PromisedArgs<T extends (...args: any) => any> =
T extends (...args: infer A) => infer R ? (
(...args: { [I in keyof A]: Promise<A[I]> }) => R
) : never;
只要你的数组类型是generic type parameter (like A
above), a mapped type over it will produce another array type。在 {[I in keyof A]: ...}
中,键类型 I
本质上只是自动迭代 numeric-like 索引。您不必手动获取 "0" | "1" | "2" | ...
或担心如何将生成的映射类型提升回数组。
使用the infer
keyword in the conditional type check is just a way to get both the parameters and return type of the function at once, instead of using the Parameters<T>
and the ReturnType
utility types separately. If you look at the definitions of Parameters
and of ReturnType
你会发现它们的实现方式是一样的
无论如何,让我们确保它按预期工作:
type Func = (id: number, guid: number | string) => number | string;
type WrappedParamsFunc = PromisedArgs<Func>
/* type WrappedParamsFunc =
(id: Promise<number>, guid: Promise<string | number>) => string | number */
let func: WrappedParamsFunc = (
id: Promise<number>,
guid: Promise<number | string>
) => 'TestString';
看起来不错!
顺便说一句,在 microsoft/TypeScript#27995 上报告了 TypeScript 中的一个错误,这可能会阻止您自己找到此解决方案。事实证明,您正在映射类似数组 通用类型参数 的键是很重要的。如果您尝试映射特定类型,映射将退回到遍历所有键,甚至是数组方法和东西:
type Foo = [1, 2, 3]
type Bar = { [I in keyof Foo]: Promise<Foo[I]> };
/* type Bar = {
[x: number]: Promise<3 | 1 | 2>;
0: Promise<1>;
1: Promise<2>;
2: Promise<3>;
length: Promise<3>;
toString: Promise<() => string>;
...
*/
因为 Foo
是一个特定的类型而不是类型参数,所以你得到上面的混乱。您的 FuncParams
是一个特定的类型,例如 Foo
,并且您尝试手动清理混乱,但效果不佳。好吧!