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

TypeScript函数联合类型参数交叉问题的解决方法

实现类型安全的事件回调类型

目标结构

需要定义满足如下要求的函数类型:

(eventName: EventNames, ...params: [ParamsTypeForEventName]) => void

核心规则:传入的eventName可唯一确定对应事件的参数类型,已有事件名到参数元组的映射类型结构:

type T1 = {
    // 键为事件名,值为该事件对应的参数类型元组
    [eventName: string]: any[]
};

原有实现的问题

原有写法通过映射生成函数联合类型,代码如下:

const names = Object.freeze({
    n1: 'n1',
    n2: 'n2'
});

type T1 = {
    [names.n1]: [arg0: number],
    [names.n2]: [arg0: string, arg1: boolean]
};

type ValueOf<T> = T[keyof T];

type ObjectWithFunc<T extends {[name: string]: any[]}> = {
    func: ValueOf<{
        [Name in keyof T]: ((name:Name, ...args:T[Name])=>void)
    }>
}

let objectWithFunc:ObjectWithFunc<T1>;
objectWithFunc = { func: ()=>{} };

objectWithFunc.func('n1',10);

type ExtractedType = typeof objectWithFunc.func;

虽然悬停查看ExtractedType显示为两个函数的联合:

type ExtractedType = ((name: "n1", arg0: number) => void) | ((name: "n2", arg0: string, arg1: boolean) => void)

但TypeScript对函数联合做类型检查时,会对参数执行逆变合并,即参数取交叉类型,最终func的实际检查类型为:

(property) func: (name: never, arg0: never, arg1: boolean) => void

导致合法调用也会报类型错误。

正确实现

不要使用函数联合类型,改为定义泛型函数,调用时根据传入的事件名动态匹配对应参数类型,即可避免参数交叉问题:

const names = Object.freeze({
    n1: 'n1',
    n2: 'n2'
});

type T1 = {
    [names.n1]: [arg0: number],
    [names.n2]: [arg0: string, arg1: boolean]
};

type ObjectWithFunc<T extends {[name: string]: any[]}> = {
    func: <EventName extends keyof T>(
        eventName: EventName, 
        ...params: T[EventName]
    ) => void
}

let objectWithFunc:ObjectWithFunc<T1>;
objectWithFunc = { func: ()=>{} };

// 类型校验符合预期
objectWithFunc.func('n1', 10); // 校验通过
objectWithFunc.func('n2', 'abc', true); // 校验通过
objectWithFunc.func('n1', '非法参数'); // 校验报错,参数类型不匹配
objectWithFunc.func('n2', 123, false); // 校验报错,第一个参数类型不匹配

实现原理:泛型参数EventName会在函数调用时根据传入的eventName自动推导为具体的事件名字面量类型,再直接从映射类型T中取对应的参数元组类型,整个过程不会生成函数联合,自然不会触发参数逆变交叉的逻辑,完全实现事件名和参数类型的绑定。


内容的提问来源于stack exchange,提问作者kharon4

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 17:06:28