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

TypeScript中对Record<string,string>部分键的类型安全约束

问题描述

假设我有一个COUNTRIES常量数组:

const COUNTRIES = [
  "fr", "sp", "de", "uk"
] as const;

type CountryCode = typeof COUNTRIES[number];

另外,我有一个存储应用翻译内容的大型Record<string,string>映射:

const EN_TRANSLATIONS: Record<TranslationKeys, string> = {
  "hello-world": "Hello world !",
  "what-time": "What time is it ?",
  "countries.fr": "France",
  "countries.sp": "Spain",
  "countries.de": "Germany",
  "countries.uk": "United Kingdom"
}

能否定义上述TranslationKeys类型,在编译时强制执行以下规则:

  • 可使用任意字符串作为键,无需在联合类型中白名单所有键(不想维护专门的键联合类型)
  • 存在遵循特定约定的“特殊”键并附带类型约束:示例中countries.*键需包含COUNTRIES的所有条目(匹配CountryCode联合类型),若缺失则编译器报错(新增国家代码时不会忘记添加翻译)
  • 尽量保持EN_TRANSLATIONS的现有写法(仅修改TranslationKeys类型):已知一种方案是拆分变量并使用扩展运算符,但希望避免这种方式。

我尝试过TypeScript 4.9新增的satisfies关键字,但效果不佳。请问有什么可行的解决方法?

解决方案

要实现需求,我们可以通过交叉类型结合模板字面量类型和条件约束来定义TranslationKeys,直接通过TypeScript的类型检查机制强制特殊键的完整性,同时保留原有写法。

具体实现代码

const COUNTRIES = [
  "fr", "sp", "de", "uk"
] as const;

type CountryCode = typeof COUNTRIES[number];

// 生成所有必填的国家翻译键,格式为countries.xx
type RequiredCountryKeys = {
  [K in CountryCode as `countries.${K}`]: string;
};

// 定义TranslationKeys:允许任意字符串键,同时强制包含所有RequiredCountryKeys的键
type TranslationKeys = string & keyof RequiredCountryKeys;

// 测试验证:
// 1. 可以添加任意自定义键(比如下面的"greeting")
// 2. 缺少某个国家键会直接触发编译错误
const EN_TRANSLATIONS: Record<TranslationKeys, string> = {
  "hello-world": "Hello world !",
  "what-time": "What time is it ?",
  "greeting": "Hi there!", // 自定义键,完全允许
  "countries.fr": "France",
  "countries.sp": "Spain",
  "countries.de": "Germany",
  // 注释掉下面这行会报错:对象字面量缺少属性"countries.uk"
  "countries.uk": "United Kingdom"
};

原理说明

  1. RequiredCountryKeys:借助模板字面量类型,自动生成所有countries.${CountryCode}格式的键,并将它们标记为必选的字符串值类型。当COUNTRIES数组新增国家代码时,这个类型会自动同步更新。
  2. TranslationKeys:使用string & keyof RequiredCountryKeys的交叉类型,既保留了“允许任意字符串作为键”的灵活性,又强制要求对象必须包含所有keyof RequiredCountryKeys对应的键——TypeScript在检查Record<TranslationKeys, string>类型时,会同时验证这两个条件。
  3. 避免拆分变量:整个实现只需要修改TranslationKeys类型,完全不需要拆分EN_TRANSLATIONS变量或使用扩展运算符,完美符合你希望保留原有写法的要求。

为什么satisfies效果不好?

satisfies关键字仅做类型兼容性校验,不会强制要求对象包含某个类型的所有必选键。而上面的方案通过将必选约束嵌入到TranslationKeys类型中,直接利用Record的类型检查机制来强制完整性,刚好解决了satisfies的局限性。


内容的提问来源于stack exchange,提问作者Frédéric Camblor

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 11:55:35