内容简介:阅读须知:本文示例的运行环境是 TypeScript 官网的TypeScript 允许我们遍历某种类型的属性,并通过 keyof 操作符提取其属性的名称。下面我们来看个例子:
阅读须知:本文示例的运行环境是 TypeScript 官网的 Playground ,对应的编译器版本是 v3.8.3 。
一、keyof 简介
TypeScript 允许我们遍历某种类型的属性,并通过 keyof 操作符提取其属性的名称。
keyof
操作符是在 TypeScript 2.1 版本引入的,该操作符可以用于获取某种类型的所有键,其返回类型是联合类型。
下面我们来看个例子:
interface Person {
name: string;
age: number;
location: string;
}
type K1 = keyof Person; // "name" | "age" | "location"
type K2 = keyof Person[]; // number | "length" | "push" | "concat" | ...
type K3 = keyof { [x: string]: Person }; // string | number
除了接口外,keyof 也可以用于操作类,比如:
class Person {
name: string = "Semlinker";
}
let sname: keyof Person;
sname = "name";
若把 sname = "name"
改为 sname = "age"
的话,TypeScript 编译器会提示以下错误信息:
Type '"age"' is not assignable to type '"name"'.
keyof 操作符除了支持接口和类之外,它也支持基本数据类型:
let K1: keyof boolean; // let K1: "valueOf" let K2: keyof number; // let K2: "toString" | "toFixed" | "toExponential" | ... let K3: keyof symbol; // let K1: "valueOf"
此外 keyof
也称为输入索引类型查询,与之相对应的是索引访问类型,也称为查找类型。在语法上,它们看起来像属性或元素访问,但最终会被转换为类型:
type P1 = Person["name"]; // string type P2 = Person["name" | "age"]; // string | number type P3 = string["charAt"]; // (pos: number) => string type P4 = string[]["push"]; // (...items: string[]) => number type P5 = string[][0]; // string
二、keyof 的作用
JavaScript 是一种高度动态的语言。有时在静态类型系统中捕获某些操作的语义可能会很棘手。以一个简单的 prop
函数为例:
function prop(obj, key) {
return obj[key];
}
该函数接收 obj 和 key 两个参数,并返回对应属性的值。对象上的不同属性,可以具有完全不同的类型,我们甚至不知道 obj 对象长什么样。
那么在 TypeScript 中如何定义上面的 prop
函数呢?我们来尝试一下:
function prop(obj: object, key: string) {
return obj[key];
}
在上面代码中,为了避免调用 prop 函数时传入错误的参数类型,我们为 obj 和 key 参数设置了类型,分别为 {}
和 string
类型。然而,事情并没有那么简单。针对上述的代码,TypeScript 编译器会输出以下错误信息:
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'.
元素隐式地拥有 any
类型,因为 string
类型不能被用于索引 {}
类型。要解决这个问题,你可以使用以下非常暴力的方案:
function prop(obj: object, key: string) {
return (obj as any)[key];
}
很明显该方案并不是一个好的方案,我们来回顾一下 prop
函数的作用,该函数用于获取某个对象中指定属性的属性值。因此我们期望用户输入的属性是对象上已存在的属性,那么如何限制属性名的范围呢?这时我们可以利用本文的主角 keyof
操作符:
function prop<T extends object, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
在以上代码中,我们使用了 TypeScript 的泛型和泛型约束。
首先定义了 T 类型并使用 extends
关键字约束该类型必须是 object 类型的子类型,然后使用 keyof
操作符获取 T 类型的所有键,其返回类型是联合类型,最后利用 extends
关键字约束 K 类型必须为 keyof T
联合类型的子类型。
是骡子是马拉出来遛遛就知道了,我们来实际测试一下:
type Todo = {
id: number;
text: string;
done: boolean;
}
const todo: Todo = {
id: 1,
text: "Learn TypeScript keyof",
done: false
}
function prop<T extends object, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
const id = prop(todo, "id"); // const id: number
const text = prop(todo, "text"); // const text: string
const done = prop(todo, "done"); // const done: boolean
很明显使用泛型,重新定义后的 prop<T extends object, K extends keyof T>(obj: T, key: K)
函数,已经可以正确地推导出指定键对应的类型。那么当访问 todo 对象上不存在的属性时,会出现什么情况?比如:
const date = prop(todo, "date");
对于上述代码,TypeScript 编译器会提示以下错误:
Argument of type '"date"' is not assignable to parameter of type '"id" | "text" | "done"'.
这就阻止我们尝试读取不存在的属性。
三、keyof 与对象的数值属性
在使用对象的数值属性时,我们也可以使用 keyof 关键字。 请记住,如果我们定义一个带有数值属性的对象,那么我们既需要定义该属性,又需要使用数组语法访问该属性, 如下所示:
class ClassWithNumericProperty {
[1]: string = "Semlinker";
}
let classWithNumeric = new ClassWithNumericProperty();
console.log(`${classWithNumeric[1]} `);
下面我们来举个示例,介绍一下在含有数值属性的对象中,如何使用 keyof 操作符来安全地访问对象的属性:
enum Currency {
CNY = 6,
EUR = 8,
USD = 10
}
const CurrencyName = {
[Currency.CNY]: "人民币",
[Currency.EUR]: "欧元",
[Currency.USD]: "美元"
};
console.log(`CurrencyName[Currency.CNY] = ${CurrencyName[Currency.CNY]}`);
console.log(`CurrencyName[36] = ${CurrencyName[6]}`);
上面的代码中,首先定义了一个 Currency
枚举用于表示三种货币类型,接着定义一个 CurrencyName
对象,该对象使用数值属性作为键,对应的值是该货币类型的名称。该代码成功运行后,控制台会输出以下结果:
CurrencyName[Currency.CNY] = 人民币 CurrencyName[36] = 人民币
为了方便用户能根据货币类型来获取对应的货币名称,我们来定义一个 getCurrencyName
函数,具体实现如下:
function getCurrencyName<T, K extends keyof T>(key: K, map: T): T[K] {
return map[key];
}
console.log(`name = ${getCurrencyName(Currency.CNY, CurrencyName)}`);
同样,getCurrencyName 函数和前面介绍的 prop 函数一样,使用了泛型和泛型约束,从而来保证属性的安全访问。最后,我们来简单介绍一下 keyof 与 typeof 操作符如何配合使用。
四、keyof 与 typeof 操作符
typeof
操作符用于获取变量的类型。因此这个操作符的后面接的始终是一个变量,且需要运用到类型定义当中。为了方便大家理解,我们来举一个具体的示例:
type Person = {
name: string;
age: number;
}
let man: Person = {
name: "Semlinker",
age: 30
}
type Human = typeof man;
了解完 typeof
和 keyof
操作符的作用,我们来举个例子,介绍一下它们如何结合在一起使用:
const COLORS = {
red: 'red',
blue: 'blue'
}
// 首先通过typeof操作符获取color变量的类型,然后通过keyof操作符获取该类型的所有键,
// 即字符串字面量联合类型 'red' | 'blue'
type Colors = keyof typeof COLORS
let color: Colors;
color = 'red' // Ok
color = 'blue' // Ok
// Type '"yellow"' is not assignable to type '"red" | "blue"'.
color = 'yellow' // Error
最后留到思考题,有兴趣的小伙伴可以想一想:
interface StringIndexArray {
[index: string]: string;
}
interface NumberIndexArray {
[index: number]: string;
}
type K1 = keyof StringIndexArray // type K1 = string | number
type K2 = keyof NumberIndexArray // type K2 = number
五、参考资源
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- JavaScript骚操作之操作符
- Android RxJava 操作符详解系列:条件 / 布尔操作符
- C语言中点操作符(.)和箭头操作符(->)的不同之处
- JS操作符拾遗
- 浅谈JavaScript位操作符
- rxjs switchMap操作符
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Essential ActionScript 3.0
Colin Moock / Adobe Dev Library / June 22, 2007 / $34.64
ActionScript 3.0 is a huge upgrade to Flash's programming language. The enhancements to ActionScript's performance, feature set, ease of use, cleanliness, and sophistication are considerable. Essentia......一起来看看 《Essential ActionScript 3.0》 这本书的介绍吧!
正则表达式在线测试
正则表达式在线测试
HEX CMYK 转换工具
HEX CMYK 互转工具