0Pricing
TypeScript Academy · 课时

翻译键的类型标注

从消息文件中推导有效键的联合类型。

翻译键的类型标注 是 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 反馈 — 无需本地设置。

此课程中的所有课时

  1. 翻译键的类型标注
  2. 插值的类型安全
  3. 使用类型处理复数形式
  4. 区域设置感知的类型推断
← 返回 TypeScript Academy