前言
文中内容都是参考https://www.typescriptlang.org/docs/handbook/2/keyof-types.html 、
https://mariusschulz.com/blog/keyof-and-lookup-types-in-typescript 以及
TypeScript 之 Keyof Type Operator --- mqyqingfeng 内容。
keyof 类型操作符
对一个对象类型应用 keyof
操作符,会返回该对象属性名组成的一个字符串或者数字字面量的联结类型。
如:
type Point = { x: number; y: number };type P = keyof Point;// type P = "x" | "y"
以上代码中, 类型P类型为"x" | "y"
联结类型
如果这个类型有一个 string 或者 number 类型的索引签名,keyof 则会间接返回这些类型:
type Arrayish = { [n: number]: unknown };type A = keyof Arrayish;// type A = numbertype Mapish = { [k: string]: boolean };type M = keyof Mapish;// type M = string | number
以上代码中, 为什么M 是 string | number类型呢?
这时因为JavaScript 对象的属性名会被强制转为一个字符串,所以 obj[0] 和 obj["0"] 是一样的。
接口
对接口应用 keyof
:
interface IPerson { name: string; age: number; height?: string;}type k1 = keyof IPerson;// type k1 = "name" | "age" | "height"type k2 = keyof IPerson[];// type k2 = number | "length" | "toString" | "toLocaleString" | "pop" | "push" | "concat" | "join" | "reverse" | "shift" | "slice" | "sort" | "splice" | "unshift" | "indexOf" | "lastIndexOf" | ...
类
对类应用 keyof
:
class Person { name: string = 'xman'; age: number = 18; height: string = '60kg'; [1]: boolean = true;}let key1: keyof Person;// let key1: "name" | "age" | "height" | 1
尝试批改下:
key1 = 'address';// 不能将类型“"address"”调配给类型“"name" | "age" | "height"”
根本数据类型
let K1: keyof number; // let K1: "valueOf" | "toString" | "toFixed" | "toExponential" | "toPrecision" | "toLocaleString"let K2: keyof string; // let K2: number | "toString" | "charAt" | "charCodeAt" | "concat" | "indexOf" | "lastIndexOf" | "localeCompare" | "match" | "replace" | "search" | "slice" | "split" | ...let K3: keyof boolean; // let K3: "valueOf"let k4: keyof bigint;// let k4: never let K5: keyof symbol; // let K5: "toString" | "valueOf" let k6: keyof undefined;// let k6: neverlet k7: keyof null;// let k7: never
其余
对对象字面量应用keyof
:
const obj = { name: "xman", age: 18,};let k1: keyof typeof obj;// let k1: "name" | "age"
数字字面量联结类型
const obj = { 1: 'one', 2: 'two', 3: 'three'} type k1 = keyof typeof obj;// typeof obj 的后果为 {// 1: string;// 2: string;// 3: string;// }// type k1 = 1 | 2 | 3
反对 symbol 类型的属性名
const sym1 = Symbol();const sym2 = Symbol();const sym3 = Symbol();const symbolToNumberMap = { [sym1]: 1, [sym2]: 2, [sym3]: 3,};type KS = keyof typeof symbolToNumberMap; // type KS = typeof sym1 | typeof sym2 | typeof sym3
keyof作用
例1: prop函数
JavaScript 是一种高度动静的语言。有时在动态类型零碎中捕捉某些操作的语义可能会很辣手。以一个简略的prop 函数为例:
function prop(obj, key) { return obj[key];}
该函数接管 obj 和 key 两个参数,并返回对应属性的值。对象上的不同属性,能够具备齐全不同的类型,咱们甚至不晓得 obj 对象长什么样。
那么在 TypeScript 中如何定义下面的 prop 函数呢?
function prop(obj: {}, key: string) { return obj[key];}
obj设置为{}
, key 设置为 string
, 然而TypeScript 编译器会输入以下错误信息:
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'. No index signature with a parameter of type 'string' was found on type '{}'.
元素隐式地领有 any 类型,因为 string 类型不能被用于索引 {} 类型 .
在prop函数里咱们冀望用户输出的属性是对象上已存在的属性,那么如何限度属性名的范畴呢?
这时能够利用keyof
操作符:
function prop<T, K extends keyof T>(obj: T, key: K) { return obj[key];}
以上代码中,咱们应用了 TypeScript 的泛型和泛型束缚。首先定义了 T
类型,而后应用 keyof
操作符获取 T
类型的所有键,其返回类型是联结类型,最初利用 extends
关键字束缚 K
类型必须为 keyof T
联结类型的子类型。
此时key
的类型为 string | number | symbol
联结类型。
测试下:
interface Todo { id: number; text: string; due: Date;}const todo: Todo = { id: 1, text: "Buy milk", due: new Date(2016, 11, 31),};const id = prop(todo, "id"); // const id: numberconst text = prop(todo, "text"); // const text: stringconst due = prop(todo, "due"); // const due: Dateconst str:string = "hello";// 测试字符串const s = prop(str, 0); // const s: string
如果拜访 todo 对象上不存在的属性时,会呈现什么状况?
const date = prop(todo, "date");// 类型“"date"”的参数不能赋给类型“"id" | "text" | "due"”的参数。
这就阻止咱们尝试读取不存在的属性。
例2: Object.getOwnPropertyDescriptors函数
查看 TypeScript 编译器附带的 lib.es2017.object.d.ts 类型申明文件中 Object.getOwnPropertyDescriptors()
办法:
interface ObjectConstructor { // ... getOwnPropertyDescriptors<T>(o: T): {[P in keyof T]: TypedPropertyDescriptor<T[P]>} & { [x: string]: PropertyDescriptor };}
getOwnPropertyDescriptors
办法用来获取一个对象的所有本身属性的描述符, 如果没有任何本身属性,则返回空对象
let obj = {name: 'xman'};let des = Object.getOwnPropertyDescriptors(obj);// des 值: {// name: {// configurable: true,// enumerable: true,// value: "xman",// writable: true// }// }// des 类型: {// name: TypedPropertyDescriptor<string>;// } & {// [x: string]: PropertyDescriptor;// }
解释: 对泛型T
应用 keyof
操作符,会返回该对象属性名组成的一个字符串或者数字字面量的联结类型, in
操作符用于确定属性是否存在于某个对象上,
其中TypedPropertyDescriptor
、PropertyDescriptor
接口定义为:
interface TypedPropertyDescriptor<T> { enumerable?: boolean; configurable?: boolean; writable?: boolean; value?: T; get?: () => T; set?: (value: T) => void;}interface PropertyDescriptor { configurable?: boolean; enumerable?: boolean; value?: any; writable?: boolean; get?(): any; set?(v: any): void;}
最初, 如有谬误,欢送各位大佬指导!感激!
参考资料
https://www.typescriptlang.org/docs/handbook/2/keyof-types.html
https://mariusschulz.com/blog/keyof-and-lookup-types-in-typescript
TypeScript 之 Keyof Type Operator --- mqyqingfeng