You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何收窄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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 06:18:10