如何生成对象所有属性及子属性的合法TypeScript路径类型?
React TypeScript 中 i18next 翻译路径的类型安全实现问题
我在React TypeScript项目中使用i18next管理文本翻译,为确保传入t函数的字符串路径真实存在,尝试创建包含所有可能路径的类型及包装函数。相关代码如下:
const enTranslation = { a: { a1: "a1", a2: "a2", }, b: { b1: "b1", b2: "b2", }, c: "c", }; type Join<K extends string, P extends string> = P extends "" ? `${K}` : `${K}.${P}`; type Paths<T> = T extends object ? Join<keyof T & string, Paths<T[keyof T]>> : "" type TranslationPath = Paths<typeof enTranslation>;
但当前类型未达预期,因为Paths<T>中的两处keyof T并非同一引用,导致出现无效路径(比如"a.b1"这种不存在的路径也能通过类型检查):
const test: TranslationPath = "a.b1"; // 错误路径却能通过类型校验
针对这个问题,我有三个疑问:
1. 如何让两处keyof T指向同一引用,TypeScript是否支持“类型变量”机制?
TypeScript支持通过分布式条件类型结合泛型参数绑定来实现同一key的关联。你当前的问题核心是两处keyof T被独立解析,导致不同层级的key被错误组合。要解决这个,需要用泛型参数绑定单个key,让每一层的key和对应的子类型一一对应,避免跨层级key的混合。
2. 是否有其他方式编写Paths<T>类型定义?
当然有,正确的Paths类型需要遍历每个key并递归处理对应的子类型,确保路径是合法的层级组合。修改后的类型定义如下:
type Join<K extends string, P extends string> = P extends "" ? `${K}` : `${K}.${P}`; type Paths<T> = T extends object ? { [K in keyof T & string]: Join<K, Paths<T[K]>> }[keyof T & string] : ""; type TranslationPath = Paths<typeof enTranslation>; // 此时 TranslationPath 的类型为:"a.a1" | "a.a2" | "b.b1" | "b.b2" | "c"
解释:这里通过映射类型遍历每个key K,对每个K对应的子类型T[K]递归生成路径,最后通过索引访问取出所有可能的路径联合类型。这样每个路径都是从根到叶子的合法层级,不会出现a.b1这类无效路径。
3. 是否有其他方法实现确保t函数路径合法的目标?
有几种实用的替代方案:
- 利用i18next官方类型支持:i18next本身提供了类型绑定能力,你可以通过声明模块的方式将翻译类型绑定到i18next的
t函数上,无需手动编写路径类型。 - 包装
t函数添加类型校验:写一个包装useTranslation的自定义hook,让返回的t函数只接受合法的TranslationPath类型参数:import { useTranslation } from 'react-i18next'; const useTypedTranslation = () => { const { t } = useTranslation(); return { t: (key: TranslationPath) => t(key) }; }; // 使用示例 const { t } = useTypedTranslation(); t("a.a1"); // 合法,编译通过 t("a.b1"); // 无效路径,编译报错 - 代码生成工具:使用
i18next-scanner这类工具扫描你的翻译文件,自动生成对应的类型定义文件,避免手动维护类型的繁琐,同时保证类型和翻译内容的一致性。
内容的提问来源于stack exchange,提问作者Space Fish
相关产品推荐
相关产品推荐

