做者: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) { // ... });
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);
在上面的例子中,同时指定了printWidth
和semicolons
。由于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...
我是小智,公众号「大迁世界」做者,对前端技术保持学习爱好者。我会常常分享本身所学所看的干货,在进阶的路上,共勉!
关注公众号,后台回复福利,便可看到福利,你懂的。