做者:Marius Schulz前端
译者:前端小智node
来源:mariusschulz.com/git
点赞再看,养成习惯
github
本文 GitHub:github.com/qq449245884… 上已经收录,更多往期高赞文章的分类,也整理了不少个人文档,和教程资料。欢迎Star和完善,你们面试能够参照考点复习,但愿咱们一块儿有点东西。面试
系列文章以下:typescript
【TypeScript 演化史 -- 1】non-nullable 的类型npm
【TypeScript 演化史 -- 2】基于控制流的类型分析 和 只读属性数组
【TypeScript 演化史 -- 3】标记联合类型 与 never 类型bash
【TypeScript 演化史 -- 4】更多的字面量类型 与 内置类型声明app
【TypeScript 演化史 -- 5】将 async/await 编译到 ES3/ES5 (外部帮助库)
【TypeScript 演化史 -- 6】对象扩展运算符和 rest 运算符及 keyof 和查找类型
【TypeScript 演化史 -- 7】映射类型和更好的字面量类型推断
上一篇更好的类型推断
的文章中,解释了 TypeScript 如何用 const
变量和 readonly
属性的字面量始化来推断字面量类型
。这篇文章继续讨论这个,扩展和非扩展字面量类型之间的区别。
当使用 const
关键字声明局部变量并使用字面量值初始化它时,TypeScript 将推断该变量的字面量类型
:
const stringLiteral = "https"; // Type "https"
const numericLiteral = 42; // Type 42
const booleanLiteral = true; // Type true
复制代码
因为 const
关键字,每一个变量的值都不能更改,因此字面量类型
很是有意义。它保存了关于被赋值的确切信息。
若是若是 let
声明上述的变量,那么每一个字面量类型都被扩展为相应的扩展类型:
let widenedStringLiteral = stringLiteral; // Type string
let widenedNumericLiteral = numericLiteral; // Type number
let widenedBooleanLiteral = booleanLiteral; // Type boolean
复制代码
与 const
变量相反,使用 let
声明的变量是能够修改的。若是 TypeScript 为 let
变量推断一个字面量类型,那么尝试为指定的值之外的任何值赋值都会在编译时产生错误。
所以,对于上述每一个let
变量,都会推断出扩展的类型,枚举字面量也是如此:
enum FlexDirection {
Row,
Column
}
const enumLiteral = FlexDirection.Row; // FlexDirection.Row 类型
let widenedEnumLiteral = enumLiteral; // FlexDirection 类型
复制代码
总结一下,下面是扩大字面量类型的规则:
字符串字面量类型被扩展为 string
类型
数字字面量类型被扩展为 number
类型
布尔字面量类型被扩展为 boolean
类型
枚举字面量类型被扩展为包含枚举的类型
到目前为止,我们一直在研究字面量类型的扩展,在必要时自动扩展。如今来看看非扩展字面量类型,如名所示,它们不会自动地扩展。
能够经过显式地将变量标注为字面量类型
来建立非扩展字面量类型的变量
const stringLiteral: "https" = "https"; // 类型 "https" (非扩展)
const numericLiteral: 42 = 42; // 类型 42 (非扩展)
复制代码
将非扩展字面量类型的变量的值赋给另外一个变量,该变量将不会扩展。
let widenedStringLiteral = stringLiteral; // 类型 "https" (非扩展)
let widenedNumericLiteral = numericLiteral; // 类型 42 (非扩展)
复制代码
为了理解非扩展字面量类型的是有用的,我们再来看看扩展字面量类型。在下面的例子中,一个数组是由两个可扩展字符串字面量类型的变量建立的:
const http = "http"; // Type "http" (可扩展)
const https = "https"; // Type "https" (可扩展)
const protocols = [http, https]; // Type string[]
const first = protocols[0]; // Type string
const second = protocols[1]; // Type string
复制代码
TypeScript 推断数组 protocols
的类型为 string[]
。所以,像 first
和 second
这样的数组元素类型被扩展为 string
。字面量类型 "http"
和 "https"
的概念在扩展过程当中丢失了。
若是我们显式地将这两个常量指定为非扩展类型,则 protocols
数组将被推断为类型 ("http" | "https")[]
,它表示一个数组,其中仅包含字符串 "http"
或 "https"
:
const http: "http" = "http"; // Type "http" (非扩展)
const https: "https" = "https"; // Type "https" (非扩展
const protocols = [http, https]; // Type ("http" | "https")[]
const first = protocols[0]; // Type "http" | "https"
const second = protocols[1]; // Type "http" | "https"
复制代码
如今 first
和 second
的类型被推断为 "http" | "https"
。这是由于数组类型没有对索引 0
处的值 "http"
和索引 1
处的值 "https"
进行编码。它只是声明该数组只包含两个字面量类型的值,无论在哪一个位置。
若是出于某种缘由,但愿保留数组中字符串字面量类型的位置信息,能够用以下的方式显示指定:
const http = "http"; // Type "http" (可扩展)
const https = "https"; // Type "https" (可扩展)
const protocols: ["http", "https"] = [http, https]; // Type ["http", "https"]
const first = protocols[0]; // Type "http" (非扩展)
const second = protocols[1]; // Type "https" (非扩展)
复制代码
如今,first
和 second
被推断为各自的非扩展字符串字面量类型。
从TypeScript 2.1 开始处理无类型化导入
更加容易。之前,编译器过于严格,当导入一个没有附带类型定义的模块时,会出现一个错误:
从 TypeScript 2.1 开始,若是模块没有类型声明,编译器将再也不报错。
如今,导入的 range
函数的类型为 any
。这样作的好处是,将现有的 JS 项目迁移到 TypeScrip t能够减小编译时错误。缺点是,不会获得任何自动完成建议或细粒度类型检查,由于编译器对模块或其导出一无所知。
若是事后提供类型声明,例如经过 npm
的类型声明包,它们将优先于默认的任何类型。(不然,将没法为导入的模块提供类型)
对于没有声明文件的模块的导入,在使用了--noImplicitAny
编译参数后仍将被标记为错误。
// Succeeds if `node_modules/asdf/index.js` exists
import { x } from "asdf";
复制代码
支持--target ES2016
,--target ES2017
和--target ESNext
TypeScript 2.1支持三个新的编译版本值--target ES2016
,--target ES2017
和--target ESNext
。
使用target--target ES2016
将指示编译器不要编译ES2016特有的特性,好比**
操做符。
一样,--target ES2017
将指示编译器不要编译ES2017特有的特性像async/await
。
--target ESNext
则对应最新的ES提议特性支持.
any
类型推断之前,若是 TypeScript 没法肯定变量的类型,它将选择any
类型。
let x; // 隐式 'any'
let y = []; // 隐式 'any[]'
let z: any; // 显式 'any'.
复制代码
使用TypeScript 2.1,TypeScript 不是仅仅选择any
类型,而是基于你后面的赋值来推断类型。
仅当设置了--noImplicitAny
编译参数时,才会启用此选项。
示例
let x;
// 你仍然能够给'x'赋值任何你须要的任何值。
x = () => 42;
// 在刚赋值后,TypeScript 2.1 知道'x'的类型是'() => number'。
let y = x();
// 感谢,如今它会告诉你,你不能添加一个数字到一个函数!
console.log(x + y);
// ~~~~~
// 错误!运算符 '+' 不能应用于类型`() => number`和'number'。
// TypeScript仍然容许你给'x'赋值你须要的任何值。
x = "Hello world!";
// 而且如今它也知道'x'是'string'类型的!
x.toLowerCase();
复制代码
如今对空数组也进行一样的跟踪。
没有类型注解而且初始值为[]
的变量被认为是一个隐式的any[]
变量。变量会根据下面这些操做x.push(value)、x.unshift(value)
或x[n] = value
向其中添加的元素来不断改变自身的类型。
function f1() {
let x = [];
x.push(5);
x[1] = "hello";
x.unshift(true);
return x; // (string | number | boolean)[]
}
function f2() {
let x = null;
if (cond()) {
x = [];
while (cond()) {
x.push("hello");
}
}
return x; // string[] | null
}
复制代码
这样作的一个很大的好处是,当使用--noImplicitAny
运行时,你将看到较少的隐式any
错误。隐式any
错误只会在编译器没法知道一个没有类型注解的变量的类型时才会报告。
示例
function f3() {
let x = []; // 错误:当变量'x'类型没法肯定时,它隐式具备'any[]'类型。
x.push(5);
function g() {
x; // 错误:变量'x'隐式具备'any【】'类型。
}
}
复制代码
这边顺便给你们推荐一个好用的BUG监控工具 Fundebug。
原文:
mariusschulz.com/blog/litera… mariusschulz.com/blog/untype…
干货系列文章汇总以下,以为不错点个Star,欢迎 加群 互相学习。
我是小智,公众号「大迁世界」做者,对前端技术保持学习爱好者。我会常常分享本身所学所看的干货,在进阶的路上,共勉!
关注公众号,后台回复福利,便可看到福利,你懂的。