let widenedStringLiteral = stringLiteral; // Type string
let widenedNumericLiteral = numericLiteral; // Type number
let widenedBooleanLiteral = booleanLiteral; // Type boolean
与 const 变量相反,使用 let 声明的变量是可以修改的。如果 TypeScript 为 let 变量推断一个字面量类型,那么尝试为指定的值以外的任何值赋值都会在编译时产生错误。
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
}
function Tagged<TBase extends Constructor>(Base: TBase) {
return class extends Base {
tag: string | null;
constructor(...args: any[]) {
super(...args);
this.tag = null;
}
};
}
如果在混合类中定义构造函数,那么它必须有一个类型为any[]的rest参数。这样做的原因是,mixin不应该绑定到具有已知构造函数参数的特定类;因此,mixin应该接受任意数量的任意值作为构造函数参数。所有参数都传递给Base的构造函数,然后mixin执行它的任务。在咱们的例子中,它初始化 tag 属性。
混合构造函数类型指仅有单个构造函数签名,且该签名仅有一个类型为 any[] 的变长参数,返回值为对象类型. 比如, 有 X 为对象类型, new (...args: any[]) => X 是一个实例类型为 X 的混合构造函数类型。
以前面使用Timestamped的相同方式来使用混合Tagged:
// 通过 User 作为混合 Tagged 来创建一个新类
const TaggedUser = Tagged(User);
// 实例化 "TaggedUser" 类
const user = new TaggedUser("John Doe");
// 现在,可以从 User 类访问属性和 Tagged 中的属性
user.name = "Jane Doe";
user.tag = "janedoe";
上一篇
更好的类型推断
的文章中,解释了 TypeScript 如何用const
变量和readonly
属性的字面量始化来推断字面量类型
。这篇文章继续讨论这个,扩展和非扩展字面量类型之间的区别。扩展字面量类型
当使用
const
关键字声明局部变量并使用字面量值初始化它时,TypeScript 将推断该变量的字面量类型
:由于
const
关键字,每个变量的值都不能更改,所以字面量类型
非常有意义。它保存了关于被赋值的确切信息。如果如果
let
声明上述的变量,那么每个字面量类型都被扩展为相应的扩展类型:与
const
变量相反,使用let
声明的变量是可以修改的。如果 TypeScript 为let
变量推断一个字面量类型,那么尝试为指定的值以外的任何值赋值都会在编译时产生错误。因此,对于上述每个
let
变量,都会推断出扩展的类型,枚举字面量也是如此:总结一下,下面是扩大字面量类型的规则:
字符串字面量类型被扩展为
string
类型数字字面量类型被扩展为
number
类型布尔字面量类型被扩展为
boolean
类型枚举字面量类型被扩展为包含枚举的类型
到目前为止,咱们一直在研究字面量类型的扩展,在必要时自动扩展。现在来看看非扩展字面量类型,如名所示,它们不会自动地扩展。
非扩展字面量类型
可以通过显式地将变量标注为
字面量类型
来创建非扩展字面量类型的变量将非扩展字面量类型的变量的值赋给另一个变量,该变量将不会扩展。
非扩展字面量类型的好处
为了理解非扩展字面量类型的是有用的,咱们再来看看扩展字面量类型。在下面的例子中,一个数组是由两个可扩展字符串字面量类型的变量创建的:
TypeScript 推断数组
protocols
的类型为string[]
。因此,像first
和second
这样的数组元素类型被扩展为string
。字面量类型"http"
和"https"
的概念在扩展过程中丢失了。如果咱们显式地将这两个常量指定为非扩展类型,则
protocols
数组将被推断为类型("http" | "https")[]
,它表示一个数组,其中仅包含字符串"http"
或"https"
:现在
first
和second
的类型被推断为"http" | "https"
。这是因为数组类型没有对索引0
处的值"http"
和索引1
处的值"https"
进行编码。它只是声明该数组只包含两个字面量类型的值,不管在哪个位置。如果出于某种原因,希望保留数组中字符串字面量类型的位置信息,可以用如下的方式显示指定:
现在,
first
和second
被推断为各自的非扩展字符串字面量类型。无类型导入
从TypeScript 2.1 开始处理
无类型化导入
更加容易。以前,编译器过于严格,当导入一个没有附带类型定义的模块时,会出现一个错误:从 TypeScript 2.1 开始,如果模块没有类型声明,编译器将不再报错。
现在,导入的
range
函数的类型为any
。这样做的好处是,将现有的 JS 项目迁移到 TypeScrip t可以减少编译时错误。缺点是,不会得到任何自动完成建议或细粒度类型检查,因为编译器对模块或其导出一无所知。如果过后提供类型声明,例如通过
npm
的类型声明包,它们将优先于默认的任何类型。(否则,将无法为导入的模块提供类型)对于没有声明文件的模块的导入,在使用了
--noImplicitAny
编译参数后仍将被标记为错误。支持
--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
类型。使用TypeScript 2.1,TypeScript 不是仅仅选择
any
类型,而是基于你后面的赋值来推断类型。仅当设置了
--noImplicitAny
编译参数时,才会启用此选项。示例
现在对空数组也进行同样的跟踪。
没有类型注解并且初始值为
[]
的变量被认为是一个隐式的any[]
变量。变量会根据下面这些操作x.push(value)、x.unshift(value)
或x[n] = value
向其中添加的元素来不断改变自身的类型。隐式 any 错误
这样做的一个很大的好处是,当使用
--noImplicitAny
运行时,你将看到较少的隐式any
错误。隐式any
错误只会在编译器无法知道一个没有类型注解的变量的类型时才会报告。示例
这边顺便给大家推荐一个好用的BUG监控工具 Fundebug。
原文:
https://mariusschulz.com/blog/literal-type-widening-in-typescript https://mariusschulz.com/blog/untyped-imports-in-typescript
交流
干货系列文章汇总如下,觉得不错点个Star,欢迎 加群 互相学习。
我是小智,公众号「大迁世界」作者,对前端技术保持学习爱好者。我会经常分享自己所学所看的干货,在进阶的路上,共勉!
关注公众号,后台回复福利,即可看到福利,你懂的。
个人专栏 ES6 深入浅出已上线,深入ES6 ,通过案例学习掌握 ES6 中新特性一些使用技巧及原理,持续更新中,←点击可订阅。
系列文章如下:
【TypeScript 演化史 -- 1】non-nullable 的类型
【TypeScript 演化史 -- 2】基于控制流的类型分析 和 只读属性
【TypeScript 演化史 -- 3】标记联合类型 与 never 类型
【TypeScript 演化史 -- 4】更多的字面量类型 与 内置类型声明
【TypeScript 演化史 -- 5】将 async/await 编译到 ES3/ES5 (外部帮助库)
【TypeScript 演化史 -- 6】对象扩展运算符和 rest 运算符及 keyof 和查找类型
【TypeScript 演化史 -- 7】映射类型和更好的字面量类型推断
【TypeScript 演化史 -- 8】字面量类型扩展 和 无类型导入
【TypeScript 演化史 -- 9】object 类型 和 字符串索引签名类型的点属性
更好地检查表达式的操作数中的
null
/undefined
在TypeScript 2.2中,空检查得到了进一步的改进。TypeScript 现在将带有可空操作数的表达式标记为编译时错误。
具体来说,下面这些会被标记为错误:
如果
+
运算符的任何一个操作数是可空的,并且两个操作数都不是any
或string
类型。如果
-
,*
,**
,/
,%
,<<
,>>
,>>>
,&
,|
或^
运算符的任何一个操作数是可空的。如果
<
,>
,<=
,>=
或in
运算符的任何一个操作数是可空的。如果
instanceof
运算符的右操作数是可空的。如果一元运算符
+
,-
,~
,++
或者--
的操作数是可空的。来看看如果咱们不小心,可空表达式操作数就会坑下咱们的情况。在 TypeScript 2.2 之前,下面这个函数是可以很好地编译通过的:
注意,
max
参数是可选的。这意味着咱们可以使用两个或三个参数来调用isValidPasswordLength
:密码
"open sesame"
的长度为10
个字符。因此,对于长度范围[6,128]
返回true
,对于长度范围[6,8]
返回false
,到目前为止,一切 ok。如果调用
isValidPasswordLength
且不提供max
参数值,那么当密码长度超过min
值时,咱们可能希望返回true
。然而,事实并非如此:这里的问题在于
<= max
比较。如果max
是undefined
,那么<= max
的值永远都为false
。在这种情况下,isValidPasswordLength
将永远不会返回true
。在 TypeScript 2.2 中,表达式
password.length <= max
是不正确的类型,如果你的应用程序正在严格的null
检查模式下运行:那么要怎么修正这个问题呢?一种的解决方案是为
max
参数提供一个默认值,它只在传递undefined
时起作用。这样,该参数仍然是可选的,但始终包含类型为number
的值当然咱们也可以选择其他的方法,但是我觉得这个方法很好。只要不再将
max
与undefined
的值进行比较,就可以了混合类
TypeScript 的一个目的是支持不同框架和库中使用的通用 JS 模式。从TypeScript 2.2开始,增加了对 ES6 混合类(mixin class)模式。接下来讲讲
mixin
是什么,然后举例说明了如何在 TypeScript 中使用它们。JavaScript/TypeScript中的 mixin
混合类是实现不同功能方面的类。其他类可以包含
mixin
并访问它的方法和属性。这样,mixin
提供了一种基于组合行为的代码重用形式。混合类指一个
extends
(扩展)了类型参数类型的表达式的类声明或表达式. 以下规则对混合类声明适用:extends
表达式的类型参数类型必须是混合构造函数.any[]
的变长参数, 并且必须使用展开运算符在super(...args)
调用中将这些参数传递。定义完成之后,来研究一些代码。下面是一个
Timestamped
函数,它在timestamp
属性中跟踪对象的创建日期:这看起来有点复杂,咱们一行一行来看看:
type Constructor <T>
是构造签名的别名,该签名描述了可以构造通用类型T
的对象的类型,并且其构造函数接受任意数量的任何类型的参数。接下来,让我们看一下
mixin
函数本身:Timestamped
函数接受一个名为Base
的参数,该参数属于泛型类型TBase
。注意,TBase
必须与Constructor
兼容,即类型必须能够构造某些东西。在函数体中,咱们创建并返回一个派生自
Base
的新类。这种语法乍一看可能有点奇怪。咱们创建的是类表达式,而不是类声明,后者是定义类的更常用方法。咱们的新类定义了一个timestamp
的属性,并立即分配自UNIX时代以来经过的毫秒数。注意,从
mixin
函数返回的类表达式是一个未命名的类表达式,因为class
关键字后面没有名称。与类声明不同,类表达式不必命名。咱们可以选择添加一个名称,它将是类主体的本地名称,并允许类引用自己现在已经介绍了两个类型别名和
mixin
函数的声明,接下来看看如何在另一个类中使用mixin
:TypeScript 编译器知道我们在这里创建并使用了一个
mixin
,一切都是完全静态类型的,并且会自动完成和重构。混合构造函数
现在,看看一个稍微高级一点的
mixin
,类中定义一个构造函数如果在混合类中定义构造函数,那么它必须有一个类型为
any[]
的rest
参数。这样做的原因是,mixin
不应该绑定到具有已知构造函数参数的特定类;因此,mixin
应该接受任意数量的任意值作为构造函数参数。所有参数都传递给Base
的构造函数,然后mixin
执行它的任务。在咱们的例子中,它初始化tag
属性。以前面使用
Timestamped
的相同方式来使用混合Tagged
:mixin 与方法
到目前为止,咱们只在
mixin
中添加了数据属性。现在来看看另一个mixin
,它额外实现了两个方法:咱们从
mixin
函数返回一个常规的类。这意味着咱们可以使用所有受支持的类功能,例如构造函数,属性,方法,getter/setter
,静态成员等。如何所示,咱们如何在
User
类中使用混合的Activatable
:组合多个mixin
组合的mixin,可以让它更加灵活。一个类可以包含任意多的
mixin
,为了演示这点,咱们把上面提到的所有mixin
代码组合在一起。当然
SpecialUser
类不一定非常有用,但关键是,TypeScript
静态地理解这种mixin
组合。编译器可以类型检查所有的使用,并在自动完成列表中建议可用的成员:与类继承进行对比,有个区别:一个类只能有一个基类。继承多个基类在 JS 中不行的,因此在
TypeScript
中也不行。原文: https://mariusschulz.com/blog/null-checking-for-expression-operands-in-typescript https://mariusschulz.com/blog/mixin-classes-in-typescript
代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug。
交流
干货系列文章汇总如下,觉得不错点个Star,欢迎 加群 互相学习。
我是小智,公众号「大迁世界」作者,对前端技术保持学习爱好者。我会经常分享自己所学所看的干货,在进阶的路上,共勉!
关注公众号,后台回复福利,即可看到福利,你懂的。