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

泛型联合类型中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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 23:36:04