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

TypeScript中能否将对象属性用作自身值的字符串字面量类型?

问题:如何在不传递完整泛型的情况下约束对象键与值的匹配?

需求与期望效果

需要创建一个对象,要求每个字符串字面量键必须和对应值对象中的value字段值完全一致。期望示例如下:

const parts: PartMap = {
  foo: { value: 'foo' }  // 合法
  bar: { value: 'cats' } // 不合法,键bar与value的cats不匹配
}

尝试代码与问题

参考相关方案后尝试了以下代码,但PartMap未能将字符串字面量传递给Part类型,导致报错:

type StringLiteral<T> = T extends string ? string extends T ? never : T : never;

interface Part<T extends string> {
  value: StringLiteral<T>
}

interface PartMap {
  [index: string]: Part<typeof index>
}

const x: PartMap = {
  // ⚠️ Type 'string' is not assignable to type 'never'.
  foo: { value: 'foo' }
}

解决方案

直接使用接口的索引签名无法捕获每个键的字面量类型,需要借助泛型映射类型+辅助推导函数实现,无需手动传递完整泛型参数:

简洁实现版本

// 定义泛型映射类型,每个键K对应value为K的对象
type PartMap<T extends string> = {
  [K in T]: { value: K };
};

// 辅助函数,让TypeScript自动推导泛型参数
const createPartMap = <T extends string>(map: PartMap<T>) => map;

// 合法示例:键与value匹配,无报错
const validParts = createPartMap({
  foo: { value: 'foo' },
  test: { value: 'test' }
});

// 不合法示例:Type '"cats"' is not assignable to type '"bar"',自动报错
const invalidParts = createPartMap({
  bar: { value: 'cats' }
});

保留原Part接口的版本

如果需要保留单独的Part接口定义,可以调整为:

type StringLiteral<T> = T extends string ? string extends T ? never : T : never;

interface Part<T extends string> {
  value: StringLiteral<T>;
}

// 泛型PartMap,每个键K对应Part<K>
type PartMap<T extends string> = { [K in T]: Part<K> };

// 辅助推导函数
const createPartMap = <T extends string>(map: PartMap<T>) => map;

// 合法使用
const valid = createPartMap({ foo: { value: 'foo' } });
// 非法使用会触发类型错误
const invalid = createPartMap({ bar: { value: 'cats' } });

原理说明

  • 泛型映射类型{ [K in T]: ... }会遍历所有推导出来的键字面量K,将每个键的类型与对应value的类型绑定。
  • 辅助函数createPartMap让TypeScript自动推导泛型参数T,无需手动指定,从而实现“不用传递完整泛型”的要求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 22:31:13