关于typescript:TypeScript基础之keyof-类型操作符

8次阅读

共计 4312 个字符,预计需要花费 11 分钟才能阅读完成。

前言

文中内容都是参考 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操作符用于确定属性是否存在于某个对象上,
其中TypedPropertyDescriptorPropertyDescriptor 接口定义为:

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

正文完
 0