TypeScript 4.0 is officially released! Now is the best time to start using it

Author | Daniel Rosenwasser

Translator | Wang Qiang

Planning | Li Junchen

Today, Microsoft announced that the official version of TypeScript 4.0 is online! This new version has in-depth improvements in expressiveness, productivity and scalability, and is a new generation version of the TypeScript language.

If you are not familiar with TypeScript, here is a brief introduction: it is a language built on top of JavaScript by adding static typing syntax. Its basic idea is that after you write down the types of values ​​and where they are used, you can use TypeScript to type-check the code, and tell you about code errors before running the code (even before saving the file). Then, you can use the TypeScript compiler to strip the types from the code and provide you with concise and readable JavaScript code that can be run anywhere. In addition to type checking, TypeScript also uses static typing to support powerful editor tools such as auto-completion, code navigation, refactoring, etc. In fact, if you have used JavaScript in an editor such as Visual Studio Code or Visual Studio, then you have already used the experience of types and TypeScript. You can learn more about it on our website.

https://www.typescriptlang.org/

TypeScript 4.0 did not introduce particularly significant changes. In fact, if you are just getting started with this language, now is the best time to start using it. Its community is mature and perfect, and is constantly evolving, with runnable code and great new resources for learning. One more thing: Although we have introduced so many good things for 4.0, you actually only need to understand the basics of TypeScript to start producing applications!

If you already use TypeScript in your project, you can get it through NuGet, or use npm with the following command:

npm install -D typescript

You can also get editor support in the following ways:

  • Download Visual Studio 2019/2017:

    • https://marketplace.visualstudio.com/items?itemName=TypeScriptTeam.TypeScript-40

  • Install the internal version of Visual Studio Code, or follow the instructions below to use a newer version of TypeScript.

    • https://code.visualstudio.com/docs/typescript/typescript-compiling#_using-newer-typescript-versions

4.0 Journey 

TypeScript is a core part of the JavaScript technology stack for many people today. On npm, TypeScript achieved over 50 million monthly downloads for the first time in July! Although we know that there is always room for growth and improvement, it is clear that most developers who use TypeScript coding do like it. StackOverflow's latest developer survey ranks TypeScript as the second most popular language. In the latest JS status survey, approximately 89% of developers using TypeScript said they would use it again.

What is worth mentioning is the journey we have taken until today. In the previous two major versions, we reviewed some of the highlights that have shined over the years. For TypeScript 4.0, we will maintain this tradition.

Looking forward from version 3.0, you can see many dazzling changes, but TypeScript 3.0 itself has had a big impact. The unified tuple type and parameter list was a highlight at the time, and a large number of existing JavaScript modes could be enabled on functions. This release also provides project references to help expand, organize, and share the code base. One small change that had a major impact in version 3.0 was the introduction of a type-safe alternative to any, called unknown.

TypeScript 3.1 extends the capabilities of mapping types to handle tuple and array types, and greatly simplifies the process of attaching properties to functions without using TypeScript's exclusive runtime features (discontinued).

TypeScript 3.2 allows objects to propagate on generic types, and uses the features of 3.0 to better model the metaprogramming of functions through strict typing of bind, call, and apply. TypeScript 3.3 pays more attention to stability, but also improves the joint type method, and adds file incremental construction in the --build mode.

In version 3.4, we further support functional mode, better support immutable data structures, and improve the inference of higher-order generic functions. A big improvement in this release is the introduction of the --incremental flag, which avoids a complete rebuild every time TypeScript is run, thus speeding up compilation and type checking.

In TypeScript 3.5 and 3.6, some type system rules have been strengthened, and more intelligent compatibility checking rules have been brought.

TypeScript 3.7 is a very noteworthy version, because it combines many new type system features with ECMAScript features. In terms of type systems, we added recursive type alias references and support for assertion style functions, both of which are unique type system features. From a JavaScript perspective, this version brings optional chaining and null value merging functions, which are the two most anticipated features for TypeScript and JavaScript users.

Recently, 3.8 and 3.9 brought type-only import/export, as well as many ECMAScript features, such as private fields, top-level await in modules, and new export* syntax. These versions also bring performance and scalability optimizations.

We haven't mentioned the work on language services, infrastructure, websites, and other core projects, which are critical to the TypeScript experience. In addition to the core team’s projects, we also have a very good community of contributors in the ecosystem. They promote the continuous improvement of the experience and provide help through DefinitelyTyped and even TypeScript itself. At the beginning of 2012, DefinitelyTyped had only 80 pull requests. In 2019, it has more than 8,300 pull requests, which is very shocking. These contributions are the foundation of the TypeScript experience. Such a busy and enthusiastic community is constantly improving our ecosystem and pushing us to continuously improve. We are grateful for that.

Variable tuple type

Consider a function called concat in JavaScript that takes two arrays or tuple types and concatenates them to create a new array.

function concat(arr1, arr2) {
    return [...arr1, ...arr2];
}

Consider tail, which takes an array or tuple and returns all but the first element.

function tail(arg) {
    const [_, ...result] = arg;
    return result
}

How do we type them in TypeScript? For concat, the only thing we can do in older versions of TS is to try to write some overloads.

function concat<>(arr1: [], arr2: []): [A];
function concat<A>(arr1: [A], arr2: []): [A];
function concat<A, B>(arr1: [A, B], arr2: []): [A, B];
function concat<A, B, C>(arr1: [A, B, C], arr2: []): [A, B, C];
function concat<A, B, C, D>(arr1: [A, B, C, D], arr2: []): [A, B, C, D];
function concat<A, B, C, D, E>(arr1: [A, B, C, D, E], arr2: []): [A, B, C, D, E];
function concat<A, B, C, D, E, F>(arr1: [A, B, C, D, E, F], arr2: []): [A, B, C, D, E, F];)

Seven overloads will pop up when the second array is always empty. When arr2 has a parameter, let's add some more.

function concat<A2>(arr1: [], arr2: [A2]): [A2];
function concat<A1, A2>(arr1: [A1], arr2: [A2]): [A1, A2];
function concat<A1, B1, A2>(arr1: [A1, B1], arr2: [A2]): [A1, B1, A2];
function concat<A1, B1, C1, A2>(arr1: [A1, B1, C1], arr2: [A2]): [A1, B1, C1, A2];
function concat<A1, B1, C1, D1, A2>(arr1: [A1, B1, C1, D1], arr2: [A2]): [A1, B1, C1, D1, A2];
function concat<A1, B1, C1, D1, E1, A2>(arr1: [A1, B1, C1, D1, E1], arr2: [A2]): [A1, B1, C1, D1, E1, A2];
function concat<A1, B1, C1, D1, E1, F1, A2>(arr1: [A1, B1, C1, D1, E1, F1], arr2: [A2]): [A1, B1, C1, D1, E1, F1, A2];

Obviously this is getting more and more outrageous. Unfortunately, you will encounter the same problem when typing functions like tail. The following is another situation, which we call "being broken by a thousand overloads", it can't even solve any problems. It only provides the correct type for the overloads we want to write (regardless of how many overloads there are). If we want to make a common mode, we need the following overload:

function concat<T, U>(arr1: T[], arr2, U[]): Array<T | U>;

But when using tuples, this signature will not contain any information about the length of the input or the order of the elements. TypeScript 4.0 brings two fundamental changes and improvements in inference so that these can be typed.

The first change is that spread in the tuple type syntax is now generic. This means that even if we don't know the actual type of operation, we can also express high-level operations on tuples and arrays. When instantiating generic spreads in these tuple types (or replacing them with real types), they can produce other sets of arrays and tuple types.

For example, we can type functions like tail without the problem of "being overwhelmed by a thousand overloads".

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"];
// type [2, 3, 4]
const r1 = tail(myTuple);
// type [2, 3, ...string[]]
const r2 = tail([...myTuple, ...myArray] as const);

The second change is that the rest element can appear anywhere in the tuple, not just at the end!

type Strings = [string, string];
type Numbers = [number, number];
// [string, string, number, number]
type StrStrNumNum = [...Strings, ...Numbers];

Previously, TypeScript would issue the following error.

A rest element must be last in a tuple type.

But now this restriction has been lifted.

When we spread in a type with no known length, the result type will also become unlimited, and all subsequent elements will become the rest element type of the result.

type Strings = [string, string];
type Numbers = number[]
// [string, string, ...Array<number | boolean>]
type Unbounded = [...Strings, ...Numbers, boolean];

Combining these two behaviors, we can write a well-typed signature for concat:

type Arr = readonly any[];
function concat<T extends Arr, U extends Arr>(arr1: T, arr2: U): [...T, ...U] {
    return [...arr1, ...arr2];
}

Although a signature is still a bit verbose, there is only one after all, it only needs to be written once, and it has predictable behavior on all arrays and tuples.

This feature is great and more useful in other more complex scenarios. For example, consider a function to partially apply parameters, named partialCall. partialCall receives a function (here called f) and several initial parameters expected by the function. Then, it returns a new function, receives all the other parameters it needs, and calls f after receiving it.

function partialCall(f, ...headArgs) {
    return (...tailArgs) => f(...headArgs, ...tailArgs)
}

TypeScript 4.0 improves the inference process of rest parameters and rest tuple elements, so we can type it and make it "work".

type Arr = readonly unknown[];
function partialCall<T extends Arr, U extends Arr, R>(f: (...args: [...T, ...U]) => R, ...headArgs: T) {
    return (...b: U) => f(...headArgs, ...b)
}

In this case, partialCall knows which parameters it can and cannot use initially, and returns a function that can correctly receive and reject the remaining content.

const foo = (x: string, y: number, z: boolean) => {}
// This doesn't work because we're feeding in the wrong type for 'x'.
const f1 = partialCall(foo, 100);
// ~~~
// error! Argument of type 'number' is not assignable to parameter of type 'string'.

// This doesn't work because we're passing in too many arguments.
const f2 = partialCall(foo, "hello", 100, true, "oops")
// ~~~~~~
// error! Expected 4 arguments, but got 5.

// This works! It has the type '(y: number, z: boolean) => void'
const f3 = partialCall(foo, "hello");
// What can we do with f3 now?
f3(123, true); // works!
f3();
// error! Expected 2 arguments, but got 0.
f3(123, "hello");
// ~~~~~~~
// error! Argument of type '"hello"' is not assignable to parameter of type 'boolean'.

Variable tuple types have created many new patterns, especially in function composition. We hope to use it to improve the type checking of JavaScript's built-in bind method. In addition, there are other inference improvements and patterns. For more information, you can check the pull request for variable tuples.

https://github.com/microsoft/TypeScript/pull/39094

Tagged tuple element

Improving the experience of tuple types and parameter lists is important because it allows us to perform strong type validation around common JavaScript idioms—actually just slice and dice the parameter list and pass them to other functions. The use of tuple types for rest parameters is the key.

For example, the following function uses the tuple type as the rest parameter:

function foo(...args: [string, number]): void {
    // ...
}

There should be no difference from the following functions.

function foo(arg0: string, arg1: number): void {
    // ...
}

For any caller of foo.

foo("hello", 42); // works
foo("hello", 42, true); // error
foo("hello"); // error

But there is a difference in readability. In the first example, we don't have parameter names for the first and second elements. Although these have no effect on type checking, the lack of markers on the tuple position will make it difficult to convey our intentions. Therefore, in TypeScript 4.0, tuple types can now provide markup.

type Range = [start: number, end: number];

In order to further strengthen the connection between the parameter list and the tuple type, we make the syntax of the rest element and optional element consistent with the syntax of the parameter list.

type Foo = [first: number, second?: string, ...rest: any[]];

There are some rules when marking tuples, one of which is: when marking a tuple element, all other elements in the tuple must also be marked.

type Bar = [first: string, number];
// ~~~~~~
// error! Tuple members must all have names or all not have names.

It is worth noting that the tag does not require us to name the variable with a different name during destructuring. They are purely for documentation and toolchain services.

function foo(x: [first: string, second: number]) {
    // ...
    // note: we didn't need to name these 'first' and 'second'
    let [a, b] = x;
    // ...
}

In general, when using the pattern around tuples and parameter lists and implementing overloading in a type-safe manner, tagged tuples are very convenient and easy to use. In fact, TypeScript's editor support will show them as overloaded when possible.

For more information, see the pull request for tagged tuple elements.

https://github.com/microsoft/TypeScript/pull/38234

Class attribute inference of constructor

When noImplicitAny is enabled, TypeScript 4.0 can now use control flow analysis to determine the type of attributes in the class.

class Square {
    // Previously: implicit any!
    // Now: inferred to `number`!
    area;
    sideLength;
    constructor(sideLength: number) {
        this.sideLength = sideLength;
        this.area = sideLength ** 2;
    }
}

If the path of the constructor is not all assigned to an instance member, the property may be considered undefined.

class Square {
    sideLength;
    constructor(sideLength: number) {
        if (Math.random()) {
            this.sideLength = sideLength;
        }
    }
    get area() {
        return this.sideLength ** 2;
        // ~~~~~~~~~~~~~~~
        // error! Object is possibly 'undefined'.
    }
}

If you are more aware of certain situations (for example, you have a certain initialize method), you need to use explicit type annotations and explicit assignment assertions (!) when you are in strictPropertyInitialization.

class Square {
    // definite assignment assertion
    // v
    sideLength!: number;
    // ^^^^^^^^
    // type annotation
    constructor(sideLength: number) {
        this.initialize(sideLength)
    }
    initialize(sideLength: number) {
        this.sideLength = sideLength;
    }
    get area() {
        return this.sideLength ** 2;
    }
}

See the pull request for more information.

https://github.com/microsoft/TypeScript/pull/379200

Short-circuit assignment operator

JavaScript and many languages ​​support a set of operators called "compound assignment operators". The compound assignment operator applies one operator to two parameters, and then assigns the result to the left side. You may have seen these before:

// Addition
// a = a + b
a += b;
// Subtraction
// a = a - b
a -= b;
// Multiplication
// a = a * b
a *= b;
// Division
// a = a / b
a /= b;
// Exponentiation
// a = a ** b
a **= b;
// Left Bit Shift
// a = a << b
a <<= b;

Many operators in JavaScript have corresponding assignment operators! But there are three notable exceptions: logical sum (&&), logical OR (||) and null merge (??).

So TypeScript 4.0 supports a new ECMAScript feature, adding three new assignment operators: &&=, ||= and ??=.

These operators are great for replacing code examples like the following:

a = a && b;
a = a || b;
a = a ?? b;

Or an if code segment like the following:

// could be 'a ||= b'
if (!a) {
    a = b;
}

We even saw some patterns to initialize values ​​lazily when needed.

let values: string[];
// Before
(values ?? (values = [])).push("hello");
// After
(values ??= []).push("hello");

In rare cases, when you use getters or setters with side effects, you need to be aware that these operators perform assignments only when necessary. In this sense, not only the right side of the operator is "short-circuited", the assignment itself is also short-circuited.

obj.prop ||= foo();
// roughly equivalent to either of the following
obj.prop || (obj.prop = foo());
if (!obj.prop) {
    obj.prop = foo();
}

You can try to run this example to see how it differs from always performing assignments.

const obj = {
    get prop() {
        console.log("getter has run");
        // Replace me!
        return Math.random() < 0.5;
    },
    set prop(_val: boolean) {
        console.log("setter has run");
    }
};
function foo() {
    console.log("right side evaluated");
    return true;
}
console.log("This one always runs the setter");
obj.prop = obj.prop || foo();
console.log("This one *sometimes* runs the setter");
obj.prop ||= foo();

For more details, please check the pull request.

https://github.com/microsoft/TypeScript/pull/37727

You can also check TC39's proposal repository.

https://github.com/tc39/proposal-logical-assignment/

catch clause binding supports unknown

Since the birth of TypeScript, catch clause variables have always been typed as any. This means TypeScript allows you to do anything with them.

try {
    // ...
}
catch (x) {
    // x has type 'any' - have fun!
    console.log(x.message);
    console.log(x.toUpperCase());
    x++;
    x.yadda.yadda.yadda();
}

The above code will have some unexpected behavior! Since these variables are of any type by default, they do not have any type safety to prevent invalid operations. Therefore, TypeScript 4.0 now allows you to specify the type of the catch clause variable as unknown. unknown is safer than any because it reminds us to perform some type check before we manipulate the value.

try {
    // ...
}
catch (e: unknown) {
    // error!
    // Property 'toUpperCase' does not exist on type 'unknown'.
    console.log(e.toUpperCase());
    if (typeof e === "string") {
        // works!
        // We've narrowed 'e' down to the type 'string'.
        console.log(e.toUpperCase());
    }
}

Although the type of the catch variable will not change by default, we may consider using the new --strict mode flag in the future so that users can choose to enable this behavior. At the same time, it should be possible to write a lint rule to force the catch variable to have one of the following explicit annotations:: any or: unknown.

For more information, you can view the pull request.

https://github.com/microsoft/TypeScript/pull/39015

Custom JSX factory

When using JSX, fragment is a type of JSX element, allowing us to return multiple child elements. When we first implemented fragments in TypeScript, we didn't know how other libraries could use them. Today, most libraries that encourage the use of JSX and support fragments have similar API designs.

In TypeScript 4.0, users can customize the fragment factory through the new jsxFragmentFactory option.

For example, the following tsconfig.json file tells TypeScript to convert JSX in a React-compatible way, but switch each factory invocation to h instead of React.createElement, and use Fragment instead of React.Fragment.

{
  "compilerOptions": {
    "target": "esnext",
    "module": "commonjs",
    "jsx": "react",
    "jsxFactory": "h",
    "jsxFragmentFactory": "Fragment"
  }
}

If you need to use different JSX factories based on each file, you can use the new /** @jsxFrag */ annotation. For example, the following:

// Note: these pragma comments need to be written
// with a JSDoc-style multiline syntax to take effect.
/** @jsx h */
/** @jsxFrag Fragment */
import { h, Fragment } from "preact";
let stuff = <>
    <div>Hello</div>
</>;

Will output as JavaScript like this:

// Note: these pragma comments need to be written
// with a JSDoc-style multiline syntax to take effect.
/** @jsx h */
/** @jsxFrag Fragment */
import { h, Fragment } from "preact";
let stuff = h(Fragment, null,
    h("div", null, "Hello"));

Check the pull request for more information.

https://github.com/microsoft/TypeScript/pull/38720

Speed ​​up the build mode

Previously, when using the --noEmitOnError flag, when an error occurred in the previous compilation under --incremental, the compilation speed would be very slow. This is because based on the --noEmitOnError flag, any information from the last compilation will not be cached in the .tsbuildinfo file.

TypeScript 4.0 changes this, which greatly improves the speed in these situations, and thus improves the --build mode scenario (which means that there are both --incremental and --noEmitOnError).

Please check the pull request for details.

https://github.com/microsoft/TypeScript/pull/38853

--Incremental with --noEmit

TypeScript 4.0 allows us to use the --noEmit flag when compiling with --incremental. This was previously not allowed because --incremental requires the .tsbuildinfo file to be issued.

Please check the pull request for details.

https://github.com/microsoft/TypeScript/pull/39122

Editor improvements

The TypeScript compiler can not only provide a better TS editing experience for most mainstream editors, but also improve the JavaScript development experience of the Visual Studio series of editors.

Depending on the editor you are using, there will be differences when using the new TypeScript/JavaScript features in the editor:

  • Visual Studio Code supports choosing different versions of TypeScript. In addition, there is JavaScript/TypeScript Nightly Extension to keep up with the latest version (usually very stable).

  • Visual Studio 2017/2019 has the above SDK installer and MSBuild installation.

For more information, see TS Editor Support List.

https://github.com/Microsoft/TypeScript/wiki/TypeScript-Editor-Support

Convert to optional chain

The optional chain is a new feature that has been widely welcomed. TypeScript 4.0 can take advantage of optional chain and null value merging when converting common modes!

We think this refactoring should capture the intent of most use cases, especially when TypeScript has a more precise understanding of your types.

Please check the pull request for details.

https://github.com/microsoft/TypeScript/pull/39135

/** @deprecated */ Support

Now, TypeScript's editing support can recognize whether there are /** @deprecated*/JSDoc comments in the declaration . This information is displayed in the auto-complete list and serves as a suggested diagnosis that the editor can handle specially. In editors like VSCode, the deprecated value is usually displayed in strikethrough style.

For details, check the pull request.

https://github.com/microsoft/TypeScript/pull/38523

Partial editing mode at startup

Many users complain about slow startup time, especially in large projects. Specifically, the culprit is usually a process called project loading, which is roughly the same as the program building steps of our compiler. This process starts with a set of initial files, resolves them, resolves their dependencies, then resolves those dependencies, resolves those dependent dependencies, and so on. Finally, it takes a long time. The larger the project, the longer the startup delay may be.

So we have been working hard to provide developers with a new model, providing part of the experience before getting a complete language service experience. The core idea here is that editors can run a lightweight partial server with only a single file view.

This new model can shorten the preparation time for TypeScript to start interacting on the code base from 20 seconds to 1 minute to just a few seconds. For example, when the editor is restarted on a larger code base, TS 3.9 version cannot immediately provide automatic completion and quick information; on the other hand, TS 4.0 can immediately provide a complete editing experience while loading the entire project in the background.

Currently, the only editor that supports this mode is Visual Studio Code, but there is still room for improvement in UX and features. We have listed the improvements we are going to join and hope to get more feedback.

https://github.com/microsoft/TypeScript/issues/39035

For more information, you can view the original proposal, pull request, and follow-up meta questions.

https://github.com/microsoft/TypeScript/issues/37713

Smarter automatic import

Automatic import is a great feature. However, automatic import does not work on packages written in TypeScript-that is, we have to write at least one explicit import elsewhere in the project.

Why does the automatic import apply to @types packages, but not to packages using your own types? In fact, the automatic import is achieved by checking the packages already included in the project. TypeScript has a quirk. It can automatically include all packages in node_modules/@types and ignore other packages; but crawling all node_modules packages can be expensive.

These will lead to a bad experience when you try to automatically import content that has just been installed but not yet used.

TypeScript 4.0 can now include the packages you listed in the dependencies (and peerDependencies) field of package.json. The information in these packages is only used to improve the automatic import and will not change other content such as type checking. This avoids the cost of traversing the node_modules directory and allows us to provide automatic import for all types of dependencies.

When your package.json lists more than ten typed dependencies that have not been imported, this feature will be automatically disabled to avoid a slow project loading process. To force it on or disable it completely, you can configure the editor. In Visual Studio Code is the "Include Package JSON Auto Imports" setting (or typescript.preferences.includePackageJsonAutoImports).

For details, you can view the proposal issues and pull requests.

https://github.com/microsoft/TypeScript/issues/37812

Our new website!

The TypeScript website has recently been completely rewritten!

For more information, please refer to the previous article:

"TypeScript new version of the website online: brings a new navigation mechanism"

Major changes

lib.d.ts

Our lib.d.ts declaration has changed, specifically the type of DOM has changed. The main thing is to delete document.origin, it only works in the old version of IE, and Safari MDN recommends using self.origin instead.

Property override accessor (and vice versa) is an error

Previously, only when useDefineForClassFields was used, it was an error to rewrite the accessor or the property by the accessor; but now, an error is always issued when declaring a property in a derived class that will override the getter or setter in the base class.

class Base {
    get foo() {
        return 100;
    }
    set foo() {
        // ...
    }
}
class Derived extends Base {
    foo = 10;
//  ~~~
// error!
// 'foo' is defined as an accessor in class 'Base',
// but is overridden here in 'Derived' as an instance property.
}
class Base {
    prop = 10;
}
class Derived extends Base {
    get prop() {
    // ~~~~
    // error!
    // 'prop' is defined as a property in class 'Base', but is overridden here in 'Derived' as an accessor.
        return 100;
    }
}

For details, check the pull request.

https://github.com/microsoft/TypeScript/pull/37894

The operand of delete must be optional

When using the delete operator in strictNullChecks, the operand must now be any, unknown, never, or optional (because it contains undefined in the type). Otherwise, it is wrong to use the delete operator.

interface Thing {
    prop: string;
}
function f(x: Thing) {
    delete x.prop;
    // ~~~~~~
    // error! The operand of a 'delete' operator must be optional.
}

For more information, see the pull request.

https://github.com/microsoft/TypeScript/pull/37921

TypeScript's Node factory usage is deprecated

Today, TypeScript provides a set of "factory" functions for generating AST nodes. However, TypeScript 4.0 provides a new node factory API. Therefore TypeScript 4.0 decided to abandon the use of these old functions, and it is recommended to use the new functions instead.

For more information, check out the pull request.

https://github.com/microsoft/TypeScript/pull/35282

Next step plan

The iteration plan of TypeScript 4.1 has been launched, you can get a general idea.

https://github.com/microsoft/TypeScript/issues/40124

At the same time, you can use nightly build in the workspace or editor to preview the new features added in 4.1. Whether you are using TypeScript 4.0 or the next version, we want to hear your feedback! You can contact us on Twitter or open an issue on GitHub.

Once again, we are grateful for all the work and dedication we have done for the community. We want to make the coding experience of TypeScript and JavaScript the pure pleasure you deserve. To this end, we need to improve the language and editing experience, improve performance, iterate our user experience, lower the barriers to entry and learning, and so on.

Thank you very much, please enjoy version 4.0, happy programming!

Further reading

https://devblogs.microsoft.com/typescript/announcing-typescript-4-0/

study Exchange

  • Follow the public account [Frontend Universe], get good article recommendations every day

  • Add WeChat, join the group to communicate


"Watching and forwarding" is the greatest support

Guess you like

Origin blog.csdn.net/liuyan19891230/article/details/108353844