《TypeScript实战指南》—2.2.7 索引类型与映射类型

举报
华章计算机 发表于 2019/06/16 12:45:04 2019/06/16
【摘要】 本节书摘来自华章计算机《TypeScript实战指南》一书中的第2章,第2.2.7节,作者是胡桓铭。

2.2.7 索引类型与映射类型

索引类型与映射类型是相对复杂的内容。使用索引类型,编译器就能够检查使用了动态属性名的代码。

例如,一个 Lodash 中常见的 pluck 函数,就是从对象中选取属性的子集。下面是 pluck 函数的简化版示例:

function pluck(obj, names) {

    return names.map(name => obj[name]);

}

如果需要在TypeScript中使用这个函数,要通过索引类型查询和索引访问操作符:

function pluck<T, K extends keyof T>(obj: T, names: K[]): T[K][] {

    return names.map(name => obj[name]);

}

 

interface Person {

    name: string;

    age: number;

}

 

const person: Person = {

    name: 'Jarid',

    age: 35

};

 

pluck(person, ['name']); // ["Jarid"]

pluck(person, ['profession']);

//   [ts]

// Argument of type '"profession "[]' is not assignable to parameter of type '("name" | "age")[]'.

//   Type '"profession "' is not assignable to type '"name" | "age"'.

编译器会检查传入的值是否是Person属性的一部分。让我们解释一下上面代码的意义。首先看泛型,这里有T和K两种类型。根据类型推断,第一个参数obj就是person,类型会被推断为Person;而第二个数组参数的类型推断,我们可以从右往左进行阅读。keyof关键字可以获取T,也就是Person的所有属性名,即['name','age']。最后,extends关键字让泛型K继承了Person的所有属性名,即['name','age']。

依托于keyof关键字完成了类型索引。

我们再来看返回值,返回值的类型是T[K][],阅读起来有点困难。它实际表述的意思是,变量T取属性K的值的数组,其中T[K]就是索引访问操作符。

这样强大的功能保证了代码的动态性和准确性,也让代码提示变得更加丰富了:

function pluck<T, K extends keyof T>(obj: T, names: K[]): T[K][] {

    return names.map(name => obj[name]);

}

 

interface Person {

    name: string;

    age: number;

}

 

const person: Person = {

    name: 'Jarid',

    age: 35

};

 

pluck(person, ['name']); // ["Jarid"]

pluck(person, ['profession']);

//   [ts]

// Argument of type '"profession"[]' is not assignable to parameter of type '("name" | "age")[]'.

//   Type '"profession "' is not assignable to type '"name" | "age"'.

还有一种场景是将一个已知类型的每个属性都变为可选的,比如像这样使用问号:

interface Person {

    name?: string;

    age?: number;

}

在实例化 Person时,我们不必给每个属性都赋值。

想要 Person 的属性值都是只读不可修改的,如下所示:

interface Person {

    readonly name: string;

    readonly age: number;

}

这在JavaScript里经常会用到。

TypeScript提供了从旧类型中创建新类型的一种方式,也就是“映射类型”。在映射类型里,新类型以相同的形式去转换旧类型里的每个属性。例如,你可以令每个属性成为只读类型或可选类型。下面是一些例子:

type Readonly<T> = {

    readonly [P in keyof T]: T[P];

}

type Partial<T> = {

    [P in keyof T]?: T[P];

}

使用方式如下所示:

type PersonPartial = Partial<Person>;

type ReadonlyPerson = Readonly<Person>;

TypeScript 中内置了 Readonly 和 Partial,所以不需要手动声明实现。

内置的类型还有 Required、Pick、Record、Exclude、Extract、NonNullable;读者可以自己尝试这些类型方法,它们的实现都在 typescript/lib/lib.es5.d.ts 中。


【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。