【TS 演化史 -- 13】字符串枚举 和 弱类型(Weak Type)探测

做者:Marius Schulz
译者:前端小智
来源: https://mariusschulz.com/
点赞再看,养成习惯

本文 GitHub https://github.com/qq44924588... 上已经收录,更多往期高赞文章的分类,也整理了不少个人文档,和教程资料。欢迎Star和完善,你们面试能够参照考点复习,但愿咱们一块儿有点东西。html

字符串枚举

TypeScript 2.4 实现了最受欢迎的特性之一:字符串枚举,或者更精确地说,带有字符串值成员的枚举前端

如今能够将字符串值分配给枚举成员了:git

enum MediaTypes {
  JSON = 'application/json',
  XML = 'application/xml'
}

字符串枚举能够像 TypeScript 中的任何其余枚举同样使用:github

enum MediaTypes {
  JSON = "application/json",
  XML = "application/xml"
}

fetch("https://example.com/api/endpoint", {
  headers: {
    Accept: MediaTypes.JSON
  }
}).then(response => {
  // ...
});

下面编译目标为 es3/es5 生成的 JS 的代码:面试

var MediaTypes;
(function (MediaTypes) {
    MediaTypes["JSON"] = "application/json";
    MediaTypes["XML"] = "application/xml";
})(MediaTypes || (MediaTypes = {}));
fetch("https://example.com/api/endpoint", {
    headers: {
        Accept: MediaTypes.JSON
    }
}).then(function (response) {
    // ...
});

这个输出几乎与编译器为带有数字成员的枚举生成的输出相似,只是字符串值成员没有反向映射。typescript

字符串值枚举成员没有反向映射

TypeScript 为每一个构造映射对象的枚举发出一些映射代码。对于字符串值枚举成员,此映射对象定义从键到值的映射,反之则不是:json

var MediaTypes;
(function (MediaTypes) {
    MediaTypes["JSON"] = "application/json";
    MediaTypes["XML"] = "application/xml";
})(MediaTypes || (MediaTypes = {}));

这意味着我们能够经过键解析一个值,但不能经过键的值解析一个键segmentfault

MediaTypes["JSON"]; // "application/json"
MediaTypes["application/json"]; // undefined

MediaTypes["XML"]; // "application/xml"
MediaTypes["application/xml"]; // undefined

与具备数字值成员的枚举进行比较:api

enum DefaultPorts {
  HTTP = 80,
  HTTPS = 443
}

在这种状况下,编译器还会生成从值到键的反向映射app

var DefaultPorts;
(function(DefaultPorts) {
  DefaultPorts[(DefaultPorts["HTTP"] = 80)] = "HTTP";
  DefaultPorts[(DefaultPorts["HTTPS"] = 443)] = "HTTPS";
})(DefaultPorts || (DefaultPorts = {}));

这种反向映射容许经过键值解析键和经过键解析值

DefaultPorts["HTTP"]; // 80
DefaultPorts[80]; // "HTTP"

DefaultPorts["HTTPS"]; // 443
DefaultPorts[443]; // "HTTPS"

用常量枚举内联枚举成员

为了不生成的枚举映射代码的开销,我们能够经过将const修饰符添加到声明中,将MediaTypes枚举转换为const枚举:

const enum MediaTypes {
  JSON = "application/json",
  XML = "application/xml"
}

fetch("https://example.com/api/endpoint", {
  headers: {
    Accept: MediaTypes.JSON
  }
}).then(response => {
  // ...
});

使用const修饰符后,编译器将不会为MediaTypes枚举生成任何映射代码。相反,它将内联全部使用站点上每一个枚举成员的值,从而可能节省一些字节和属性访问间接性的开销:

fetch("https://example.com/api/endpoint", {
    headers: {
        Accept: "application/json" /* JSON */
    }
}).then(function (response) {
    // ...
});

可是,若是因为某种缘由,我们须要在运行时访问映射对象,该怎么办呢

使用preserveConstEnums生成一个常量枚举

有时,可能有必要发出一个const枚举的映射代码,例如,当某些 JS 代码须要访问它时,在这种状况下,能够在tsconfig.json文件中打开prepareConstEnums编译器选项:

{
  "compilerOptions": {
    "target": "es5",
    "preserveConstEnums": true
  }
}

若是我们使用设置的preserveConstEnums选项再次编译代码,编译器仍然会内联MediaTypes,同时它也会发出映射代码:

var MediaTypes;
(function (MediaTypes) {
    MediaTypes["JSON"] = "application/json";
    MediaTypes["XML"] = "application/xml";
})(MediaTypes || (MediaTypes = {}));
fetch("https://example.com/api/endpoint", {
    headers: {
        Accept: "application/json" /* JSON */
    }
}).then(function (response) {
    // ...
});

弱类型(Weak Type)探测

TypeScript 2.4 引入了弱类型的概念。若是类型的全部属性都是可选的,则认为类型是弱类型。更具体地说,弱类型定义一个或多个可选属性,没有必需属性,也没有索引签名。

例如,下面的类型被认为是弱类型:

interface PrettierConfig {
  printWidth ?: number;
  tabWidth?: number;
  semi?: boolean;
}

弱类型检测的主要目标是发现代码中可能的错误,不然这些错误将是无声的错误。考虑一下这个例子:

interface PrettierConfig {
  printWidth?: number;
  tabWidth?: number;
  semi?: boolean;
}

function createFormatter(config: PrettierConfig) {
  // ...
}

const prettierConfig = {
  semicolons: true
};

const formatter = createFormatter(prettierConfig); // 错误

在 TypeScript 2.4 以前,这段代码是类型正确的。PrettierConfig的全部属性都是可选的,因此彻底能够不指定它们。相反,我们的prettierConfig对象有一个semicolons 属性,它在prettierConfig类型中不存在。

从 TypeScript 2.4 开始,当属性没有重叠时,给弱类型赋值是一个错误,带有如下消息的类型检查器错误

类型“{ semicolons: boolean; }”与类型“PrettierConfig”不具备相同的属性

虽然我们的代码并不是严格错误,但它可能包含一个静默错误。createFormatter函数可能会忽略它不知道的config的任何属性(例如semicolons),并退回到每一个属性的默认值。在这种状况下,不管将其semicolons 设置为true仍是false,我们的semicolons 属性都不会起做用。

TypeScript 的弱类型检测帮助我们解决了这个问题,并在函数调用中为prettierConfig参数提出了一个类型错误。这样,我们很快就会意识到有些事情看起来不对劲。

显式类型注解

无需依赖弱类型检测,我们能够向prettierConfig对象显式添加类型注释:

const prettierConfig: PrettierConfig = {
  semicolons: true // Error
};

const formatter = createFormatter(prettierConfig);

使用了这个类型注释,我们会获得如下类型错误:

不能将类型“{ semicolons: boolean; }”分配给类型“PrettierConfig”。
  对象文字能够只指定已知属性,而且“semicolons”不在类型“PrettierConfig”中。

这样,类型错误就出如今我们(错误地)定义semicolons 属性的地方,而不是将prettierConfig参数传递给createFormatter函数的行中。

另外一个好处是 TypeScript 语言能够给我们自动完成建议,由于类型注释告诉它咱建立的对象的类型。

弱类型的解决方法

若是出于某种缘由,我们就是不想从特定弱类型的弱类型检测中得到错误,该怎么办?一种解决方法是使用unknown 类型添加索引签名到PrettierConfig类型:

interface PrettierConfig {
  [prop: string]: unknown;
  printWidth?: number;
  tabWidth?: number;
  semi?: boolean;
}

function createFormatter(config: PrettierConfig) {
  // ...
}

const prettierConfig = {
  semicolons: true
};

const formatter = createFormatter(prettierConfig);

如今,这段代码是类型正确的,由于我们在PrettierConfig类型中明确容许使用unknown名称的属性。

或者,我们可使用类型断言来告诉类型检查器将prettierConfig对象视为类型为PrettierConfig

interface PrettierConfig {
  printWidth?: number;
  tabWidth?: number;
  semi?: boolean;
}

function createFormatter(config: PrettierConfig) {
  // ...
}

const prettierConfig = {
  semicolons: true
};

const formatter = createFormatter(
  prettierConfig as PrettierConfig
);

建议不要使用类型断言来绕过弱类型检。也许在一个用例中,这种方法是有意义的,可是一般,我们应该更喜欢其余解决方案之一。

弱类型检测的限制

请注意,弱类型检测仅在属性中彻底没有重叠时才会产生类型错误。一旦指定了弱类型中定义的一个或多个属性,编译器将再也不引起类型错误

interface PrettierConfig {
  printWidth?: number;
  tabWidth?: number;
  semi?: boolean;
}

function createFormatter(config: PrettierConfig) {
  // ...
}

const prettierConfig = {
  printWidth: 100,
  semicolons: true
};

const formatter = createFormatter(prettierConfig);

在上面的例子中,同时指定了printWidthsemicolons。由于printWidth存在于PrettierConfig中,如今我们的对象和PrettierConfig类型之间有一个属性重叠,弱类型检测再也不为函数调用引起类型错误。

这里的结论是,弱类型检测目的设计是为了最小化误报(正确的使用被视为不正确)的数量,这是以牺牲更少的真报(不正确的使用被视为不正确)为代价的。


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

原文:

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

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

https://www.tslang.cn/docs/re...


交流

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

https://github.com/qq44924588...

我是小智,公众号「大迁世界」做者,对前端技术保持学习爱好者。我会常常分享本身所学所看的干货,在进阶的路上,共勉!

关注公众号,后台回复福利,便可看到福利,你懂的。

clipboard.png

相关文章
相关标签/搜索