TypeScript 之 Indexed Access Types

发布于 2022-10-24 13:58:11 字数 3261 浏览 98 评论 4

TypeScript 的官方文档早已更新,但我能找到的中文文档都还停留在比较老的版本。所以对其中新增以及修订较多的一些章节进行了翻译整理。本篇整理自 TypeScript Handbook 中 「Indexed Access Types」 章节。本文并不严格按照原文翻译,对部分内容也做了解释补充。

正文

我们可以使用 索引访问类型(indexed access type) 查找另外一个类型上的特定属性:

type Person = { age: number; name: string; alive: boolean };
type Age = Person["age"];
// type Age = number

因为索引名本身就是一个类型,所以我们也可以使用联合、keyof 或者其他类型:

type I1 = Person["age" | "name"];  
// type I1 = string | number
 
type I2 = Person[keyof Person];
// type I2 = string | number | boolean
 
type AliveOrName = "alive" | "name";
type I3 = Person[AliveOrName];  
// type I3 = string | boolean

如果你尝试查找一个不存在的属性,TypeScript 会报错:

type I1 = Person["alve"];
// Property 'alve' does not exist on type 'Person'.

接下来是另外一个示例,我们使用 number 来获取数组元素的类型。结合 typeof 可以方便的捕获数组字面量的元素类型:

const MyArray = [
  { name: "Alice", age: 15 },
  { name: "Bob", age: 23 },
  { name: "Eve", age: 38 },
];
 
type Person = typeof MyArray[number];
       
// type Person = {
//    name: string;
//    age: number;
// }

type Age = typeof MyArray[number]["age"];  
// type Age = number

// Or
type Age2 = Person["age"];   
// type Age2 = number

作为索引的只能是类型,这意味着你不能使用 const 创建一个变量引用:

const key = "age";
type Age = Person[key];

// Type 'key' cannot be used as an index type.
// 'key' refers to a value, but is being used as a type here. Did you mean 'typeof key'?

然而你可以使用类型别名实现类似的重构:

type key = "age";
type Age = Person[key];

最后讲一个实战案例:​

假设有这样一个业务场景,一个页面要用在不同的 APP 里,比如淘宝、天猫、支付宝,根据所在 APP 的不同,调用的底层 API 会不同,我们可能会这样写:

const APP = ['TaoBao', 'Tmall', 'Alipay'];

function getPhoto(app: string) {
  // ...
}
  
getPhoto('TaoBao'); // ok
getPhoto('whatever'); // ok

如果我们仅仅是对 app 约束为 string 类型,即使传入其他的字符串,也不会导致报错,我们可以使用字面量联合类型约束一下:

const APP = ['TaoBao', 'Tmall', 'Alipay'];
type app = 'TaoBao' | 'Tmall' | 'Alipay';

function getPhoto(app: app) {
  // ...
}
  
getPhoto('TaoBao'); // ok
getPhoto('whatever'); // not ok

但写两遍又有些冗余,我们怎么根据一个数组获取它的所有值的字符串联合类型呢?我们就可以结合上一篇的 typeof 和本节的内容实现:

const APP = ['TaoBao', 'Tmall', 'Alipay'] as const;
type app = typeof APP[number];
// type app = "TaoBao" | "Tmall" | "Alipay"

function getPhoto(app: app) {
  // ...
}
  
getPhoto('TaoBao'); // ok
getPhoto('whatever'); // not ok

我们来一步步解析:

首先是使用 as const 将数组变为 readonly 的元组类型:

const APP = ['TaoBao', 'Tmall', 'Alipay'] as const;
// const APP: readonly ["TaoBao", "Tmall", "Alipay"]

但此时 APP 还是一个值,我们通过 typeof 获取 APP 的类型:

type typeOfAPP = typeof APP;
// type typeOfAPP = readonly ["TaoBao", "Tmall", "Alipay"]

最后在通过索引访问类型,获取字符串联合类型:

type app = typeof APP[number];
// type app = "TaoBao" | "Tmall" | "Alipay"

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

梦里°也失望 2022-05-02 14:36:24

关于最后的实战部分, 想请教两个问题

  1. 在使用typeof 获取APP中的索引字符串字段的时候, 为什么一定要先使用 as const 将其转化为readonly?这个的原理我没太完全理解。尝试后发现, 如果不指定为readyonly, 那么typeof之后的结果只是string
  2. 在这个场景下, 我第一反应是使用枚举类型来做定义, 请问这种情况下,使用枚举类型和使用数组来定义的区别或者优劣?枚举类型不方便迭代算一个吗?
enum App {
  'TaoBao' = 'TaoBao',
  'Tmall' = 'Tmall',
  'Alipay' = 'Alipay'
}
function getPhoto(app: App) {
  // ...
}
getPhoto(App.TaoBao);
getPhoto('sss'); // not ok

1.#229

2.enum 会生产很多冗余代码

夏至、离别 2022-05-02 09:35:31

关于最后的实战部分, 想请教两个问题

  1. 在使用typeof 获取APP中的索引字符串字段的时候, 为什么一定要先使用 as const 将其转化为readonly?这个的原理我没太完全理解。尝试后发现, 如果不指定为readyonly, 那么typeof之后的结果只是string
  2. 在这个场景下, 我第一反应是使用枚举类型来做定义, 请问这种情况下,使用枚举类型和使用数组来定义的区别或者优劣?枚举类型不方便迭代算一个吗?
enum App {
  'TaoBao' = 'TaoBao',
  'Tmall' = 'Tmall',
  'Alipay' = 'Alipay'
}
function getPhoto(app: App) {
  // ...
}
getPhoto(App.TaoBao);
getPhoto('sss'); // not ok
哥,最终变帅啦 2022-05-02 00:03:56

@btea 是的,感谢提醒,原文已经修改

神经暖 2022-05-01 13:00:51

**索引访问类型(indexed access type)** , 这里是不是多了个空格,加粗语法没生效

~没有更多了~

关于作者

0 文章
0 评论
23 人气
更多

推荐作者

遂心如意

文章 0 评论 0

5513090242

文章 0 评论 0

巷雨优美回忆

文章 0 评论 0

junpengz2000

文章 0 评论 0

13郎

文章 0 评论 0

qq_xU4RDg

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文