TypeScript中能否基于keyof模板实现类型继承?
解决TypeScript中基于字符串类型的消息类型自动推断问题
我想参考DOM的TypeScript类型映射方式,让自定义类型根据字符串值自动继承对应字段集,先定义了消息类型映射:
interface MessagesToContentMap { "error": {message: string}, "success": {result: number} }
尝试通过泛型实现通用消息类型,让它继承对应字段:
interface GenericMessage<KType extends keyof MessagesToContentMap> extends MessagesToContentMap[KType] { type: KType }
核心需求是在if判断中让TypeScript自动推断消息类型,比如:
const msg: GenericMessage<any> = {}; if(msg.type === "error") { // 期望这里能识别msg为{message: string, type: "error"} }
但当前代码只能提示type的可选值,无法自动推断其他字段。另外还试过另一种写法,虽然能工作但不符合实际消息结构,还需要显式类型转换:
interface GenericMessage<KType extends keyof MessagesToContentMap> { type: KType; data: MessagesToContentMap[KType]; }
使用时需要(testMessage as GenericMessage<"error">).data,不够便捷。
解决方案
问题出在你使用了GenericMessage<any>,TypeScript无法基于any做类型窄化推断。正确的做法是创建联合类型,把MessagesToContentMap的每个键对应的消息类型合并成一个明确的联合集合。
步骤1:生成联合类型Message
type Message = { [K in keyof MessagesToContentMap]: { type: K } & MessagesToContentMap[K] }[keyof MessagesToContentMap];
这个类型会自动展开为:
{type: "error", message: string} | {type: "success", result: number}
步骤2:使用联合类型实现自动推断
const msg: Message = { type: "error", message: "something wrong" }; if(msg.type === "error") { // TypeScript自动推断msg为{type: "error", message: string} console.log(msg.message); // 编辑器会正确提示message字段 } else if(msg.type === "success") { console.log(msg.result); // 编辑器会正确提示result字段 }
原理说明
原来的泛型写法无法触发自动推断,是因为GenericMessage<any>是一个宽泛的模糊类型,TypeScript无法从中提取具体的键信息。而联合类型是多个明确类型的集合,TypeScript可以通过type字段的字面量值自动启用类型守卫,完成精准的类型窄化。
如果需要保留泛型的灵活性,可以同时定义泛型和联合类型:
// 保留原泛型定义 interface GenericMessage<KType extends keyof MessagesToContentMap> extends MessagesToContentMap[KType] { type: KType } // 基于泛型生成联合类型 type Message = GenericMessage<keyof MessagesToContentMap>;
这样既可以用泛型单独指定某一种消息类型,也可以用联合类型实现自动推断。
内容的提问来源于stack exchange,提问作者Tomáš Zato
相关产品推荐
相关产品推荐

