如何收窄TypeScript函数参数类型?BuilderUsingPlugins类型问题
问题描述
BuilderUsingPlugins类的addEntry方法本应通过pluginValueType参数确定pluginParams的类型,但实际并未根据pluginValueType收窄类型,反而生成了所有传入插件createEntry参数的联合类型。需要让pluginParams匹配以pluginValueType为键的插件对应的参数类型,而非所有插件参数的联合类型。
原代码示例
type plugin = { name:string, createEntry(...params:any[]):any } type PluginMap<T extends ReadonlyArray<plugin>> = { [key in T[number] as key["name"]]: key["createEntry"] extends ((...args:any[])=>any)? key["createEntry"]:never } type valueStore<T extends ReadonlyArray<plugin>> ={ [k in string]: ReturnType<T[number]["createEntry"]> } type storeEntry<k extends string,T extends (...args:any)=>any>={ store:{ [key in k]:ReturnType<T> } } class BuilderUsingPlugins<T extends ReadonlyArray<plugin>> { store: valueStore<T> = { } plugins: PluginMap<T> addEntry( pluginValueType: keyof PluginMap<T>, name: string, ...pluginParams: Parameters<PluginMap<T>[keyof PluginMap<T>]> extends Parameters<PluginMap<T>[typeof pluginValueType]>? Parameters<PluginMap<T>[keyof PluginMap<T>]> :never ): this & storeEntry<string extends keyof this["store"]? never:string,ReturnType<PluginMap<T>[typeof pluginValueType]>> { this["store"][name] = this.plugins[pluginValueType as keyof PluginMap<T>](pluginParams) return this as this & storeEntry<string extends keyof this["store"]? never:string,ReturnType<PluginMap<T>[typeof pluginValueType]>> } constructor(params:T){ this.plugins = params.reduce((acc,curr)=>{ return { ...acc, ...{[curr.name]:curr.createEntry} as const satisfies PluginMap<[typeof curr]> } },{} ) as PluginMap<typeof params> } } const fooPlugin={ createEntry(){ return 'I am the "foo" plugin!' }, name:"foo" } as const satisfies plugin const barPlugin = { name:"bar", createEntry(someparam:number){ return someparam } } as const satisfies plugin const MyPluginList = [fooPlugin,barPlugin] as const const builder = new BuilderUsingPlugins<typeof MyPluginList>(MyPluginList) builder.addEntry("bar","barEntry") // @ts-expect-error builder.addEntry("foo","fooEntry")
解决方案
要实现pluginParams与对应插件参数类型的精准匹配,需将addEntry改为泛型方法,通过泛型参数捕获pluginValueType的具体键类型,从而关联对应插件的createEntry参数类型。修改后的代码如下:
type plugin = { name: string, createEntry(...params: any[]): any } type PluginMap<T extends ReadonlyArray<plugin>> = { [key in T[number] as key["name"]]: key["createEntry"] extends ((...args: any[]) => any) ? key["createEntry"] : never } type valueStore<T extends ReadonlyArray<plugin>> = { [k in string]: ReturnType<T[number]["createEntry"]> } type storeEntry<K extends string, T extends (...args: any) => any> = { store: { [key in K]: ReturnType<T> } } class BuilderUsingPlugins<T extends ReadonlyArray<plugin>> { store: valueStore<T> = {} plugins: PluginMap<T> // 将addEntry改为泛型方法,用K捕获pluginValueType的具体类型 addEntry<K extends keyof PluginMap<T>>( pluginValueType: K, name: string, ...pluginParams: Parameters<PluginMap<T>[K]> ): this & storeEntry<K extends keyof this["store"] ? never : string, ReturnType<PluginMap<T>[K]>> { // 修正参数调用方式:展开pluginParams,匹配插件createEntry的参数接收形式 this.store[name] = this.plugins[pluginValueType](...pluginParams) return this as this & storeEntry<K extends keyof this["store"] ? never : string, ReturnType<PluginMap<T>[K]>> } constructor(params: T) { this.plugins = params.reduce((acc, curr) => { return { ...acc, ...{ [curr.name]: curr.createEntry } as const satisfies PluginMap<[typeof curr]> } }, {}) as PluginMap<typeof params> } } const fooPlugin = { createEntry() { return 'I am the "foo" plugin!' }, name: "foo" } as const satisfies plugin const barPlugin = { name: "bar", createEntry(someparam: number) { return someparam } } as const satisfies plugin const MyPluginList = [fooPlugin, barPlugin] as const const builder = new BuilderUsingPlugins(MyPluginList) // 正确:bar插件传入number类型参数 builder.addEntry("bar", "barEntry", 123) // 正确:foo插件无需参数 builder.addEntry("foo", "fooEntry") // 错误:bar插件缺少参数,触发TypeScript报错 // builder.addEntry("bar", "barEntry") // 错误:foo插件多传参数,触发TypeScript报错 // builder.addEntry("foo", "fooEntry", 456)
关键修改点
- 将
addEntry改为泛型方法,引入泛型参数K extends keyof PluginMap<T>,精准捕获pluginValueType的具体键类型。 - 用
Parameters<PluginMap<T>[K]>直接作为pluginParams的类型,实现参数类型与对应插件的绑定。 - 修正插件调用逻辑,将
pluginParams数组展开为独立参数传入createEntry,匹配插件的参数接收形式。
内容的提问来源于stack exchange,提问作者A Kam
相关产品推荐
相关产品推荐

