翻译键的类型标注
从消息文件中推导有效键的联合类型。
翻译键的类型标注 是 CoddyKit 上的免费 TypeScript Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 TypeScript Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 TypeScript Academy 课程共包含 4 节课。
推导有效的翻译键
在类型安全的国际化配置中,有效翻译键的集合源自您的消息对象。像 t("greetng") 这样的拼写错误会变成编译错误,而不是运行时才发现的缺少字符串错误。
消息对象
从一个普通的消息对象开始。它是值和键类型的唯一事实来源。
const messages = {
greeting: "Hello",
farewell: "Goodbye",
cart: {
empty: "Your cart is empty",
checkout: "Proceed to checkout",
},
} as const;使用 keyof 获取顶层键
对于扁平对象,keyof typeof 会得到键的联合类型。
type TopKeys = keyof typeof messages;
// "greeting" | "farewell" | "cart"嵌套问题
实际的消息目录是嵌套的。我们需要像 "cart.empty" 这样的点分隔路径,而不仅仅是顶层键。因此需要一种递归类型来遍历对象。
递归路径类型
条件类型与映射类型通过递归进入嵌套对象并为键添加前缀,构建出点分隔路径。前缀使用模板字面量类型,在实际代码中用反引号写作 [backtick]${K}.${Paths<T[K]>}[backtick]。为使代码列表更加简洁,下面用 JoinPath(K, child) 表示它。
type JoinPath<K extends string, Rest extends string> = K + "." + Rest;
// (Real TS uses a template literal type:
// the K dot Rest pattern delimited by backticks.)
type Paths<T> = {
[K in keyof T & string]: T[K] extends string
? K
: K | (T[K] extends object
? JoinPath<K, Paths<T[K]>>
: never);
}[keyof T & string];应用路径类型
将消息类型传入 Paths 后,就会得到所有有效的点分隔键。
type MsgKey = Paths<typeof messages>;
// "greeting" | "farewell" | "cart" | "cart.empty" | "cart.checkout"仅包含叶节点的键
通常您只需要叶节点路径(实际字符串),而要排除中间对象。请调整递归逻辑,跳过非字符串节点。K-点-子项前缀同样使用模板字面量类型(实际代码中以反引号分隔),这里用 JoinPath 表示。
type LeafPaths<T> = T extends string
? ""
: {
[K in keyof T & string]: T[K] extends string
? K
: JoinPath<K, LeafPaths<T[K]>>;
}[keyof T & string];
// "greeting" | "farewell" | "cart.empty" | "cart.checkout"带类型的 t 函数
将翻译函数的参数限制为推导出的键联合类型,这样只有有效键才能通过编译。
declare function t(key: LeafPaths<typeof messages>): string;
t("cart.empty"); // ok
t("cart.missing"); // Error: not a valid key解析值类型
您还可以进一步推断路径处的值类型,这在消息包含非字符串条目时很有用。用于路径索引的条件类型会沿点号逐层遍历。它使用模板字面量模式拆分路径;在实际代码中,该模式用反引号写成由头部、点号、剩余部分和 infer 组成的形式。下面用 SplitHead/SplitRest 表示这种拆分。
// Conceptually: split "cart.empty" into Head="cart", Rest="empty"
// using a template literal pattern with infer.
type ValueAt<T, P extends string> =
P extends SplitHead<infer Head, infer Rest>
? Head extends keyof T ? ValueAt<T[Head], Rest> : never
: P extends keyof T ? T[P] : never;为什么 as const 很重要
如果没有 as const,字符串值会拓宽为 string,从而丢失之后所需的字面量信息(例如提取占位符时所需的信息)。请始终将目录声明为常量。
为什么这很重要
从目录推导键意味着,添加、重命名或删除消息后,允许使用的键类型会在所有位置立即更新。不需要维护集中式枚举,也不会留下过时的字符串常量。
快速检查
请确认您对翻译键类型化的理解。
回顾
您使用 keyof 和递归模板字面量路径类型,从 messages 对象推导出了有效键的联合类型。仅叶节点的变体会排除中间对象,带类型的 t 会拒绝无效键,而 as const 会保留类型机制所需的字面量信息。
常见问题解答
「翻译键的类型标注」课时是免费的吗?
是的 — 「翻译键的类型标注」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 TypeScript Academy 课程的其余内容,请升级到 CoddyKit PRO。 TypeScript Academy 课程共包含 4 节课。
「翻译键的类型标注」这节课中我会学到什么?
从消息文件中推导有效键的联合类型。 你通过在浏览器中直接运行的动手代码来练习 TypeScript Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 TypeScript Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 TypeScript Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「翻译键的类型标注」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 TypeScript Academy 课中编写并运行代码吗?
能。每节 TypeScript Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 翻译键的类型标注
- 插值的类型安全
- 使用类型处理复数形式
- 区域设置感知的类型推断