Angular 中如何修复使用 string 索引翻译对象的类型错误?
可以用已有的 MessageKey 限制 get 的参数,前提是两种语言的翻译对象都包含这些键。如果 get 需要接收任意字符串,应为翻译字典声明字符串索引类型,并处理查找不到结果的情况。
key as keyof typeof MESSAGES 用错了层级:keyof typeof MESSAGES 表示语言键 'en' | 'it',不是翻译条目的键。
为什么 key: string 会报错
这段访问包含两层索引:
MESSAGES[this.lang][key]
第一层的 this.lang 用来选择语言,应该具有 Lang 类型;第二层的 key 用来选择该语言中的翻译条目。
如果 EN 和 IT 是具有固定属性的对象,TypeScript 只知道它们声明过的键。string 可以是任意字符串,无法确认它是否为对象中的键,因此不能直接用于索引。
原代码中的 && 可以在运行时检查对象是否存在,但不能证明 key 是翻译对象允许的属性名。仅凭现有代码,也无法确定是哪项编译配置变化触发了报错。修复时需要让索引类型与对象类型匹配。
方案一:调用方只能传入已定义的翻译键
如果英语字典定义了全部合法键,意大利语字典也包含这些键,可以将 get 的参数改为 MessageKey:
get(key: MessageKey, params?: any): string {
let text = (MESSAGES[this.lang] && MESSAGES[this.lang][key]) ||
(MESSAGES['en'] && MESSAGES['en'][key]) ||
key;
if (params) {
Object.keys(params).forEach(p => {
text = text.replace(`{{${p}}}`, params[p]);
});
}
return text;
}
还要将服务中 this.lang 的类型声明为 Lang。例如,以英语为初始值:
private lang: Lang = 'en';
要在定义字典时检查两种语言是否都包含英语字典中的全部键,可以调整 index.ts:
import { EN } from './en';
import { IT } from './it';
export type Lang = 'en' | 'it';
export type MessageKey = keyof typeof EN;
export const MESSAGES = {
en: EN,
it: IT
} satisfies Record<Lang, Record<MessageKey, string>>;
这里假设翻译值都是字符串。satisfies 会检查每种语言是否包含全部 MessageKey、对应值是否为字符串,同时保留对象自身推断出的类型。
如果 IT 缺少某些英语键,检查就会报错。此时可以选择补齐翻译,或允许语言字典不完整。原始材料没有提供 EN、IT 的具体内容,因此无法确认两者的键是否完全一致。
方案二:允许任意字符串,并保留缺失翻译回退
如果 get(key: string) 是有意设计的接口,例如键来自运行时数据,或不同语言允许缺少条目,可以增加一个用于查找的字典类型:
type TranslationDictionary = Readonly<
Record<string, string | undefined>
>;
const dictionaries: Record<Lang, TranslationDictionary> = MESSAGES;
这里同样假设现有翻译条目的值都是字符串。该类型允许按任意字符串查找,并用 undefined 表示可能不存在的结果。
然后将 get 中声明 text 的部分替换为:
let text = dictionaries[this.lang][key] ||
dictionaries.en[key] ||
key;
get 的参数继续使用 key: string,后面的占位符替换和返回逻辑保持原样。这里也要求 this.lang 的类型为 Lang;如果语言值来自外部输入,应先验证它是否为 'en' 或 'it'。
这段代码保留了原来的 || 行为:空字符串也会触发回退。如果空字符串是有效翻译,希望只在结果为 null 或 undefined 时回退,可以改用:
let text = dictionaries[this.lang][key] ??
dictionaries.en[key] ??
key;
这会改变回退行为,应根据翻译数据的约定选择。
为什么不直接断言 key
keyof typeof MESSAGES 得到的是外层语言键。已有的 MessageKey = keyof typeof EN 才对应英语字典的翻译键。
写成 key as MessageKey 只会让编译器相信这个字符串合法,不会检查运行时值,也不能保证 IT 包含同一个键。调用方可以使用固定键时,可直接声明 get(key: MessageKey);需要传入任意字符串时,允许缺失值的字典类型更符合现有回退逻辑。
备注:内容仅供参考。
