Variadic Tuple Types
Older versions of Typescript implement a concat method that looks like this:
function concat<T.U> (arr1: T[], arr2: U[]) {
return [...arr1, ...arr2]
}
Copy the code
This way concat method return value type is a joint types of arrays (T | U) []
declare const arr1: number[]
declare const arr2: string[]
// type: (string | number)[]
const arr = concat(arr1, arr2)
Copy the code
What if you want to concat primitives
declare const arr1: [number.string]
declare const arr2: [boolean]
// type: (string | number | boolean)[]
const arr = concat(arr1, arr2)
Copy the code
The primitive has a specific length and element type. The return type of the above example is obviously imprecise. The expected return type is [number, string, Boolean].
To achieve the desired results, you could only write overloads in older versions of TS
function concat<T.U.V> (arr1: [T, U], arr2: [V]) :T.U.V]
function concat<T.U> (arr1: T[], arr2: U[]) {
return [...arr1.arr2]}Copy the code
But if the length of the incoming progenitors is uncertain, we have to keep writing overloads to cover all cases as best we can, which is obviously unacceptable.
TypeScript 4.0 brings with it two fundamental changes and improvements in inference.
One change is that the paradigm can be used to extend operators. This means that you can declare a mutable progenitor using a stereotype.
This allows you to implement a type that supports better concat functions
function concat<T extends unknown[].U extends unknown[] > (t: [...T], u: [...U]) : [...T.U] {
return [...t, ...u];
}
declare const arr1: [string.number]
declare const arr2: string[]
declare const arr3: ['hello']
concat(arr1, arr2) // [string, number, ...string[]]
concat(arr1, arr3) // [string, number, 'hello']
Copy the code
Another change is that rest arguments in older versions of Typescript only support array types and must be placed at the end of a meta-ancestor. Now you can put it anywhere in the original.
// Older versions of ts
type t1 = [...string[]]
type t2 = [...[string.number]] // error: A rest element type must be an array type.
type t3 = [...string[], string] // A rest element must be last in a tuple type
Copy the code
Array types of indefinite length use the extension operator. If they are not placed last, all subsequent elements are inferred as the type of the array element and the combined type of the subsequent element types
type t1 = string[]
type t2 = [boolean. t1,number] // [boolean, ...(string | number)[]]
Copy the code
Labeled Tuple Elements (Labeled Tuple Elements)
If we were to create an icon, we might have the following implementation
function createIcon(url: string, size: [number.number]) {
const [width, height] = size
return new BMapGL.Icon(url, new BMapGL.Size(width, height));
}
Copy the code
The size argument is a meta-ancestor type, but when we call createIcon, we only know the type of size, not the meaning of each element. At call time, you also need to jump to the function body to see the meaning of each element of size.
In Typescript 4.0, meta-ancestor elements can be tagged. The createIcon method in the above example can be implemented like this:
function createIcon(url: string, size: [width: number, height: number]) {
const [width, height] = size
return new BMapGL.Icon(url, new BMapGL.Size(width, height));
}
Copy the code
This allows you to see the meaning of each element of the size parameter at call time
Some rules of use
When you mark a tuple element, you must also mark all other elements in the tuple.
type Size = [width: number.number] // error: Tuple members must all have names or all not have names.
Copy the code
You don’t need to name variables differently when you deconstruct tags. In the following example, the variable name does not need to use width and height when deconstructing the size parameter
function createIcon(url: string, size: [width: number, height: number]) {
const [w, h] = size
return new BMapGL.Icon(url, new BMapGL.Size(w, h));
}
Copy the code
Overloading can be achieved using tagged meta-ancestors
Class Property Inference from Constructors
For example, in older versions of Typescript, when noImplicitAny is enabled, the defined instance properties area and sideLength report errors. Because it does not explicitly declare a type, it is inferred to be any.
class Square {
area;
sideLength;
constructor(sideLength: number) {
this.sideLength = sideLength;
this.area = sideLength ** 2; }}Copy the code
In Typescript 4.0, the type of this instance attribute is inferred from the constructor function, and both area and sideLength are inferred to be of type number without error.
Typescript cannot infer the type of a class instance property if the initialization is not written in the constructor function.
class Square {
// error: Member 'sideLength' implicitly has an 'any' type.
sideLength;
constructor(sideLength: number) {
this.initialize(sideLength)
}
initialize(sideLength: number) {
this.sideLength = sideLength; }}Copy the code
Need to display a statement at this time the type of instance attributes, but if strictPropertyInitialization options (check has been declared but not in the constructor sets the class attribute) also need to display the assignment assertions to type system identification
class Square { sideLength! :number;
constructor(sideLength: number) {
this.initialize(sideLength)
}
initialize(sideLength: number) {
this.sideLength = sideLength; }}Copy the code
Short-circuiting Assignment Operators (Short-circuiting Assignment Operators)
Among the new features in ES2021 is a proposal for Logical Assignment Operators.
When variable A is truthy, set its value to b, which is equivalent to a = a && b
a &&= b;
Copy the code
When the variable a is falsy, set it to b, that is equivalent to a = a | | b
a ||= b;
Copy the code
When variable A is nullish, set it to B, i.e., a = a?? B.
?? The Nullish Coalescing operator is a new feature of ES2020
// set a to b only when a is nullisha ?? = b;Copy the code
Typescript 4.0 supports these features
Catch Clause variables can be declared as unknown (Unknown on catch Clause Bindings).
In older versions of Typescript, variables in a catch clause have type any and cannot be declared as other types
try{}catch(err: unknown) { // error: Catch clause variable cannot have a type annotation.
}
Copy the code
Declaring this variable unknown is supported in Typescript version 4.0, and this example will not fail.
This is done because the any type is compatible with all other types, and as in the example above, any operation on the ERR variable will not report a type error. Unknown is safer than any because it reminds us to do some type checking before we can manipulate the value.
try{}catch(err: unknown) {
if (err instanceof Error) {
console.error(err.message)
}
}
Copy the code
Customize the JSX Fragment factory function
Older versions of Typescript already support custom JSX factory functions, which can be customized with the jsxFactory option.
In Typescript 4.0, you can customize Fragment factory functions with the new jsxFragmentFactory option.
The following tsconfig.json configuration tells TypeScript to convert JSX in a react-compatible way, but to switch each factory function to H instead of react.createElement. And use a Fragment instead of react. Fragment.
Use the following tsconfig.json configuration
{
"compilerOptions": {
"target": "esnext"."module": "commonjs"."jsx": "react"."jsxFactory": "h"."jsxFragmentFactory": "Fragment"}}Copy the code
Compile the following code
import { h, Fragment } from "preact";
let stuff = <>
<div>Hello</div>
</>;
Copy the code
The output
"use strict";
exports.__esModule = true;
/ * *@jsx h */
/ * *@jsxFrag Fragment */
var preact_1 = require("preact");
var stuff = preact_1.h(preact_1.Fragment, null,
preact_1.h("div".null."Hello"));
Copy the code
The JSX factory function supports the /** @jsx */ comment to specify the JSX factory function used by the current file. Also, the Fragment factory function can be specified with the new /** @jsxfrag */ comment.
As follows, specify the JSX factory and Fragment factory functions used in the current file in the header of the file.
The way you specify it through an annotation has a higher priority than the one configured in the tsconfig.json file
/** @jsx h */
/** @jsxFrag Fragment */
import { h, Fragment } from "preact";
let stuff = <>
<div>Hello</div>
</>;
Copy the code
Major changes
lib.d.ts
Typescript 4.0 removes Document. origin, which only works in older versions of IE, while Safari MDN recommends self.origin instead.
As follows, accessing the Document origin property in Typescript version 4.0 will prompt that the property does not exist
document.origin // error: Property 'origin' does not exist on type 'Document'
Copy the code
If you want to use this property on older versions of IE, you need to set it explicitly
interface Document {
origin: string
}
console.log(document.origin)
Copy the code
Properties Overriding Accessors (and vice versa) is an Error
In older versions of Typescript, an error is reported only when the useDefineForClassFields option is used when instance attributes of a subclass override accessor attributes of a parent class.
class Base {
get foo() {
return 100;
}
set foo(val) {
// ... }}class Derived extends Base {
// Older versions of useDefineForClassFields error: 'foo' is defined as an accessor in class 'Base', but is overridden here in 'Derived' as an instance property.
foo = 10;
}
Copy the code
In Typescript version 4.0, an error is always reported when instance attributes of a subclass override accessor attributes of the parent class (or when accessor attributes of a subclass override instance attributes of the parent class), with or without the useDefineForClassFields option.
The operation object of delete must be optional
Typescript version 4.0 uses the delete operator when strictNullChecks is enabled, and the operation object must now be any, unknown, never, or optional (because it contains undefined in its type). Otherwise, using the DELETE operator will report an error.
interface Thing {
prop: string;
a: unknown;
b: any;
c: never;
d: undefined;
}
function f(x: Thing) {
delete x.prop; // error: The operand of a 'delete' operator must be optional.
delete x.a
delete x.b
delete x.c
delete x.d
}
Copy the code
The resources
- Announcing the TypeScript 4.0