前言
文中内容都是参考 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 = number
type 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: never
let 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: number
const text = prop(todo, "text");
// const text: string
const due = prop(todo, "due");
// const due: Date
const 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