Typescript 4.4 is officially released! There are still three months away from the release of Typescript 4.5, so hurry up and learn!
Intensive articles this week: announcing-typescript-4-4
Overview
Smarter automatic type narrowing
The type narrowing function is very convenient. It allows Typescript to automatically and intelligently determine the type like Js as much as possible, thereby avoiding the work of type definition and making your Typescript write more like Js.
In fact, this feature has long been in our Intensive "Typescript2.0 - 2.9" has been introduced, was using the term automatic type inference, this time with a more precise automatic type narrowed the word, because only Type narrowing is safe, such as:
function foo(arg: unknown) {
if (typeof arg === "string") {
// We know 'arg' is a string now.
console.log(arg.toUpperCase());
}
}
In the version before Typescript 4.4, if we assign this judgment to a variable and use it in the if
branch, the type cannot be narrowed normally:
function foo(arg: unknown) {
const argIsString = typeof arg === "string";
if (argIsString) {
console.log(arg.toUpperCase());
// ~~~~~~~~~~~
// Error! Property 'toUpperCase' does not exist on type 'unknown'.
}
}
This problem has been solved in Typescript 4.4. In fact, this type of narrowing judgment logic has been deepened, that is, the judgment can take effect no matter where it is written. Therefore, the following deconstruction usage judgment can also infer the type narrowing:
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;
}
}
Not only a single judgment, Typescript 4.4 also supports compound type deduction:
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
to true
branch means inputA
, inputB
were narrowed to string
type.
This kind of in-depth judgment is also reflected in the recalculation of a variable with type judgment, and the generated variable also has a type judgment function:
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'.
}
}
As you can see, we can almost write Typescript like Js, and 4.4 supports most of the intuitive derivation is very convenient. But it’s important to note that Typescript
After all, it is not at runtime, so more thorough automatic inference cannot be done, but it is sufficient to support most scenarios.
Subscript supports Symbol and template string type judgment
Originally we defined an object accessed with a subscript like this:
interface Values {
[key: string]: number
}
Symbol pull is now also supported:
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'.
And it also supports type matching for specific string templates. For example, if you want the data-
be an independent type, you can define it like this:
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'.
};
This is very helpful data-
At the same time, it also supports joint type definition. The following two type definition methods are equivalent:
interface Data {
[optName: string | symbol]: any;
}
// Equivalent to
interface Data {
[optName: string]: any;
[optName: symbol]: any;
}
Stricter error trap type
Before the unknown
type came out, Typescript used any
as the default type for throwing errors. After all, no one knows what type of error is thrown:
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... This sentence is very interesting. Runtime errors may occur anywhere in a function. This is not something static analysis can solve at all, so it is impossible to automatically infer the error type, so you can only use any
.
--useUnknownInCatchVariables
or --strict
mode of Typescript 4.4, unknown
as the default type of the error caught.
Compared with the non-existent type never
, unknown
just doesn't know what type it is, so it cannot be any
, but we can deduce it to any type at will:
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);
}
}
If you find it troublesome, you can also re-declare the type as any
:
try {
executeSomeThirdPartyCode();
}
catch (err: any) {
console.error(err.message); // Works again!
}
But this is actually not appropriate, because even considering runtime factors, theoretically unexpected errors may still occur, so type inference that is too confident about errors is not appropriate. It is best to keep its unknown
type for all possible Deal with boundary conditions.
Explicit optional attributes
There is an ambiguity in the type description of the optional attributes of the object, such as:
interface Person {
name: string,
age?: number;
}
In fact, Typescript defines its types as follows:
interface Person {
name: string,
age?: number | undefined;
}
Why is it defined this way? Because in many cases, there is no such key, which is equivalent to the performance of undefined
But for example Object.keys
scene are not equivalent, so theoretically age?: number
is: either there is no age
, or there is age
and the type is number
, which means that the following writing should be wrong:
// With 'exactOptionalPropertyTypes' on:
const p: Person = {
name: "Daniel",
age: undefined, // Error! undefined isn't a number
};
In Typescript 4.4, open both --exactOptionalPropertyTypes
and --strictNullChecks
to take effect.
It is reasonable to think about it carefully. Since the defined type is not undefined
, even if the object is an optional type, the assignment of undefined
cannot be considered reasonable, because age?: number
is that either there is no such key or there is but the type is number
. So when Object.keys
finds age
, the value should be number
.
Support Static Block
Typescript 4.4 supports class static blocks , and private variables can be accessed within the scope of the code block.
There are also some miscellaneous performance improvements and experience optimizations that are not listed one by one. If you are interested, you can directly see the original document: perf-improvements .
Summarize
It can be seen from the features of Typescript 4.4 that Typescript is making efforts in the direction of "more native JS affinity", which will undoubtedly make Typescript more and more usable.
More interested in the new features, you can view Typescript 4.5 release plan .
The discussion address is: Intensive Reading of "Typescript 4.4" · Issue #348 · dt-fe/weekly
If you want to participate in the discussion, please click here , a new theme every week, weekend or Monday. Front-end intensive reading-to help you filter reliable content.
Follow front-end intensive reading WeChat public
<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">
Copyright notice: Freely reprinted-non-commercial-non-derivative-keep the signature ( Creative Commons 3.0 License )
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。