早读《Const Assertions in Literal Expressions in TypeScript》

  • 2019 年 12 月 24 日
  • 筆記

https://mariusschulz.com/blog/const-assertions-in-literal-expressions-in-typescript

这篇文章讲述了 TypeScript 3.4 提供的一个功能 const 断言,这个功能解决了我以前遇到过的一个问题(axios 定义的 method 就是 "GET" | "POST",不知道现在他们改了么)。

这个类型和对象属性字符串类型是对不上的,比如你直接传递 "HTTPMethod.GET",不过常规方法还是要使用枚举来处理这些问题,由于提取精髓,因此略有删减。

TypeScript 对于类型推断有它自己的一套原则,当你定义了一个封装,如下一个场景就能还原这个问题:

function fetchJSON(url: string, method: "GET" | "POST") {    return fetch(url, { method })      .then(response => response.json());  }    const HTTPRequestMethod = {    CONNECT: "CONNECT",    DELETE: "DELETE",    GET: "GET",    HEAD: "HEAD",    OPTIONS: "OPTIONS",    PATCH: "PATCH",    POST: "POST",    PUT: "PUT",    TRACE: "TRACE"  };

如果你使用:

fetchJSON("https://example.com/", HTTPRequestMethod.GET)    .then(data => {      // ...    });

TS编译器检查类型时会报错,原因在于属性的字符串和字符串类型,这是有区别的,要理解这个问题需要去理解 TS 是如何定义字符串的,这里作者写的很长,综合起来就是:

  • 如果定义常量,不可变,TS知道其中的值
  • 使用的 let 或者 对象属性都是字符串类型,这和明确定义的字符"GET"类型不符合
  • TS 有一种特殊的类型叫:扩展字符串类型,比如 "GET" as "GET"

我们可以设想一下,HTTP的规范定义的 Method 有 9 个,如果我们都那样写扩展字符串类型,这个就很脆了。

TS 3.4 给予了一个新功能来处理这个问题:

const HTTPRequestMethod = {    CONNECT: "CONNECT",    DELETE: "DELETE",    GET: "GET",    HEAD: "HEAD",    OPTIONS: "OPTIONS",    PATCH: "PATCH",    POST: "POST",    PUT: "PUT",    TRACE: "TRACE"  } as const;

这是使用 const 关键字的特殊类型:

  • 文字表达式中的文字类型都不会扩展
  • 对象属性只读
  • 数组变成只读的元组

由于变成了只读,如果你还想给 HTTPRequestMethod 分配新的值时,TS编译器是会给出错误的。

最后结论:

例如,您可以定义一个ORIGIN变量来描述二维空间中的原点,如下所示:

const ORIGIN = {    x: 0,    y: 0  } as const;

这等同于(并且比以下声明更简洁):

const ORIGIN: {    readonly x: 0;    readonly y: 0;  } = {    x: 0,    y: 0  };