keyof T 是 TS 内置操作符,提取对象类型的所有键的联合类型。ValueOf<T> 不是内置的,但可以用一行工具类型实现:提取对象所有值的联合类型。两者配合使用可以构建类型安全的字典、枚举替代和配置映射。
// keyof 与 ValueOf 的用法与对比
// 1. keyof T -- 提取对象所有键的联合类型
interface User {
id: number;
name: string;
email: string;
}
type UserKey = keyof User; // "id" | "name" | "email"
// 2. ValueOf<T> -- 提取对象所有值的联合类型(自定义工具类型)
type ValueOf<T> = T[keyof T];
type UserValue = ValueOf<User>; // string | number
// 3. keyof 与 typeof 配合:从值推导类型
const config = {
apiUrl: 'https://api.example.com',
timeout: 5000,
retry: 3,
} as const;
type ConfigKey = keyof typeof config; // "apiUrl" | "timeout" | "retry"
type ConfigValue = typeof config[keyof typeof config]; // "https://api.example.com" | 5000 | 3
// 4. 实战:字典类型定义
const STATUS_MAP = {
active: '激活',
inactive: '未激活',
banned: '封禁',
} as const;
type Status = keyof typeof STATUS_MAP; // "active" | "inactive" | "banned"
type StatusLabel = typeof STATUS_MAP[Status]; // "激活" | "未激活" | "封禁"
// 5. 实战:枚举替代(更灵活)
const ROLES = {
ADMIN: 1,
USER: 2,
GUEST: 3,
} as const;
type RoleName = keyof typeof ROLES; // "ADMIN" | "USER" | "GUEST"
type RoleValue = typeof ROLES[RoleName]; // 1 | 2 | 3
// 6. 实战:配置映射类型安全
interface AppConfig {
theme: 'light' | 'dark';
lang: 'zh' | 'en';
debug: boolean;
}
function getConfig<K extends keyof AppConfig>(key: K): AppConfig[K] {
const config: AppConfig = { theme: 'light', lang: 'zh', debug: false };
return config[key];
}
const theme = getConfig('theme'); // "light" | "dark"
const debug = getConfig('debug'); // boolean
keyof T 是 TS 内置的关键字操作符,不需要额外定义。它返回对象类型所有键的字符串字面量联合类型。type ValueOf<T> = T[keyof T] 本质是索引访问 + keyof 的组合,利用索引访问获取所有值的联合类型。as const 才能保留字面量类型,否则 keyof 只会得到 string 而非具体字符串字面量。K extends keyof T 是最常见的泛型约束模式,确保传入的 key 是对象的合法属性名。as const 对象 + keyof 比 enum 更灵活,可以绑定中文 label、支持更多值类型,且不产生额外的运行时代码。// 实战:通过 ValueOf 构建类型安全的颜色映射
const COLORS = {
primary: '#1890ff',
success: '#52c41a',
warning: '#faad14',
error: '#f5222d',
} as const;
type ColorName = keyof typeof COLORS; // "primary" | "success" | "warning" | "error"
type ColorHex = typeof COLORS[ColorName]; // "#1890ff" | "#52c41a" | "#faad14" | "#f5222d"
function useColor(name: ColorName): ColorHex {
return COLORS[name];
}
const btnColor = useColor('primary'); // "#1890ff"
// useColor('unknown'); // 编译错误!
// 实战:类型安全的对象映射
function mapValues<T extends Record<string, unknown>>(
obj: T
): ValueOf<T>[] {
return Object.values(obj) as ValueOf<T>[];
}
const values = mapValues(COLORS); // ColorHex[]