TypeScript 4.4 重点:
- Control Flow Analysis of Aliased Conditions and Discriminants
- Symbol and Template String Pattern Index Signatures
- Defaulting to the unknown Type in Catch Variables (—useUnknownInCatchVariables)
- Exact Optional Property Types (—exactOptionalPropertyTypes)
- Class static Blocks
- tsc—help Updates and Improvements
- Inlay Hints
Control Flow Analysis of Aliased Conditions and Discriminants
对于条件别名和判别的流分析
通常我们会使用类型守卫(type guards)来确认它的类型:
function foo(arg: unknown) {
if (typeof arg === "string") {
// We know 'arg' is a string now.
console.log(arg.toUpperCase());
}
}
但如果我们将判断条件移出并定义为argIsString
变量?
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的版本中,则不会报错了。如果类型守卫使用 const
或 readonly
属性,或一个不可改变的参数,TypeScript会合理的缩小其值。并且不仅仅是 typeof
检查,其他类型守卫的值也会保留。
type Shape =
| { kind: "circle", radius: number }
| { kind: "square", sideLength: number };
function area(shape: Shape): number {
const isCircle = shape.kind === "circle";
if (isCircle) {
// 这里是 circle
return Math.PI * shape.radius ** 2;
}
else {
// 剩下的是 square
return shape.sideLength ** 2;
}
}
甚至可以再深入判断,提取了字段也能缩小原来对象的类型判断:
type Shape =
| { kind: "circle", radius: number }
| { kind: "square", sideLength: number };
function area(shape: Shape): number {
// 先提前 'kind'
const { kind } = shape;
if (kind === "circle") {
return Math.PI * shape.radius ** 2;
}
else {
return shape.sideLength ** 2;
}
}
Symbol and Template String Pattern Index Signatures
支持 symbol类型和模板字符串类型的索引签名
使用索引允许我们定义像词典那样的对象。
// 字符串:key为string,value为boolean
interface BooleanDictionary {
[key: string]: boolean;
}
declare let myDict: BooleanDictionary;
// Valid to assign boolean values
myDict["foo"] = true;
myDict["bar"] = false;
// Error, "oops" isn't a boolean
myDict["baz"] = "oops";
// 数字
interface Array<T> {
[index: number]: T;
// ...
}
let arr = new Array<string>();
// Valid
arr[0] = "hello!";
// Error, expecting a 'string' value here
arr[1] = 123;
但是目前索引被限制为 string 或 number 类型,意味着不能使用symbol,也不能使用一些字符串的子集,比如data-
开头的文字:
在4.4的版本中,支持symbol类型和模板字符串类型:
// 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'.
// template string pattern type
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'.
};
最后,索引类型也支持联合类型,只要类型是由这些类型组成的:
- string
- number
- symbol
- template string patterns (e.g.
hello-${string}
) ```typescript interface Data {
[optName: string | symbol]: any;
}
// 等同于
interface Data {
[optName: string]: any;
[optName: symbol]: any;
}
<a name="CqVsM"></a>
## Defaulting to the unknown Type in Catch Variables (--useUnknownInCatchVariables)
tsconfig<br />在过去,throw...catch 语句中,catch 返回的err被声明为any:
```typescript
try {
// Who knows what this might throw...
executeSomeThirdPartyCode();
}
catch (err) { // err: any
console.error(err.message); // Allowed, because 'any'
err.thisWillProbablyFail(); // Allowed, because 'any' :(
}
TypeScript 4.4 添加 --useUnknownInCatchVariables
,开启该配置使默认的any类型变为unknown类型:
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);
}
}
该配置在 --strict
开启后,自动打开。
除非你在catch里明确声明any类型:
try {
executeSomeThirdPartyCode();
}
catch (err: any) {
console.error(err.message); // Works again!
}
Exact Optional Property Types (—exactOptionalPropertyTypes)
tsconfig
一些可选属性能被明确定义为 undefined:
interface Person {
name: string,
age?: number;
}
// 等价于
interface Person {
name: string,
age?: number | undefined;
}
// 意味着age可以被定义为undefined
const p: Person = {
name: "Daniel",
age: undefined, // This is okay by default.
};
TypeScript 4.4 添加 --exactOptionalPropertyTypes
,约定可选属性应该完全按照所写的解释,意味着 | undefined
没有被添加到类型中:
// With 'exactOptionalPropertyTypes' on:
const p: Person = {
name: "Daniel",
age: undefined, // Error! undefined isn't a number
};
该配置不在--strict family
中,同时要求--strictNullChecks
配置打开。
static Blocks in Classes
class Foo {
static #count = 0;
get count() {
return Foo.#count;
}
static {
try {
const lastInstances = loadLastInstances();
Foo.#count += lastInstances.length;
}
catch {}
}
}
tsc —help Updates and Improvements
tsc —help 更新与优化
更新编译器选项的描述,用颜色和其他视觉分离重新设置 —help 菜单的样式:
Inlay Hints
inlay hints 描述更多的信息:
参考: