class DemoClass { // 定义示例属性 name: string; age: number; location: string; } // 使用 var 或 let 定义变量,并使用 keyof 关键字 var variableName: keyof DemoClass; variableName = "name"; // 示例赋值 let anotherVariableName: keyof DemoClass; anotherVariableName = "age"; // 示例赋值在上面的代码片段中,我们创建了一个名为 DemoClass 的类,并定义了三个属性:name、age 和 location。随后,我们使用 var 或 let 定义了两个变量 variableName 和 anotherVariableName,并使用 keyof 关键字调用 DemoClass。当我们为变量赋值时,TypeScript 会确保赋值的值是 DemoClass 的有效属性之一。
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; }上面的函数使用了泛型来定义一个对象属性的类型。keyof T 返回的是字符串字面量类型的联合。字面量指的是赋值给常量变量的固定值。由于 K 是一个字符串字面量类型,我们使用 extends 关键字对 K 进行约束。索引操作符 obj[key] 返回属性所具有的相同类型。
type Staff = { name: string; empCode: number; }; const manager: Staff = { name: 'Brian', empCode: 100, }; const nameType = getProperty(manager, 'name'); // 返回类型为 string const empCodeType = getProperty(manager, 'empCode'); // 返回类型为 number // const invalidType = getProperty(manager, 'sal'); // 编译错误编译器会验证传递的键是否匹配类型 T 的属性名,因为我们对第二个参数应用了类型约束。如果我们尝试传递一个无效的键,比如 sal,编译器会报错。
type keyProp = 'name' | 'empCode'; function getProperty<T, K extends keyProp>(obj: T, key: K): T[K] { return obj[key]; }尽管这种手动方式应用了相同类型的约束,但这种方法的可维护性较差。类型定义会重复,如果原始类型发生变化,手动定义的类型不会自动更新。
type OptionsFlags<T> = { [Property in keyof T]: boolean; }; // 堆代码 duidaima.om // 使用 OptionsFlags type FeatureFlags = { readingMode: () => void; loggedUserProfile: () => void; }; type UpdatedFeatures = OptionsFlags<FeatureFlags>;输出结果:
type UpdatedFeatures = { readingMode: boolean; loggedUserProfile: boolean; }在上面的代码片段中,OptionsFlags 被定义为一个包含类型参数 T 的泛型类型。[Property in keyof T] 定义了对类型 T 的属性名称的迭代,方括号表示索引签名语法。因此,OptionsFlags 会将所有 T 类型的属性值重新映射为 boolean 类型。
type Partial<T> = { [P in keyof T]?: T[P]; };将所有属性设置为只读:
type Readonly<T> = { [P in keyof T]: readonly T[P]; };四、 KeyOf 运算符与显式键
interface User { userName: string; id: number; } function userData(user: User, property: keyof User) { console.log(`Print user information ${property}: "${user[property]}"`); } let user = { userName: "Karl", id: 100 }; userData(user, "userName");输出结果
Print user information userName: "Karl"在上面的代码中,我们定义了一个 User 接口和一个 userData 函数。函数接受一个 User 对象和一个 User 类型的属性键,并打印相应的用户信息。
type stringMapDemo = {[key: string]: unknown}; function sampleStringPair(property: keyof stringMapDemo, value: string): stringMapDemo { return {[property]: value}; }我们定义了一个类型 stringMapDemo,它表示一个对象,其中所有键都是字符串类型,所有值的类型为 unknown。
type OptionsFlags<T> = { [Property in keyof T]: T[Property] extends Function ? T[Property] : boolean }; type DemoFeatures = { readingMode: () => void; loggedUserProfile: () => void; loginPassword: string; userName: string; }; type Features = OptionsFlags<DemoFeatures>;运行后 Features 的类型结构如下
type Features = { readingMode: () => void; loggedUserProfile: () => void; loginPassword: boolean; userName: boolean; }代码解析
我们使用 OptionsFlags 来定义新类型 Features。通过条件映射,Features 类型中的方法保持不变,而字符串属性被映射为 boolean 类型。
type Record<K extends keyof any, T> = { [P in K]: T; };示例
type FeatureFlags = { readingMode: () => void; loggedUserProfile: () => void; loginPassword: string; userName: string; };我们可以使用 Record 实用类型将所有属性映射为 boolean 类型:
type Features = Record<keyof FeatureFlags, boolean>; // 结果类型 type Features = { readingMode: boolean; loggedUserProfile: boolean; loginPassword: boolean; userName: boolean; };Record 实际应用场景
enum Status { OPEN = "OPEN", STARTED = "STARTED", CLOSED = "CLOSED" }定义 Props 接口
interface Props { status: Status; }使用 Record 定义 statusMap
const statusMap: Record<Status, { label: string; color: "bg-red-400" | "bg-blue-400" | "bg-green-400" }> = { OPEN: { label: "Open", color: "bg-red-400" }, STARTED: { label: "Started", color: "bg-blue-400" }, CLOSED: { label: "Closed", color: "bg-green-400" }, };组件调用
const TicketStatusBadge: React.FC<Props> = ({ status }) => { return ( <Badge className={` ${statusMap[status].color} text-background hover:${statusMap[status].color} `}> {statusMap[status].label} </Badge> ); };解析
type Pick<T, K extends keyof T> = { [P in K]: T[P]; };示例
type User = { id: number; name: string; age: number; email: string; };我们可以使用 Pick 类型选择 id 和 name 属性:
type UserPreview = Pick<User, 'id' | 'name'>; // 结果类型 type UserPreview = { id: number; name: string; };在这个例子中,UserPreview 类型只包含 id 和 name 属性。通过使用 TypeScript 的实用类型,如 Record 和 Pick,我们可以轻松地重构和简化类型定义。结合 keyof 运算符,我们可以确保类型的灵活性和安全性。