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

TypeScript递归泛型类型异常原因及正常实现问询

泛型递归类型的类型错误原因与修复方案

问题背景

非泛型版本的递归类型可以正常进行类型提示:

type StringAndNumberForNestedSemanticTokenValue =
  | string
  | number
  | ({
      DEFAULT?: string | number
    } & { [key: string]: StringAndNumberForNestedSemanticTokenValue })

export type SemanticSpacingDefinition =
  Record<string, StringAndNumberForNestedSemanticTokenValue>

const test: SemanticSpacingDefinition = {
  a: {
    DEFAULT: 'test',
    b: {
      c: {
        DEFAULT: 'test',
        d: 'ddd',
      },
    },
  },
}

但改为泛型版本后,出现类型错误:Object literal may only specify known properties, and 'c' does not exist in type '{ DEFAULT?: string | undefined; }'

type NestedSemanticTokenValue<T> =
  | T
  | ({
      DEFAULT?: T
    } & { [key: string]: NestedSemanticTokenValue<T> })

export type SemanticSpacingDefinition = Record<string,
  NestedSemanticTokenValue<string>
>

const test: SemanticSpacingDefinition = {
  a: {
    DEFAULT: 'test',
    b: {
      // type error 
      c: { 
        DEFAULT: 'test',
        d: 'ddd',
      },
    },
  },
}

错误原因

TypeScript对对象字面量有多余属性检查机制,在泛型版本中,类型推断时会优先匹配联合类型里的{ DEFAULT?: T }分支,而非带索引签名的交叉分支。这个分支没有定义允许额外属性的规则,所以当你给b添加c这个额外属性时,就触发了类型错误。

而非泛型版本中,TypeScript能更清晰地识别到联合类型里包含带索引签名的结构,允许添加任意字符串键,所以不会报错。

修复方案

方案一:合并属性与索引签名

直接将DEFAULT属性合并到带有索引签名的对象中,让TypeScript明确这个分支允许任意字符串键:

type NestedSemanticTokenValue<T> =
  | T
  | {
      DEFAULT?: T;
      [key: string]: NestedSemanticTokenValue<T> | undefined;
    }

export type SemanticSpacingDefinition = Record<string, NestedSemanticTokenValue<string>>

const test: SemanticSpacingDefinition = {
  a: {
    DEFAULT: 'test',
    b: {
      c: {
        DEFAULT: 'test',
        d: 'ddd',
      },
    },
  },
}

方案二:用Record替代手动索引签名

如果需要保留交叉类型的写法,可以用Record<string, ...>替代手动编写的索引签名,让TypeScript正确识别允许任意字符串键:

type NestedSemanticTokenValue<T> =
  | T
  | ({
      DEFAULT?: T;
    } & Record<string, NestedSemanticTokenValue<T>>)

export type SemanticSpacingDefinition = Record<string, NestedSemanticTokenValue<string>>

const test: SemanticSpacingDefinition = {
  a: {
    DEFAULT: 'test',
    b: {
      c: {
        DEFAULT: 'test',
        d: 'ddd',
      },
    },
  },
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 12:28:19