TypeScript Academy · 课时

组合 typeof 与 keyof

串联类型运算符,推导出精确的类型。

第 4 / 4 课13 个步骤

组合 typeof 与 keyof 是 CoddyKit 上的免费 TypeScript Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 TypeScript Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 TypeScript Academy 课程共包含 4 节课。

强力组合

单独使用时,typeof 可以从值推导类型,keyof 可以列出类型的键。二者结合为 keyof typeof value 后,就能得到值的实际键的联合类型,从而实现完全类型安全的访问。

类型键查询与类型查询的实际应用

先从一个 const 对象开始,使用 typeof 获取其类型,再使用 keyof 列出其键。结果是该对象字面量键名的联合类型。

const colors = { red: "#f00", green: "#0f0" }
type ColorKey = keyof typeof colors // "red" | "green"
const k: ColorKey = "red"
console.log(colors[k])

为什么需要这种模式

您经常会拥有一个运行时对象,却没有单独的接口。keyof typeof 可以直接提供键类型,无需将对象的结构重复定义为手写类型。

const status = { active: 1, inactive: 0, pending: 2 }
type Status = keyof typeof status // "active" | "inactive" | "pending"
const s: Status = "pending"
console.log(status[s]) // 2

按键进行类型安全的访问

将函数参数声明为 keyof typeof obj 类型后,编译时就会拒绝无效键,因此内部的索引访问一定是安全的。

const rates = { usd: 1, eur: 1.1, gbp: 1.3 }
function rate(currency: keyof typeof rates): number {
  return rates[currency]
}
console.log(rate("eur")) // 1.1
// rate("jpy") // Error: not a key

类似枚举的常量对象

一种常见惯用写法是:使用 as const 定义一个伪枚举对象,然后从中同时推导出键类型和值类型。

const Direction = { Up: "UP", Down: "DOWN" } as const
type DirKey = keyof typeof Direction // "Up" | "Down"
type DirValue = typeof Direction[keyof typeof Direction] // "UP" | "DOWN"
console.log(Direction.Up)

键与值

请注意二者的区别:keyof typeof obj 提供键名,而 typeof obj[keyof typeof obj] 提供值类型。请根据接口的需要选择合适的一种。

const sizes = { sm: 8, md: 16, lg: 24 } as const
type SizeName = keyof typeof sizes // "sm" | "md" | "lg"
type SizeValue = typeof sizes[keyof typeof sizes] // 8 | 16 | 24
const v: SizeValue = 16
console.log(v)

泛型 getProperty

经典的类型安全获取器使用 K extends keyof T 约束键参数,并返回 T[K],因此结果类型与查找到的属性类型完全一致。

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key]
}
const user = { id: 1, name: "Ada" }
const name = getProperty(user, "name") // string
console.log(name)

返回类型随键变化

由于返回值是 T[K],每次调用的结果类型都会根据传入的具体键进行调整:某个键返回数字,另一个键返回字符串。

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key]
}
const u = { id: 1, name: "Ada" }
const id = getProperty(u, "id")     // number
const nm = getProperty(u, "name")   // string
console.log(id, nm)

无效键会被拒绝

传入对象中不存在的键会产生编译错误。泛型约束会在代码运行前强制执行这一规则。

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key]
}
const u = { id: 1 }
// getProperty(u, "missing") // Error: not a key of u
console.log(getProperty(u, "id"))

类型安全的设置器

使用同样的机制可以构建设置器:值参数的类型是 T[K],因此只能为该键赋予正确类型的值。

function setProperty<T, K extends keyof T>(obj: T, key: K, value: T[K]): void {
  obj[key] = value
}
const u = { id: 1, name: "Ada" }
setProperty(u, "name", "Leo")
console.log(u.name)

综合运用

keyof typeof 模式与泛型 T[K] 访问相结合,可以为真实对象提供安全、可复用的工具,这也是类型化配置读取器、查找工具和表单辅助工具的基础。

const theme = { primary: "#007", danger: "#f00" } as const
function color(name: keyof typeof theme): string {
  return theme[name]
}
console.log(color("danger")) // #f00

快速检查

请检验您对结合类型查询与类型键查询的理解。

回顾

keyof typeof value 可以推导出值的实际键的联合类型,无需编写单独的接口,从而使索引访问具备类型安全性。请区分键类型(keyof typeof obj)和值类型(typeof obj[keyof typeof obj])。泛型模式 getProperty<T, K extends keyof T>(obj, key): T[K] 会返回查找到的确切类型,并在编译时拒绝无效键。

免费开始

用 AI 导师学习 TypeScript — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
101
课程
352

常见问题解答

「组合 typeof 与 keyof」课时是免费的吗?

是的 — 「组合 typeof 与 keyof」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 TypeScript Academy 课程的其余内容,请升级到 CoddyKit PRO。 TypeScript Academy 课程共包含 4 节课。

「组合 typeof 与 keyof」这节课中我会学到什么?

串联类型运算符,推导出精确的类型。 你通过在浏览器中直接运行的动手代码来练习 TypeScript Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 TypeScript Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 TypeScript Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「组合 typeof 与 keyof」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 TypeScript Academy 课中编写并运行代码吗?

能。每节 TypeScript Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. typeof 类型运算符
  2. keyof 类型运算符
  3. 索引访问类型
  4. 组合 typeof 与 keyof
← 返回 TypeScript Academy