泛型联合类型中streamId无法赋值的问题排查与解决
TypeScript 联合类型键访问错误分析与修复
问题重现
以下是最小复现代码:
type VideoContentType = 'feature' | 'advertisement' | 'live'; type BaseVideoAttributes<T extends VideoContentType> = { contentType: T; title?: string; }; type LiveVideoAttributes = BaseVideoAttributes<'live'> & { duration?: number; }; type NonLiveVideoAttributes<T extends Exclude<VideoContentType, 'live'>> = BaseVideoAttributes<T> & { duration: number; }; type DurationAttributes<T extends VideoContentType> = T extends Exclude<VideoContentType, 'live'> ? NonLiveVideoAttributes<T> : LiveVideoAttributes; type VideoAttributesWithStreamId<T extends VideoContentType> = { streamId: string; } & DurationAttributes<T>; type VideoAttributesWithSrc<T extends VideoContentType> = { src: string; } & DurationAttributes<T>; type VideoAttributes<T extends VideoContentType = VideoContentType> = VideoAttributesWithStreamId<T> | VideoAttributesWithSrc<T>; function updateVideoAttribute<K extends keyof VideoAttributes>(attrKey: K, attrValue: VideoAttributes[K]) { console.log(`Changing ${attrKey} to ${attrValue}`); // ... } updateVideoAttribute('streamId', 'foo');
调用updateVideoAttribute('streamId', 'foo')时,触发如下类型错误:
Argument of type '"streamId"' is not assignable to parameter of type '"contentType" | "title" | "duration"'.(2345)
错误原因
核心问题在于联合类型的keyof操作仅返回所有分支的公共键:
VideoAttributes是VideoAttributesWithStreamId<T>和VideoAttributesWithSrc<T>的联合类型,前者独有streamId字段,后者独有src字段,两者的公共键只有contentType、title、duration。- 函数
updateVideoAttribute的泛型约束K extends keyof VideoAttributes,限制K只能取这三个公共键,因此传入streamId会触发类型不匹配错误。
修复方案
我们需要让函数支持联合类型的所有分支键,而非仅公共键。以下是两种可行方案:
方案1:泛型绑定到具体的VideoContentType分支
修改函数的泛型定义,让K基于具体T对应的VideoAttributes<T>的键,利用TypeScript的自动推断能力:
function updateVideoAttribute<T extends VideoContentType, K extends keyof VideoAttributes<T>>( attrKey: K, attrValue: VideoAttributes<T>[K] ) { console.log(`Changing ${attrKey} to ${attrValue}`); } // 调用示例(无需手动指定T,TypeScript会自动推断) updateVideoAttribute('streamId', 'foo'); updateVideoAttribute('src', 'bar.mp4'); updateVideoAttribute('duration', 120);
方案2:显式声明所有可能的键与对应值类型
如果不想引入额外泛型T,可以直接枚举所有允许的键,并通过条件类型映射对应的值类型:
type AllVideoAttributeKeys = keyof VideoAttributesWithStreamId<VideoContentType> | keyof VideoAttributesWithSrc<VideoContentType>; function updateVideoAttribute<K extends AllVideoAttributeKeys>( attrKey: K, attrValue: K extends keyof VideoAttributesWithStreamId<VideoContentType> ? VideoAttributesWithStreamId<VideoContentType>[K] : VideoAttributesWithSrc<VideoContentType>[K] ) { console.log(`Changing ${attrKey} to ${attrValue}`); } updateVideoAttribute('streamId', 'foo');
两种方案都能解决问题,方案1更简洁且支持自动类型推断,推荐优先使用。
内容的提问来源于stack exchange,提问作者slhck
相关产品推荐
相关产品推荐

