typescript - 如何获取以下变量的对应类型
interface Model {
bar: number
foo: string
}
async function getSth(a: number, b: string): Promise<Model> {
return await Promise.resolve({
bar: a,
foo: b,
})
}
function createA(m: Model): number {
return m.bar
}
function createB(m: Model): string {
return m.foo
}
GetSth 是一个异步函数。createA 和 createB 都需要依赖它的返回值作为参数来运行。所以我想创建一个带有记忆功能的异步函数,以便于使用,如下所示:
function memorifyFuncs<T extends any[]>(asyncFunc: (...args: T) => Promise<any>) {
return <Fs extends ((...args: any[]) => any)[]>(...createFuncs: Fs) => {
const cache: Record<string, any> = {}
return createFuncs.map(
<F extends (...args: any[]) => any>(func: F) =>
async (...args: T): Promise<ReturnType<F>> => {
const key = args.map((arg) => JSON.stringify(arg)).join('&')
if (!cache[key]) {
cache[key] = await asyncFunc(...args)
}
return func(cache[key])
}
)
}
}
MemoryFuncs 接收类似于 getSth 的异步函数,在内部设置缓存,然后接收并构建类似于 createA 和 createB 的函数。它缓存实现过程,并通过 map 进行映射。然而,我发现返回的对应函数无法获取它的返回值类型:
在此输入图片描述
获取下面变量的对应类型
答案#1
你需要使 memorityFuncs 的调用签名更加通用,可能如下所示:
declare function memorifyFuncs<T extends any[], R>(
asyncFunc: (...args: T) => Promise<R>
): <O extends any[]>(...createFuncs: {
[I in keyof O]: (arg: R) => O[I]; }
) => { [I in keyof O]: (...args: T) => Promise<O[I]>; }
在这里,我们希望 memorifyFuncs 不仅在对应于 asyncFunc 回调的参数列表的 T 上是通用的,而且在对应于该回调的 await 返回类型的 R 上也是通用的。你还需要返回的函数在 O 上是通用的,O 是 createFuncs 中每个函数的 await 输出类型的元组。因此,如果 createFuncs 的类型是 [(x: string) => number, (x: string) => boolean, (x: string) => Date],那么 O 将是 [number, boolean, Date]。
然后我们可以将 createFuncs 的类型和返回类型写成映射元组类型。createFuncs 的类型是 {[I in keyof O]: (arg: R) => O[I]},这意味着它是一个函数数组:每个函数都以 R 作为输入,并输出 O 的对应元素类型。输出的类型是 {[I in keyof O]: (...args: T) => Promise<O[I]>},这意味着它也是一个函数数组:每个函数都接受与 asyncFunc 相同的输入,并输出对应 O 元素类型的 Promise。注意,因为 createFuncs 的类型是针对 O 的 同态映射类型(参见“同态映射类型是什么意思?”),编译器能够从 createFuncs 推断 出 O(否则表达起来会更复杂)。
我不会去担心让编译器对 memorifyFuncs 的实现感到满意。它不可能判断你的算法是否遵守了映射元组类型的规则。你应该直接用断言解决这个问题,或者使用 any 类型来放宽检查:
function memorifyFuncs<T extends any[], R>(asyncFunc: (...args: T) => Promise<R>) {
return <O extends any[]>(...createFuncs: { [I in keyof O]: (arg: R) => O[I] }):
{ [I in keyof O]: (...args: T) => Promise<O[I]> } => {
const cache: Record<string, any> = {}
return createFuncs.map(
(func: any) =>
async (...args: any) => {
const key = args.map((arg: any) => JSON.stringify(arg)).join('&')
if (!cache[key]) {
cache[key] = await asyncFunc(...args)
}
return func(cache[key])
}
) as any
}
}
好吧,我们来试试:
const [funcA, funcB] = memorifyFuncs(getSth)(createA, createB)
// const funcA: (a: number, b: string) => Promise<number>
// const funcB: (a: number, b: string) => Promise<string>
async function foo() {
const n = await funcA(1, "two");
console.log(n.toFixed(2)) // "1.00"
const s = await funcB(2, "three");
console.log(s.toUpperCase()) // "THREE"
}
foo();
看起来不错。编译器推断 funcA 的类型是 (a: number, b: string) => Promise<number>,因为 createA 返回一个 number……并且它推断 funcB 的类型是 (a: number, b: string) => Promise<string>,因为 createB 返回一个 string。
代码的 Playground 链接
