【TypeScript 演化史 -- 4】更多的字面量类型 与 内置类型声明

做者:Marius Schulz
译者:前端小智
来源:Marius Schulz

阿里云双12已开启,新老用户都可参与,2核1G云服务器仅需79元,,更多服务器配置及价格请关注:Hi拼团,或点此了解“云上爆款1折特惠活动”。同时,建议在购买阿里云相关产品前先领取阿里云2000元代金券会更优惠哦。前端


为了保证的可读性,本文采用意译而非直译。git

TypeScript 1.8 引入了字符串字面量类型,用于将变量限制为可能的字符串值的有限集。在 TypeScript 2.0 中,字面量类型再也不局限于字符串。如下字面量类型已添加到类型系统中:es6

  • boolean 字面量类型
  • 数字字面量类型
  • 枚举字面量类型

接下来,来看看这个类型对应的一些事例。github

boolean 字面量类型

下面的示例定义了两个常量 TRUEFALSE,它们分别持有 true false 值:web

const TRUE: true = true; // OK
const FALSE: false = false; // OK

试图为每一个局部变量分配相反的布尔值会致使类型错误:typescript

const TRUE: true = false;
// Error: Type 'false' is not assignable to type 'true'

const FALSE: false = true;
// Error: Type 'true' is not assignable to type 'false'

随着 boolean 字面类型的引入,预约义的 boolean 类型如今等价于 true | false 的联合类型:npm

let value: true | false; // Type boolean

虽然 boolean 字面量类型在隔离时不多有用,但它们与标记联合类型和基于控制流的类型分析结合使用时很是有效。例如,能够定义一个泛型 Result <T>类型,该类型要么包含一个类型为 T 的值,要么包含一个类型为 string 的错误消息,以下所示json

type Result<T> =
  | { success: true; value: T }
  | { success: false; error: string };

这是一个接受参数的函数:segmentfault

function parseEmailAddress(
  input: string | null | undefined
): Result<string> {
  // 若是 input 为 null,undefined 或空字符串   
  //(全部都是虚假的值),就直接返回。
  if (!input) {
    return {
      success: false,
      error: "The email address cannot be empty."
    };
  }

  // 咱们只检查 input 是否与模式匹配   
  // <something> @ <something> . <something>   
  // 保持简单,正确验证电子邮件地址 
  if (!/^\S+@\S+\.\S+$/.test(input)) {
    return {
      success: false,
      error: "The email address has an invalid format."
    };
  }

  return {
    success: true,
    value: input
  };
}

请注意,启用 strictNullChecks 选项后,string 是不可为 null 的类型。为了使函数的 input 参数接受可为 null 的类型的值,必须在联合类型中明确包含 nullundefined 类型。promise

咱们如今能够像下面这样调用 parseEmailFunction

const parsed = parseEmailAddress("example@example.com");

if (parsed.success) {
  parsed.value; // OK
  parsed.error; // Error
} else {
  parsed.value; // Error
  parsed.error; // OK
}

请注意,某些属性访问表达式用红色波浪线下划线:

clipboard.png

这样作的好处是,编译器仅在检查了 parsed.success 后才容许我们使用valueerror属性:

  • 若是 parsed.successtrue,则 parsed 的类型必须为 { success: true; value: string }。在这种状况下,我们能够访问 value,但不能访问 error
  • 若是 parsed.successfalse,则 parsed 的类型必须为 { success: false; error: string }。在这种状况下,我们能够访问 error,但不能访问 value

数字字面量类型

与字符串字面量类型相似,咱们能够将数值变量限制为已知值的有限集

let zeroOrOne: 0 | 1;

zeroOrOne = 0;
// OK

zeroOrOne = 1;
// OK

zeroOrOne = 2;
// 错误:类型 '2' 不能分配给类型 '0 | 1'

在实践中,咱们能够在处理端口号时使用数字字面量。不安全的 HTTP 使用端口 80,而 HTTPS 使用端口 443。我们能够编写一个 getPort 函数,并在其函数签名中编码仅有的两个可能的返回值

function getPort(scheme: "http" | "https"): 80 | 443 {
  switch (scheme) {
    case: "http":
      return 80;
    case: "https":
      return 443;
  }
}

const httpPort = getPort('http'); // Type 80 | 443

若是咱们将字面量类型与 TypeScript 的函数重载结合起来,那就更有趣了。这样,咱们能够为 getPort函数的不一样重载提供更具体的类型。

function getPort(scheme: "http"): 80;
function getPort(scheme: "https"): 443;
function getPort(scheme: "http" | "https"): 80 | 443 {
  switch (scheme) {
    case "http":
      return 80;
    case "https":
      return 443;
  }
}

const httpPort = getPort("http"); // Type 80
const httpsPort = getPort("https"); // Type 443

如今,当返回的时候与比较的值永远都不会相同的状况下,编辑器会提示咱们,例如,将 httpPort 与值 443 进行比较时:

clipboard.png

因为 httpPort 的类型为 80,所以它始终包含值 80,该值固然永远不会等于值 443。在这种状况下,TypeScript 编译器能够帮助我们检测错误的逻辑和无效的代码。

枚举字面量类型

最后,我们还可使用枚举做为字面量类型。继续前面的示例,实现一个给定端口(80443)映射到相应方案(分别为 HTTPHTTPS)的函数。为此,咱们首先声明一个const enum,它对两个端口号进行构建:

const enum HttpPort {
  Http = 80,
  Https = 443
}

如今是 getScheme 函数,再次使用函数重载来实现专门的类型注解:

function getScheme(port: HttpPort.Http): "http";
function getScheme(port: HttpPort.Https): "https";
function getScheme(port: HttpPort): "http" | "https" {
  switch (port) {
    case HttpPort.Http:
      return "http";
    case HttpPort.Https:
      return "https";
  }
}

const scheme = getScheme(HttpPort.Http);
// Type "http"

常量枚举没有运行时表现形式(除非提供了preserveConstEnums编译器选项),也就是说,enum 用例的常量值将被内联到使用它们的任何地方。下面是通过编译的 JS 代码,去掉了注解:

function getScheme(port) {
  switch (port) {
    case 80:
      return "http";
    case 443:
      return "https";
  }
}
var scheme = getScheme(80);

是否是超级简洁?

TypeScript 2.0 让我们以更细粒度地控制项目中包含哪些内置 API 声明。之前,只有在的项目配置 ES6 相关的包才能访问 ES6 Api。如今,内置的标准库声明已经模块化,TypeScript 容许咱们选择包含哪一种类型声明。

--lib 编译器选项

JS 标准库的类型声明被划分为一组 API 组。 2016 年 11 月下旬撰写本文时,定义了如下组

  • dom
  • webworker
  • es5
  • es6 / es2015
  • es2015.core
  • es2015.collection
  • es2015.iterable
  • es2015.promise
  • es2015.proxy
  • es2015.reflect
  • es2015.generator
  • es2015.symbol
  • es2015.symbol.wellknown
  • es2016
  • es2016.array.include
  • es2017
  • es2017.object
  • es2017.sharedmemory
  • scripthos

能够经过 --lib 命令行选项或 tsconfig.json 中的 lib 属性将上述组的任何子集传递给TypeScript 编译器。TypeScript 将只注入你指定的类型;也就是说,它会将全部其余 API 组视为不存在于你的的环境中。

若是未明确提供 lib 选项,则 TypeScript 将隐式注入Web开发所需的API组。

注意:若是--lib没有指定默认库。默认库是

  • For --target ES5:["dom", "es5", "scripthost"]
  • For --target ES6: ["dom", "es6", "dom.iterable", "scripthost"]

以 es5 为 target 的 TypeScript 项目中使用 ES6 Promise

假设你正在处理一个 targetes5 项目,为了让它能在全部主流浏览器中运行。你的tsconfig.json 多是这样的:

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es5",
    "noImplicitAny": true,
    "strictNullChecks": true
  }
}

由于 lib 选项没有指定,因此默认状况下 TypeScript 会注入 API 组 "dom""es5""scripthost"。如今但愿在项目中使用ES6 中原生的 Pormise。这些在 ES5 中并无,因此我们须要安装一个 polyfill 来让咱们的代码在旧的浏览器中运行:

npm install --save es6-promise

而后能够在入口文件中导入对应的库

import "es6-promise";

有了这个 polyfill,如今就能够在应用程序中使用 Promise,代码也能够正常运行。然而,TypeScript 会给你一个编译时错误: Cannot find the name 'Promise'。这是由于 Promise 的类型声明不包含在任何注入的 API 组中。

clipboard.png

咱要让 TypeScript 知道 Promise 会在运行时存在,这就是 lib 编译器选项发挥做用的地方:

clipboard.png

注意,一旦覆盖了默认值,就必须显式地提供全部API组,以下所示:

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es5",
    "noImplicitAny": true,
    "strictNullChecks": true,
    "lib": ["dom", "es5", "es2015.promise"]
  }
}

如今编辑器就不会在报错了:

clipboard.png


编辑中可能存在的bug无法实时知道,过后为了解决这些bug,花了大量的时间进行log 调试,这边顺便给你们推荐一个好用的BUG监控工具 Fundebug

原文:

https://mariusschulz.com/blog...
https://mariusschulz.com/blog...


交流

干货系列文章汇总以下,以为不错点个Star,欢迎 加群 互相学习。

https://github.com/qq449245884/xiaozhi

由于篇幅的限制,今天的分享只到这里。若是你们想了解更多的内容的话,能够去扫一扫每篇文章最下面的二维码,而后关注我们的微信公众号,了解更多的资讯和有价值的内容。

clipboard.png

相关文章
相关标签/搜索