Typescript 4.4 正式公布了!间隔 Typescript 4.5 公布还有三个月的工夫,放松上车学习吧!
本周精读的文章:announcing-typescript-4-4
概述
更智能的主动类型收窄
类型收窄性能十分不便,它能够让 Typescript 尽可能的像 Js 一样主动智能断定类型,从而防止类型定义的工作,让你的 Typescript 写得更像 Js。
其实这个性能早就有了,在咱们 精读《Typescript2.0 – 2.9》就曾经介绍过,过后用的名词是主动类型推导,这次用了更准确的主动类型收窄一词,因为只有类型收窄是平安的,比方:
function foo(arg: unknown) {if (typeof arg === "string") {
// We know 'arg' is a string now.
console.log(arg.toUpperCase());
}
}
而在 Typescript 4.4 之前的版本,如果咱们将这个断定赋值给一个变量,再用到 if
分支里,就无奈失常收窄类型了:
function foo(arg: unknown) {
const argIsString = typeof arg === "string";
if (argIsString) {console.log(arg.toUpperCase());
// ~~~~~~~~~~~
// Error! Property 'toUpperCase' does not exist on type 'unknown'.
}
}
这个问题在 Typescript 4.4 失去了解决,实际上是把这种类型收窄判断逻辑加深了,即无论这个判断写在哪都能够失效。所以上面这种解构的用法判断也能够推断出类型收窄:
type Shape =
| {kind: "circle", radius: number}
| {kind: "square", sideLength: number};
function area(shape: Shape): number {
// Extract out the 'kind' field first.
const {kind} = shape;
if (kind === "circle") {
// We know we have a circle here!
return Math.PI * shape.radius ** 2;
}
else {
// We know we're left with a square here!
return shape.sideLength ** 2;
}
}
不仅是繁多的判断,Typescript 4.4 还反对复合类型推导:
function doSomeChecks(
inputA: string | undefined,
inputB: string | undefined,
shouldDoExtraWork: boolean,
) {
const mustDoWork = inputA && inputB && shouldDoExtraWork;
if (mustDoWork) {
// We can access 'string' properties on both 'inputA' and 'inputB'!
const upperA = inputA.toUpperCase();
const upperB = inputB.toUpperCase();
// ...
}
}
mustDoWork
为 true
的分支就意味着 inputA
、inputB
均收窄为 string
类型。
这种深层的断定还体现在,一个具备类型判断的变量进行再计算,生成的变量还具备类型判断性能:
function f(x: string | number | boolean) {
const isString = typeof x === "string";
const isNumber = typeof x === "number";
const isStringOrNumber = isString || isNumber;
if (isStringOrNumber) {x; // Type of 'x' is 'string | number'.}
else {x; // Type of 'x' is 'boolean'.}
}
能够看到,咱们简直能够像写 Js 一样写 Typescript,4.4 反对了大部分合乎直觉的推导十分不便。但要留神的是,Typescript
毕竟不是运行时,无奈做到更彻底的主动推断,但足以反对绝大部分场景。
下标反对 Symbol 与模版字符串类型断定
本来咱们定义一个用下标拜访的对象是这样的:
interface Values {[key: string]: number
}
当初也反对 Symbol 拉:
interface Colors {[sym: symbol]: number;
}
const red = Symbol("red");
const green = Symbol("green");
const blue = Symbol("blue");
let colors: Colors = {};
colors[red] = 255; // Assignment of a number is allowed
let redVal = colors[red]; // 'redVal' has the type 'number'
colors[blue] = "da ba dee"; // Error: Type 'string' is not assignable to type 'number'.
而且对于特定的字符串模版也反对类型匹配,比方心愿以 data-
结尾的下标是一种独立类型,能够这么定义:
interface Options {
width?: number;
height?: number;
}
let a: Options = {
width: 100,
height: 100,
"data-blah": true, // Error! 'data-blah' wasn't declared in'Options'.
};
interface OptionsWithDataProps extends Options {
// Permit any property starting with 'data-'.
[optName: `data-${string}`]: unknown;
}
let b: OptionsWithDataProps = {
width: 100,
height: 100,
"data-blah": true, // Works!
"unknown-property": true, // Error! 'unknown-property' wasn't declared in'OptionsWithDataProps'.
};
这个对于 HTML 的 data-
属性十分有帮忙。
同时还反对联结类型定义,上面两种类型定义形式是等价的:
interface Data {[optName: string | symbol]: any;
}
// Equivalent to
interface Data {[optName: string]: any;
[optName: symbol]: any;
}
更严格的谬误捕捉类型
在 unknown
类型进去之前,Typescript 以 any
作为抛出谬误的默认类型,毕竟谁也不晓得抛出谬误的类型是什么:
try {
// Who knows what this might throw...
executeSomeThirdPartyCode();}
catch (err) { // err: any
console.error(err.message); // Allowed, because 'any'
err.thisWillProbablyFail(); // Allowed, because 'any' :(}
Who knows what this might throw… 这句话很有意思,一个函数任何中央都可能呈现运行时谬误,这基本不是动态剖析能够解决的,所以不可能主动推断谬误类型,所以只能用 any
。
在 Typescript 4.4 的 --useUnknownInCatchVariables
或 --strict
模式下都将以 unknown
作为捕捉到谬误的默认类型。
相比不存在的类型 never
,unknown
仅仅是不晓得是什么类型而已,所以不能像 any
一样当作任何类型应用,但咱们能够将其随便推断为任意类型:
try {executeSomeThirdPartyCode();
}
catch (err) { // err: unknown
// Error! Property 'message' does not exist on type 'unknown'.
console.error(err.message);
// Works! We can narrow 'err' from 'unknown' to 'Error'.
if (err instanceof Error) {console.error(err.message);
}
}
如果感觉这样做麻烦,也能够从新申明类型为 any
:
try {executeSomeThirdPartyCode();
}
catch (err: any) {console.error(err.message); // Works again!
}
但这样做其实并不适合,因为即使是思考了运行时因素,实践上还是可能发生意外谬误,所以对谬误过于自信的类型推断是不太适合的,最好放弃其 unknown
类型,对所有可能的边界状况做解决。
明确的可选属性
对象的可选属性在类型形容时有个含糊不清的中央,比方:
interface Person {
name: string,
age?: number;
}
其实 Typescript 对其的类型定义的是:
interface Person {
name: string,
age?: number | undefined;
}
为什么要这么定义呢?因为很多状况下,没有这个 key,与这个 key 的值为 undefined
的体现是等价的。但比方 Object.keys
场景下这两种体现却又不等价,所以实践上对于 age?: number
的确切表述是:要么没有 age
,要么有 age
且类型为 number
,也就是说上面的写法应该是谬误的:
// With 'exactOptionalPropertyTypes' on:
const p: Person = {
name: "Daniel",
age: undefined, // Error! undefined isn't a number
};
在 Typescript 4.4 中同时开启 --exactOptionalPropertyTypes
与 --strictNullChecks
即可失效。
认真想想这是正当的,既然定义的类型不是 undefined
,就算对象是可选类型,也不能认为赋值 undefined
是正当的,因为 age?: number
的心理预期是,要么没有这个 key,要么有然而类型为 number
,所以当 Object.keys
发现 age
这个 key 时,值就应该是 number
。
反对 Static Block
Typescript 4.4 反对了 class static blocks,并且在代码块作用域内能够拜访公有变量。
还有一些性能晋升与体验优化杂项就不一一列举了,感兴趣能够间接看原文档:perf-improvements。
总结
从 Typescript 4.4 个性能够看出,Typescript 正在往“更具备原生 JS 亲和性”方向作出努力,这无疑会使 Typescript 变得越来越好用。
对更多新个性感兴趣,能够 查看 Typescript 4.5 版本公布打算。
探讨地址是:精读《Typescript 4.4》· Issue #348 · dt-fe/weekly
如果你想参加探讨,请 点击这里,每周都有新的主题,周末或周一公布。前端精读 – 帮你筛选靠谱的内容。
关注 前端精读微信公众号
<img width=200 src=”https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg”>
版权申明:自在转载 - 非商用 - 非衍生 - 放弃署名(创意共享 3.0 许可证)