Give priority to personal learning and facilitate others 😉
Starting address: https://github.com/any86/ts-l... The content of this article will also be updated continuously
🔥 Reading instructions
Due to the limited personal ability, this paper only filters the knowledge points related to type / syntax from the "typescript update log". The versions before 3.1 are some basic knowledge, so only some contents are extracted If there are any mistakes, please give more advice and help
Note: changes in type inference (relaxation / narrowing) and configuration items, as well as new syntax selections in ECMA
v4.5
Add wait type
Gets the type of the resolve value of prmove
// Promise<number> const p = Promise.resolve(123); // Awaited<Promise<number>> === number type B = Awaited<typeof p>; // Type parameter is not Promise type, // Then do not process and return directly type S = Awaited<string>; // string
Import name modifier "type"
The previous version supported the syntax of "import type {xx} from 'xxx'". Now it supports marking a single import item with "type"
import { someFunc, type BaseType } from "./some-module.js";
Check whether the private property of the class exists
Synchronous compatible ecma syntax
class Person { #name: string; constructor(name: string) { this.#name = name; } equals(other: unknown) { return other && typeof other === "object" && #name in other && // <- 🔥 New grammar this.#name === other.#name; } }
Import assertion
Synchronization is compatible with ecma syntax. It makes runtime judgment on the imported file, and ts does not make any judgment
import obj from "./something.json" assert { type: "json" };
And the syntax of the "import" function:
const obj = await import("./something.json", { assert: { type: "json" }, });
v4.4
More intelligent type protection
Common types of protection are as follows:
function nOrs() { return Math.random() > 0.5 ? 123 : "abc"; } let input = nOrs(); if (typeof input === "number") { input++; }
If typeof input === 'number' is abstracted into a variable, type protection will fail before version 4.4, but ts can be protected correctly in version 4.4
function nOrs() { return Math.random() > 0.5 ? 123 : "abc"; } const input = nOrs(); const isNumber = typeof input === "number"; if (isNumber) { // Invalid, cannot know input is of type number input++; }
Note: the required protected variables must be "const variables" or "realonly attributes", such as the input above and the "n" attribute below
interface A { readonly n: number | string; } const a: A = { n: Math.random() > 0.5 ? "123" : 321 }; const isNumber = typeof a.n === "number"; if (isNumber) { // r is number const r = a.n; }
Deeper type protection
The range of union types can be narrowed through attribute judgment
type Shape = | { kind: "circle"; radius: number } | { kind: "square"; sideLength: number }; function area(shape: Shape): number { const isCircle = shape.kind === "circle"; if (isCircle) { // 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; } }
âš¡ Add a key that supports symbol type as object type
Previously, only "string | number" was supported, resulting in incomplete description of Object type keys. Now it is solved
interface Test1 { [k: string | number | symbol]: any; } type Test2 = { [k in string | number | symbol]: any; };
static block in class
Synchronization supports es new syntax
class Foo { static #count = 0; get count() { return Foo.#count; } static { try { const lastInstances = loadLastInstances(); Foo.#count += lastInstances.length; } catch {} } }
v4.3
override keyword
"override" is the syntax provided to the class to mark whether the attribute / method in the subclass overrides the attribute / method with the same name of the parent class
class A {} class B extends A { // Hint: override cannot be used because there is no "a" field in the base class override a() {} }
@ link in comment
Click to jump to the specified code
/** * To be called 70 to 80 days after {@link plantCarrot}. */ function harvestCarrot(carrot: Carrot) {} /** * Call early in spring for best results. Added in v2.1.0. * @param seed Make sure it's a carrot seed! */ function plantCarrot(seed: Seed) { // TODO: some gardening }
v4.2
Yuanzu supports optional symbols
let c: [string, string?] = ["hello"]; c = ["hello", "world"];
Primitive ancestor type definitions support the use of '...' anywhere
However, optional elements (?) are not allowed in the tail And "..." appear
let foo: [...string[], number]; foo = [123]; foo = ["hello", 123]; foo = ["hello!", "hello!", "hello!", 123]; let bar: [boolean, ...string[], boolean]; bar = [true, false]; bar = [true, "some text", false]; bar = [true, "some", "separated", "text", false];
Error example
let StealersWheel: [...Clown[], "me", ...Joker[]]; // A rest element cannot follow another rest element. let StringsAndMaybeBoolean: [...string[], boolean?]; // An optional element cannot follow a rest element.
v4.1
Template string type
The syntax is the same as that of ` ` 'in es6, except that it is used to package types:
type World = "world"; // hello world type Greeting = `hello ${World}`; type Color = "red" | "blue"; type Quantity = "one" | "two"; // "one fish" | "two fish" | "red fish" | "blue fish" type SeussFish = `${Quantity | Color} fish`;
New string type
The ts system adds Uppercase / Lowercase / Capitalize / Uncapitalize types
// ABC type S1 = Uppercase<"abc">; // abc type S2 = Lowercase<"ABC">; // Abc type S3 = Capitalize<"abc">; // aBC type S4 = Uncapitalize<"ABC">;
Use as in the key in structure
After obtaining the key, it is used to obtain the value, but not the key, only the value, and then re label the key
type PPP<T> = { [K in keyof T as "ww"]: T[K]; }; // type A = {ww:1|'2'} type A = PPP<{ a: 1; b: "2" }>;
v4.0
The rest term of Yuanzu can be deduced accurately
Previously, get the type of tuple except the first element:
function tail<T extends any[]>(arr: readonly [any, ...T]) { const [_ignored, ...rest] = arr; return rest; } const myTuple = [1, 2, 3, 4] as const; const myArray = ["hello", "world"]; const r1 = tail(myTuple); // const r1: [2, 3, 4] const r2 = tail([...myTuple, ...myArray] as const); // const r2: [2, 3, 4, ...string[]]
rest elements can appear anywhere in tuples, not just at the end!
It requires "..." The following element must be a tuple, and only the last element can be "..."+ Array
type Strings = [string, string]; type Numbers = [number, number]; type StrStrNumNumBool = [...Strings, ...Numbers, boolean]; type StrStrNumNumBool = [...[string], string, ...string[]];
Tuple elements support labeling
It is mainly to be compatible with the definition of function parameters, because function parameters have parameter names
type Range = [start: number, end: number];
The assignment of constructor in class can directly deduce the type of attribute
When defining attributes, you can omit the type annotation
class A { // At this point, a is directly deduced to be of type number // Type can be omitted, a cannot be omitted a; constructor() { this.a = 1; } }
The parameter of catch in try/catch becomes unknown
Judgment or assertion is required before use
try { // ... } catch (e) { e; // unknown }
/*_ @deprecated _/
Label deprecated
v3.9
// @ts-expect-error
It is used to shield errors. Different from the motivation of "/ / @ TS ignore", the main use scenario of "/ / @ TS expect error" is that you deliberately make errors, such as test cases
function doStuff(abc: string, xyz: string) { assert(typeof abc === "string"); assert(typeof xyz === "string"); } // You want to test the incoming digital parameters, but ts will automatically infer errors, which is not what you want, so add "/ / @ ts expect error" expect(() => { // @ts-expect-error doStuff(123, 456); }).toThrow();
v3.8
import type / export type
New syntax added for type import and export only.
import type { SomeThing } from "./some-module.js"; export type { SomeThing };
Private property '#' of class
Unlike the private modifier, # the private attribute syntax of synchronous ecma cannot be accessed outside the class even after it is compiled into js
class Person { #name: string; constructor(name: string) { this.#name = name; } greet() { console.log(`Hello, my name is ${this.#name}!`); } }
export * as ns
Export syntax, synchronized with ecma2020 syntax
export * as utilities from "./utilities.js";
await keyword for top-level scope
The syntax synchronized with ecma must be used within the module, so there must be at least one "empty export {}"
const response = await fetch("..."); const greeting = await response.text(); console.log(greeting); // Make sure we're a module export {};
Another limitation is the configuration item: "target" is above es2017 and "module" is "esnext"
v3.4
readonlyArray
Read only array
function foo(arr: ReadonlyArray<string>) { arr.slice(); // okay arr.push("hello!"); // error! }
readonly T[]
Same effect as readonlyArray
function foo(arr: readonly string[]) { arr.slice(); // okay arr.push("hello!"); // error! }
readonly Yuanzu
function foo(pair: readonly [string, string]) { console.log(pair[0]); // okay pair[1] = "hello!"; // error }
const assertion
After using const assertion, the inferred types are "immutable"
// Type '"hello'", the value of x cannot be modified let x = "hello" as const; // Type 'readonly [10, 20]' let y = [10, 20] as const; // Type '{ readonly text: "hello" }' let z = { text: "hello" } as const;
You can also write assertions using cusps:
// Type '"hello"' let x = <const>"hello"; // Type 'readonly [10, 20]' let y = <const>[10, 20]; // Type '{ readonly text: "hello" }' let z = <const>{ text: "hello" };
Note that const assertions can only be applied immediately to simple literal expressions.
// Error! A 'const' assertion can only be applied to a // to a string, number, boolean, array, or object literal. let a = (Math.random() < 0.5 ? 0 : 1) as const; let b = (60 * 60 * 1000) as const; // Works! let c = Math.random() < 0.5 ? (0 as const) : (1 as const); let d = 3_600_000 as const;
v3.2
bigint type
bigint is part of ECMAScript's upcoming proposal
let foo: bigint = BigInt(100); // the BigInt function let bar: bigint = 100n; // a BigInt literal
v3.1
typesVersions
A project supports multiple declaration files. For example, if the following version is greater than 3.1, go to the "ts3.1" folder under the project to find the declaration file
// tsconfig.js { "typesVersions": { ">=3.1": { "*": ["ts3.1/*"] } ">=3.2": { "*": ["ts3.2/*"] } } }
v2.9
import type
New -- declarationMap
With -- Declaration enabled together with -- Declaration map, the compiler is generating d. TS will be generated at the same time d.ts.map. Language services can now understand these map files and map the declaration files to the source code.
In other words, generated after -- declarationMap is enabled d. Clicking go to definition in the TS file will navigate to the location (. TS) in the source file instead of the. d.ts file.
v2.8
-/+
Yes, readonly /? Modifier to control addition and deletion
type MutableRequired<T> = { -readonly [P in keyof T]-?: T[P] }; // Remove readonly and? type ReadonlyPartial<T> = { +readonly [P in keyof T]+?: T[P] }; // Add readonly and?
v2.7
let x!: number[];
Initializing variables directly asserts that they have been assigned
// Without this!, It will prompt "operation cannot be performed before assignment" let x!: number[]; // The assignment ts of this function is undetectable, so "!" is used in the previous line initialize(); x.push(4); function initialize() { x = [0, 1, 2, 3]; }
v2.6
Chinese tsc
tsc --locale zh-cn
The following command line prompts will be displayed in Chinese
@ts-ignore
@The TS ignore comment hides the next line of error
if (false) { // @TS ignore: error in code that cannot be executed console.log("hello"); }
v2.5
You can omit parameters in try/catch
let input = "..."; try { JSON.parse(input); } catch { // ^Note that our 'catch' statement does not declare a variable console.log("Incoming JSON wrongful\n\n" + input); }
v2.4
For dynamic import, many packaging tools already support import asynchronous loading modules
Note: you need to set the target module in tsconfig to "esnext"
async function getZipFile(name: string, files: File[]): Promise<File> { const zipUtil = await import("./utils/create-zip-file"); const zipContents = await zipUtil.getContentAsBlob(files); return new File(zipContents, name); }
v2.3
@ts-nocheck
Declare that the file does not detect the type through the "/ / @ TS nocheck" annotation
// @ts-nocheck let a; // It should have indicated that a was not assigned a.push(123);
v2.0
Quick external module declaration
When you use a new module, if you don't want to spend time writing a declaration, now you can use the shortcut declaration to get started quickly.
// xxx.d.ts declare module "hot-new-module";
All imports are of type any
// x. Y is any import x, { y } from "hot-new-module"; x(y);
Wildcards in module names
This vue initializes the project, which is used to declare A vue file is a component
declare module "*.vue" { import { DefineComponent } from "vue"; // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types const component: DefineComponent<{}, {}, any>; export default component; }
also:
declare module "*!text" { const content: string; export default content; } // Some do it the other way around. declare module "json!*" { const value: any; export default value; }
Wechat group
Thank you for reading. If you have any questions, you can add me wechat and I'll pull you into the wechat group (because Tencent has a limit of 100 people on the wechat group, more than 100 people must be pulled in by the group members)
github
My personal open source is based on ts, welcome to visit https://github.com/any86