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

TypeScript泛型问题:判断泛型对象属性无法确定其类型

解决postMessage监听器的TypeScript类型推断问题

你的问题出在泛型的定义位置:把泛型<M extends keyof Messages>放在了监听函数上,导致TypeScript无法根据event.data.method的具体值来窄化event.data.data的类型——因为泛型M是整个函数的参数,不是针对每个消息类型的联合分支。

正确的做法是先定义所有合法消息的联合类型,再让监听器接收这个联合类型的MessageEvent,这样TypeScript就能通过method的判断自动完成类型窄化。

修改后的代码如下:

interface Messages {
    'say-hi': {
        greeting: string;
    };
    'say-bye': {
        farewell: string;
    };
}

// 生成所有合法消息数据的联合类型
type MessageEventData = {
    [M in keyof Messages]: {
        method: M;
        data: Messages[M];
    };
}[keyof Messages];

// 监听器接收的是MessageEvent<MessageEventData>
type MessageListener = (
    e: MessageEvent<MessageEventData>
) => any;

const addMessageEventListener = (listener: MessageListener): void =>
    window.addEventListener('message', listener);

addMessageEventListener(event => {
    if (event.data.method === 'say-hi') {
        // 现在可以正常访问greeting,类型推断生效了
        console.log(event.data.data.greeting);
    } else if (event.data.method === 'say-bye') {
        console.log(event.data.data.farewell);
    }
});

为什么原来的写法不行?

你原来的泛型写法会让event.data的类型变成{ method: M; data: Messages[M]; },但M是整个函数的泛型参数,TypeScript无法在函数内部根据method的具体值来锁定M的类型,所以event.data.data会被推断成Messages[keyof Messages](也就是两个类型的联合),自然无法直接访问greeting或farewell。

而用联合类型MessageEventData的话,每个分支都有明确的method和对应的data类型,TypeScript能识别出event.data.method === 'say-hi'是一个有效的类型守卫,自动把event.data.data窄化为Messages['say-hi']。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 12:25:21