作者:Marius Schulz 译者:前端小智 来源:https://mariusschulz.com/
点赞再看,养成习惯本文
GitHub
https://github.com/qq44924588... 上已经收录,更多往期高赞文章的分类,也整理了很多我的文档,和教程资料。欢迎Star和完善,大家面试可以参照考点复习,希望我们一起有点东西。
字符串枚举
TypeScript 2.4 实现了最受欢迎的特性之一:字符串枚举,或者更精确地说,带有字符串值成员的枚举。
现在可以将字符串值分配给枚举成员了:
代码语言:javascript复制enum MediaTypes {
JSON = 'application/json',
XML = 'application/xml'
}
字符串枚举可以像 TypeScript 中的任何其他枚举一样使用:
代码语言:javascript复制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 为每个构造映射对象的枚举发出一些映射代码。对于字符串值枚举成员,此映射对象定义从键到值的映射,反之则不是:
代码语言:javascript复制var MediaTypes;
(function (MediaTypes) {
MediaTypes["JSON"] = "application/json";
MediaTypes["XML"] = "application/xml";
})(MediaTypes || (MediaTypes = {}));
这意味着咱们可以通过键解析一个值,但不能通过键的值解析一个键
代码语言:javascript复制MediaTypes["JSON"]; // "application/json"
MediaTypes["application/json"]; // undefined
MediaTypes["XML"]; // "application/xml"
MediaTypes["application/xml"]; // undefined
与具有数字值成员的枚举进行比较:
代码语言:javascript复制enum DefaultPorts {
HTTP = 80,
HTTPS = 443
}
在这种情况下,编译器还会生成从值到键的反向映射
代码语言:javascript复制var DefaultPorts;
(function(DefaultPorts) {
DefaultPorts[(DefaultPorts["HTTP"] = 80)] = "HTTP";
DefaultPorts[(DefaultPorts["HTTPS"] = 443)] = "HTTPS";
})(DefaultPorts || (DefaultPorts = {}));
这种反向映射允许通过键值解析键和通过键解析值
代码语言:javascript复制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 引入了弱类型的概念。如果类型的所有属性都是可选的,则认为类型是弱类型。更具体地说,弱类型定义一个或多个可选属性,没有必需属性,也没有索引签名。
例如,下面的类型被认为是弱类型:
代码语言:javascript复制interface PrettierConfig {
printWidth ?: number;
tabWidth?: number;
semi?: boolean;
}
弱类型检测的主要目标是发现代码中可能的错误,否则这些错误将是无声的错误。考虑一下这个例子:
代码语言:javascript复制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 开始,当属性没有重叠时,给弱类型赋值是一个错误,带有以下消息的类型检查器错误
代码语言:javascript复制类型“{ semicolons: boolean; }”与类型“PrettierConfig”不具有相同的属性
虽然咱们的代码并非严格错误,但它可能包含一个静默错误。createFormatter
函数可能会忽略它不知道的config
的任何属性(例如semicolons
),并退回到每个属性的默认值。在这种情况下,无论将其semicolons
设置为true
还是false
,咱们的semicolons
属性都不会起作用。
TypeScript 的弱类型检测帮助咱们解决了这个问题,并在函数调用中为prettierConfig
参数提出了一个类型错误。这样,咱们很快就会意识到有些事情看起来不对劲。
显式类型注解
无需依赖弱类型检测,咱们可以向prettierConfig
对象显式添加类型注释:
const prettierConfig: PrettierConfig = {
semicolons: true // Error
};
const formatter = createFormatter(prettierConfig);
使用了这个类型注释,咱们会得到以下类型错误:
代码语言:javascript复制不能将类型“{ 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
);
建议不要使用类型断言来绕过弱类型检。也许在一个用例中,这种方法是有意义的,但是通常,咱们应该更喜欢其他解决方案之一。
弱类型检测的限制
请注意,弱类型检测仅在属性中完全没有重叠时才会产生类型错误。一旦指定了弱类型中定义的一个或多个属性,编译器将不再引发类型错误
代码语言:javascript复制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...
我是小智,对前端技术保持学习爱好者。我会经常分享自己所学所看的干货,在进阶的路上,共勉!