TypeScript无法正确推断字典类型?如何优化类型推断?
TypeScript翻译函数类型推断问题解答
问题代码
export type Locale = "en" | "fr"; export type Namespace = "products" | "home"; const dict = { en: { home: { title: "My Super Title" }, products: { product: "Product" }, }, fr: { home: { title: "Mon Super Titre" }, products: { product: "Produit" }, }, }; export const getTranslation = (namespace: Namespace, locale: Locale) => { return dict[locale][namespace]; }; const t = getTranslation("home", "en"); // TypeScript推断的t类型 // const t: { // title: string; // } | { // product: string; // } | { // title: string; // } | { // product: string; // }
核心问题
- 为什么TypeScript无法正确推断该类型?
- 能否通过给
getTranslation添加手动类型标注,让它正确推断出具体类型?
期望推断类型:
{ title: string; }
解答
1. 类型推断错误的原因
TypeScript默认会将getTranslation的返回值推断为所有dict[Locale][Namespace]组合的联合类型,因为函数的两个参数namespace和locale是独立的联合类型——TS无法自动建立“传入特定locale+namespace时,返回对应具体子对象”的关联关系,只能把所有可能的取值结果合并,最终形成重复的联合类型。
2. 手动类型标注的解决方案
利用TypeScript 5+的泛型特性,通过泛型参数绑定传入的locale和namespace的具体字面量类型,让TS精准关联到对应返回值的类型。
方案1:保留字面量类型的精准推断
export type Locale = "en" | "fr"; export type Namespace = "products" | "home"; // 用as const锁定对象的字面量类型,避免TS自动拓宽为string const dict = { en: { home: { title: "My Super Title" }, products: { product: "Product" }, }, fr: { home: { title: "Mon Super Titre" }, products: { product: "Produit" }, }, } as const; // 泛型L和N分别约束为Locale和Namespace的子集,绑定参数的具体类型 export const getTranslation = <L extends Locale, N extends Namespace>(namespace: N, locale: L) => { return dict[locale][namespace]; }; const t = getTranslation("home", "en"); // 推断类型:{ readonly title: "My Super Title" }
方案2:统一返回宽松的string类型
如果不需要保留字面量,只需要属性值为string的类型,可以手动定义翻译类型并标注返回值:
export type Locale = "en" | "fr"; export type Namespace = "products" | "home"; const dict = { en: { home: { title: "My Super Title" }, products: { product: "Product" }, }, fr: { home: { title: "Mon Super Titre" }, products: { product: "Produit" }, }, }; // 定义通用翻译类型,提取对应locale+namespace的结构并将值统一为string type Translation<L extends Locale, N extends Namespace> = { [K in keyof typeof dict[L][N]]: string; }; export const getTranslation = <L extends Locale, N extends Namespace>( namespace: N, locale: L ): Translation<L, N> => { return dict[locale][namespace] as Translation<L, N>; }; const t = getTranslation("home", "en"); // 推断类型:{ title: string }
内容的提问来源于stack exchange,提问作者Mike Whittom
相关产品推荐
相关产品推荐

