做者:Marius Schulzhtml
译者:前端小智前端
来源:mariusschulz.com/git
点赞再看,养成习惯
github
本文 GitHub:github.com/qq449245884… 上已经收录,更多往期高赞文章的分类,也整理了不少个人文档,和教程资料。欢迎Star和完善,你们面试能够参照考点复习,但愿咱们一块儿有点东西。面试
我的专栏 ES6 深刻浅出已上线,深刻ES6 ,经过案例学习掌握 ES6 中新特性一些使用技巧及原理,持续更新中,←点击可订阅。typescript
系列文章以下:express
【TypeScript 演化史 -- 1】non-nullable 的类型app
【TypeScript 演化史 -- 2】基于控制流的类型分析 和 只读属性框架
【TypeScript 演化史 -- 3】标记联合类型 与 never 类型async
【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 以前,下面这个函数是能够很好地编译经过的:
function isValidPasswordLength(
password: string,
min: number,
max?: number
) {
return password.length >= min && password.length <= max;
}
复制代码
注意,max
参数是可选的。这意味着我们可使用两个或三个参数来调用isValidPasswordLength
:
isValidPasswordLength("open sesame", 6, 128); // true
isValidPasswordLength("open sesame", 6, 8); // false
复制代码
密码 "open sesame"
的长度为10
个字符。所以,对于长度范围 [6,128]
返回 true
,对于长度范围[6,8]
返回false
,到目前为止,一切 ok。
若是调用isValidPasswordLength
且不提供max
参数值,那么当密码长度超过 min
值时,我们可能但愿返回 true
。然而,事实并不是如此:
isValidPasswordLength("open sesame", 6); // false
复制代码
这里的问题在于 <= max
比较。若是max
是 undefined
,那么 <= max
的值永远都为false
。在这种状况下,isValidPasswordLength
将永远不会返回true
。
在 TypeScript 2.2 中,表达式password.length <= max
是不正确的类型,若是你的应用程序正在严格的null
检查模式下运行:
function isValidPasswordLength(
password: string,
min: number,
max?: number
) {
return password.length >= min && password.length <= max; // Error: 对象可能为“未定义”.
}
复制代码
若是操做数的类型是
null
或undefined
或者包含null
或undefined
的联合类型,则操做数视为可空的。
注意:包含
null
或undefined
的联合类型只会出如今--strictNullChecks
模式中,由于常规类型检查模式下null
和undefined
在联合类型中是不存在的。
那么要怎么修正这个问题呢?一种的解决方案是为max
参数提供一个默认值,它只在传递undefined
时起做用。这样,该参数仍然是可选的,但始终包含类型为number
的值
function isValidPasswordLength(
password: string,
min: number,
max: number = Number.MAX_VALUE
) {
return password.length >= min && password.length <= max;
}
复制代码
固然我们也能够选择其余的方法,可是我以为这个方法很好。只要再也不将max
与undefined
的值进行比较,就能够了
TypeScript 的一个目的是支持不一样框架和库中使用的通用 JS 模式。从TypeScript 2.2开始,增长了对 ES6 混合类(mixin class)模式。接下来说讲 mixin
是什么,而后举例说明了如何在 TypeScript 中使用它们。
混合类是实现不一样功能方面的类。其余类能够包含 mixin
并访问它的方法和属性。这样,mixin
提供了一种基于组合行为的代码重用形式。
混合类指一个extends
(扩展)了类型参数类型的表达式的类声明或表达式. 如下规则对混合类声明适用:
extends
表达式的类型参数类型必须是混合构造函数.any[]
的变长参数, 而且必须使用展开运算符在super(...args)
调用中将这些参数传递。定义完成以后,来研究一些代码。下面是一个 Timestamped
函数,它在timestamp
属性中跟踪对象的建立日期:
type Constructor<T = {}> = new (..args: any[]) => T;
function Timestamped<TBase extends Constructor>(Base: TBase) {
return class extends Base {
timestamp = Date.now()
}
}
复制代码
这看起来有点复杂,我们一行一行来看看:
type Constructor<T = {}> = new (..args: any[]) => T;
复制代码
type Constructor <T>
是构造签名的别名,该签名描述了能够构造通用类型T
的对象的类型,而且其构造函数接受任意数量的任何类型的参数。
接下来,让咱们看一下mixin
函数自己:
function Timestamped<TBase extends Constructor>(Base: TBase) {
return class extends Base {
timestamp = Date.now();
};
}
复制代码
Timestamped
函数接受一个名为Base
的参数,该参数属于泛型类型 TBase
。注意,TBase
必须与Constructor
兼容,即类型必须可以构造某些东西。
在函数体中,我们建立并返回一个派生自Base
的新类。这种语法乍一看可能有点奇怪。我们建立的是类表达式,而不是类声明,后者是定义类的更经常使用方法。我们的新类定义了一个timestamp
的属性,并当即分配自UNIX时代以来通过的毫秒数。
注意,从mixin
函数返回的类表达式是一个未命名的类表达式,由于class
关键字后面没有名称。与类声明不一样,类表达式没必要命名。我们能够选择添加一个名称,它将是类主体的本地名称,并容许类引用本身
function Timestamped<TBase extends Constructor>(Base: TBase) {
return class Timestamped extends Base {
timestamp = Date.now();
};
}
复制代码
如今已经介绍了两个类型别名和mixin
函数的声明,接下来看看如何在另外一个类中使用 mixin
:
class User {
name: string;
constructor(name: string) {
this.name = name;
}
}
// 经过将`"Timestamped"`混合到"User"中建立一个新类
const TimestampedUser = Timestamped(User);
// 实例化新的 "TimestampedUser" 类
const user = new TimestampedUser("前端小智")
// 如今,我们能够同时从User 类中访问属性
// 也能够从 Timestamped 类中访问属性
console.log(user.name);
console.log(user.timestamp);
复制代码
TypeScript 编译器知道咱们在这里建立并使用了一个mixin
,一切都是彻底静态类型的,而且会自动完成和重构。
如今,看看一个稍微高级一点的 mixin
,类中定义一个构造函数
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";
复制代码
到目前为止,我们只在mixin
中添加了数据属性。如今来看看另外一个 mixin
,它额外实现了两个方法:
fucntion Activatable<TBase extends Constructor>(Base: TBase) {
return class extends Base {
isActivated = false;
activate() {
this.isActivated = true;
}
deactivate() {
this.isActivated = false;
}
}
}
复制代码
我们从mixin
函数返回一个常规的类。这意味着我们可使用全部受支持的类功能,例如构造函数,属性,方法,getter/setter
,静态成员等。
如何所示,我们如何在 User
类中使用混合的 Activatable
:
const ActivatableUser = Activatable(User);
// 实例化新的"ActivatableUser"类
const user = new ActivatableUser("John Doe");
//初始化,isActivated 的值为 false
console.log(user.isActivated);
user.activate();
console.log(user.isActivated); // true
复制代码
组合的mixin,可让它更加灵活。一个类能够包含任意多的mixin
,为了演示这点,我们把上面提到的全部mixin
代码组合在一块儿。
const SpecialUser = Activatable(Tagged(Timestamped(User)));
const user = new SpecialUser("John Doe");
复制代码
固然 SpecialUser
类不必定很是有用,但关键是,TypeScript
静态地理解这种mixin
组合。编译器能够类型检查全部的使用,并在自动完成列表中建议可用的成员:
与类继承进行对比,有个区别:一个类只能有一个基类。继承多个基类在 JS 中不行的,所以在 TypeScript
中也不行。
原文: mariusschulz.com/blog/null-c… mariusschulz.com/blog/mixin-…
代码部署后可能存在的BUG无法实时知道,过后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给你们推荐一个好用的BUG监控工具 Fundebug。
干货系列文章汇总以下,以为不错点个Star,欢迎 加群 互相学习。
我是小智,公众号「大迁世界」做者,对前端技术保持学习爱好者。我会常常分享本身所学所看的干货,在进阶的路上,共勉!
关注公众号,后台回复福利,便可看到福利,你懂的。