如何在Typescript中动态地从联合类型的函数中选择泛型?



假设我有这样一个函数:

type FooParams <Params extends unknown[], Result> = { 
name: string, 
request: (...params: Params) => Promise<Result> 
}
const foo = <Params extends unknown[], Result>(params: FooParams<Params, Result>) => {
// do stuff
}

假设我有几个请求和一个"商店"。包含这些请求:

interface Todo {
id: number;
title: string;
}
const getTodos: () => Promise<Todo[]> = () => Promise.resolve([{ id: 2, title: 'clean' }]);
const getTodo: (id: number) => Promise<Todo> = (id: number) => Promise.resolve({ id, title: 'clean' });
const requestStore = {
getTodo: {
name: 'getTodo',
request: getTodo,
},
getTodos: {
name: 'getTodos',
request: getTodos,
},
} as const;

我现在想为存储中的每个请求生成foo函数。

为存储中的每个请求手动添加显式键:

// Works
foo(requestStore['getTodo'])

但是像这样动态地添加它们是行不通的:

// Does not work. Error message:
// Type '(() => Promise<Todo[]>) | ((id: number) => Promise<Todo>)' is not assignable to type '() => Promise<Todo[]>'.
//  Type '(id: number) => Promise<Todo>' is not assignable to type '() => Promise<Todo[]>'.(2322)
const createFooFromStore = (requestName: keyof typeof requestStore) => () => {
const { name, request } = requestStore[requestName]
foo({ name, request })
}

是否有某种方法可以重写这个,以便可以为"requeststore"中的每个条目创建foo函数?

下面是一个带有示例代码的游乐场链接:

游乐场

最底部的"request"参数显示了一条错误消息。

我不能把这个游乐场的链接放到评论区…但这是有效的游乐场

// The type constraint here does the trick.
// It allows every specified key in the Requeststore as an input for foo
// The 
const foo = <T extends RequestStore[keyof RequestStore]>(params: T) => {
// return params to check type infering
return params
}
interface Todo {
id: number;
title: string;
}
const getTodos: () => Promise<Todo[]> = () => Promise.resolve([{ id: 2, title: 'clean' }]);
const getTodo: (id: number) => Promise<Todo> = (id: number) => Promise.resolve({ id, title: 'clean' });
// if you want to ensure typesafety here you can use the new satisfies keyword to prevent wrong request definitions but you don't need it
const requestStore = {
getTodo: {
name: 'getTodo',
request: getTodo,
},
getTodos: {
name: 'getTodos',
request: getTodos,
},
} as const satisfies Record<string, { name: string, request: (...args: any[]) => any }>;
type RequestStore = typeof requestStore
// Works
foo(requestStore['getTodo'])
foo(requestStore['getTodos'])

const createFooFromStore = <T extends keyof typeof requestStore>(requestName: T) => () => foo(requestStore[requestName])
const a = createFooFromStore("getTodo") // valid
const b = createFooFromStore("getTodos") // valid
const c = createFooFromStore("getTos") // invalid
const createFooFromStore2 = <T extends keyof typeof requestStore>(requestName: T) => () => { foo(requestStore[requestName]) }

最新更新