TypeScript 3.0

Original author: Daniel Rosenwasser
  • Transfer
TypeScript 3.0! Yes, it came out, and there are really a lot of innovations in it. Under the cat you will find a detailed description of all the latest versions of the latest version, including the build mode, the new unknown type, significant changes in the API, performance improvements and much more. Join now!

TypeScript 3.0 released! A new milestone in the development of TypeScript, an assistant to all users of JavaScript, has begun.

If you are not familiar with TypeScript yet, it’s not too late to find out about it now! TypeScript is a JavaScript extension designed for use in the modern version of this static type language. The TypeScript compiler reads TypeScript code containing, in particular, type declarations and annotations, and provides clean, easy to read JavaScript code in which these constructs are converted and deleted. The resulting code runs in any runtime environment that conforms to the ECMAScript standard, for example in your favorite browser or on the Node.js server platform.

Using such an environment means that the code will be analyzed for errors or typos before it is launched by users, but its benefits are not limited to this. With all this information and analysis results, TimeScript improves usability by providing automatic code completion and navigation tools such as Find all References, Go to Definition, and Rename in your favorite editor. .

To get started with the language and get more information, follow the link . If you want to try TypeScript 3.0 right now, you can download it from NuGet or via npm by entering the command

npm install -g typescript

In addition, support is available in the following editors:

Other editors are updated according to their own graphics, but soon they will all have excellent TypeScript support.

Review Version 3.0

After TypeScript 2.0, we made a brief overview of the contributions of previous versions to its current state. Between the releases of TypeScript 1.0 and 2.0, the language includes union types, type conditions (type guards), support for the modern ECMAScript standard, type aliases, JSX support, literal and polymorphic types this. If we include here the introduced TypeScript 2.0 types that do not allow the “null” value, control flow analysis, support for tagged unions, this types, and a simplified model for receiving files .d.ts, then we can say that this period fully determined the fundamentals of TypeScript operation.

So what has been done since then? What led us to TypeScript 3.0, besides the new features of the ECMAScript standard like asynchronous functions async/await, generators and operator of expansion / residual (rest / spread)?

TypeScript 2.1 was a fundamental release in which a static model of metaprogramming in JavaScript was introduced. Key request ( keyof), index access ( T[K]) and types of associated objects ( { [K in keyof T]: } T[K]}) - here is a list of tools that were used for more efficient modeling of React, Ember, Lodash and other libraries.

In TypeScript 2.2 and 2.3 releases, support for the mixin class templates, type object (representing an object that is not a primitive), and default values ​​for generic types have appeared. These features have been used in a number of projects, such as Angular Material and Polymer. In addition, TypeScript 2.3 introduced the ability to have detailed type management.this, which allows the language to work well with libraries such as Vue, and a flag has been added checkJsthat allows types to be checked in JavaScript files.

In the TypeScript 2.4 and 2.6 releases, the story continues to increase the severity of function type checking associated with some of the oldest reviews about our type system. A flag was introduced --strictFunctionTypes, forcing the contravariance of parameters. In release 2.7, the trend toward rigor has been preserved and has been expressed in testing in classes with the help of a flag --strictPropertyInitialization.

In TypeScript 2.8, conditional types are introduced, a powerful tool for static expression of types-based solutions, and release 2.9 summarizes the keyof operator and simplifies import for types.

And that brings us to TypeScript 3.0! Despite the new integer number in the issue, little has changed in release 3.0 (which implies a very easy update). It introduces a new, flexible and scalable way of structuring projects, powerful new support for working with parameter lists, new types for providing explicit checks, improved JSX support, significantly more user-friendly error diagnostics, and much more.

What's new?

  • Links to projects
    • Mode --build
    • Output structure management
    • Future plans
  • Extract and distribute parameter lists using tuples
  • New features of tuple types
  • Type of unknown
  • Improved error diagnosis and user environment
    • Related error ranges
    • Improved diagnostics and error handling
  • Property Support defaultPropsin JSX
  • Directives /// <reference lib="..." />
  • Increase the speed of work in the editor
    • Refactoring named import statements
    • Completion of end tags and frame with contour
    • Quick fixes for unreachable code and unused tags
  • Critical changes
    • unknown is a reserved type name
    • API Critical Changes

Links to projects

Quite often, building a library or application requires several steps. Suppose your code base contains directories src and test. Suppose you have a folder clientwhere the client-side code of the application is stored, and a server folder containing the server-side code on the Node.js platform, and each of them borrows a part of the code from the folder shared. Perhaps you use the so-called “single repository” and have many projects that are complexly dependent on each other.

One of the most important functions that we worked on with the release of TypeScript 3.0, called "links to the project", and it is designed to simplify the work with such scripts.

Thanks to links to a project, some TypeScript projects may depend on others. In particular, filestsconfig.jsonallowed to refer to other files tsconfig.json. Defining these dependencies makes it easy to divide the code into smaller projects, because the TypeScript compiler (and its tools) can understand the build order and the structure of the output. This means that the assembly is faster and is performed incrementally (step by step), transparent navigation, editing and refactoring on various projects are supported. Since TypeScript 3.0 lays the foundation for the project and provides an API, any build tool must be able to provide this.

What does this look like?

As a simple example, here is a file tsconfig.jsoncontaining links to projects.

// ./src/bar/tsconfig.json
    "compilerOptions": {
        // Needed for project references.
        "composite": true,
        "declaration": true,
        // Other options...
        "outDir": "../../lib/bar",
        "strict": true, "module": "esnext", "moduleResolution": "node",
    "references": [
        { "path": "../foo" }

It has two new fields: compositeand references.

The field references simply points to other files tsconfig.json(or folders in which they are contained). Each link here is just an object with a field path("path") and indicates to the TypeScript compiler that to build this project, you first need to build another project to which it refers.

Apparently, the field is just as important composite. The field composite guarantees that certain parameters will be included that allow any project that depends on a given one to refer to it and to include it in an incremental build. Intellectual and incremental build is important because one of the main reasons why you can abandon the project is assembly speed.

For example, if the project front-enddepends on the project shared, and shared on the core, then our APIs relating to project references will help identify changes in the core, but will only collect the shared again, if the types produced by the project core (i.e. files .d.ts) have changed . This means that a change in core does not entail a global reassembly of all projects. For this reason, setting a flag composite also generates a setting and a flag declaration.

--Build mode

In TypeScript 3.0, a set of APIs will appear for referencing projects so that other tools can provide this fast incremental build method. In particular, in the gulp-typescript plug-in, these APIs are already used! Thus, later references to projects will be integrated with your chosen orchestra compilers.

However, for many simple applications and libraries, it is advisable not to use external tools. That's why the tsc command now sets the new flag --build.

The team tsc --build(or its pseudonym, tsc -b) takes a set of projects and builds them, as well as build dependent projects. When using the new build mode, firstly, a flag must be set --build, and it can be combined with some other flags:
  • --verbose: shows each step required by the build process.
  • --dry: builds without generating output files (useful in conjunction with the parameter --verbose).
  • –clean: tries to delete the output files corresponding to the specified input.
  • --force: forcibly performs a complete, non-incremental build of the project.

Output structure management

One subtle, but incredibly useful advantage of references to projects is the logical ability to match input files with the corresponding output.

If you have ever tried to separate the client and server parts of the application, then you might encounter problems managing the output structure.

For example, if both client / index.ts and server / index.ts files refer to shared / index.ts for the following projects:

... then when trying to build client and server projects we get ...

... and not ...

Notice that after the build we got copies of the shared folder in both the client and server. We spent too much time on the shared shared build and added an undesirable level of nesting in lib / client / client and lib / server / server.

The problem is that TypeScript is eagerly looking for .ts files and trying to include them in this compilation. Ideally, TypeScript should understand that these files should not be part of the assembly in the same compilation, and instead refer to the .d.ts files for information about the types.

Creating a tsconfig.json file for shared results in exactly this result. It signals the TypeScript compiler:

  1. that a shared project should be built independently
  2. and that when importing from ../shared we should look for .d.ts files in its output directory.

This allows you to avoid running a double build, as well as accidental inclusion of the entire contents of the shared.

Future plans

To gain a deeper understanding of the project links and the possibilities of using them, read about them in more detail in the tracker of this release . In the near future we will prepare documentation for links to projects and build mode.

We want authors of other programming tools to maintain links to projects and continue to improve the editing environment with regard to this function. We intend to ensure that working with links to projects goes as smoothly as developing code with a single tsconfig.json file. If you end up using links to projects, we will be grateful for any feedback.

Extract and distribute parameter lists using tuples

We often take this for granted, but JavaScript allows us to treat parameter lists as first-class values ​​— using either arguments or rest parameters (for example, ... rest).

functioncall(fn, ...args){
    return fn(...args);

Note that call works for functions with any number of parameters. Unlike other languages, JavaScript does not force us to define call0, call1, call2, etc., as follows:

functioncall0(fn) {return fn();
functioncall1(fn, param1) {return fn(param1);
functioncall2(fn, param1, param2) {return fn(param1, param2);
functioncall3(fn, param1, param2, param3) {return fn(param1, param2, param3);

Unfortunately, for some time there was no good way to express it in TypeScript without declaring a finite number of overloads:

// TODO (billg): 5 overloads should *probably* be enough for anybody?functioncall<T1, T2, T3, T4, R>(fn: (param1: T1, param2: T2, param3: T3, param4: T4) => R, param1: T1, param2: T2, param3: T3, param4: T4): Rfunctioncall<T1, T2, T3, R>(fn: (param1: T1, param2: T2, param3: T3) => R, param1: T1, param2: T2, param3: T3): Rfunctioncall<T1, T2, R>(fn: (param1: T1, param2: T2) => R, param1: T1, param2: T2): Rfunctioncall<T1, R>(fn: (param1: T1) => R, param1: T1): R;
functioncall<R>(fn: () => R, param1: T1): R;
functioncall(fn: (...args: any[]) => any, ...args: any[]){
    return fn(...args);

Phew! Another fatal case with a thousand overloads! Or at least as much of the overload as required by users.

TypeScript 3.0 allows you to better model such scripts, since now parameters of the rest type can be universal, and their type is defined as a tuple. Instead of declaring each of these overloads, we say that the rest parameter ... args from the fn function should be a type parameter that expands the array, and then reuse it for the ... args parameter that the call function passes:

functioncall<TSextendsany[], R>(fn: (...args: TS) => R, ...args: TS): R {return fn(...args);

When we call the call function, TypeScript tries to extract the parameter list from what we pass to fn, and turn it into a tuple:

functionfoo(x: number, y: string): string {
    return (x + y).toLowerCase();
// The `TS` type parameter is inferred as `[number, string]`
call(foo, 100, "hello");

When TypeScript defines TS as [number, string], and we end up using TS for the rest parameter of the call function, an instance of the function looks like this:

function call(fn: (...args: [number, string]) => string, ...args: [number, string]): string

And in TypeScript 3.0, when using a tuple in the rest, the parameter is minimized to the rest of the parameter list! The above copy is reduced to simple parameters without tuples:

function call(fn: (arg1: number, arg2: string) => string, arg1: number, arg2: string): string

So, in addition to catching type conversion errors on passing invalid arguments:

function call<TS extends any[], R>(fn: (...args: TS) => R, ...args: TS): R {
call((x: number, y: string) => y, "hello", "world");
//                                ~~~~~~~// Error! `string` isn't assignable to `number`!

... and type definitions from other arguments:

call((x, y) => { /* .... */ }, "hello", 100);
//    ^  ^
//`x`and`y` have their types inferred as `string`and`number` respectively.

... we can also see the tuple types that these functions define externally:

functiontuple<TSextendsany[]>(...xs: TS): TS {
    return xs;
let x = tuple(1, 2, "hello"); // has type `[number, number, string]`

But pay attention to one nuance. To do all this work, we had to expand the possibilities of tuples ...

New features of tuple types

In order to model the list of parameters in the form of a tuple (as we have just discussed), we had to rethink the tuple types for a bit. Prior to the release of TypeScript 3.0, the best of what was allowed to be modeled using tuples was the order and length of the parameter set.

However, parameter lists are not just ordered type lists. For example, parameters at the end may be optional:

// Both `y` and `z` are optional here.functionfoo(x: boolean, y = 100, z?: string){
    // ...
foo(true, undefined, "hello");
foo(true, 200);

The last parameter may be a rest parameter.

// `rest` accepts any number of strings - even none!functionfoo(...rest: string[]){
    // ...
foo("hello", "world");

And finally, there is one rather interesting property of parameter lists - they can be empty:

// Accepts no parameters.functionfoo(){
    // ...

Therefore, in order for tuples to match the parameter lists, we needed to model each of these scenarios.

First, there are now optional elements at the end of the tuples:

 * 2D, or potentially 3D, coordinate.
 */typeCoordinate= [number, number, number?];

The Coordinate type creates a tuple with an optional property named 2 — an element with index 2 may not be defined! Interestingly, since tuples use numeric literal types for their length property (length), the length property of the Coodinate tuple is type 2 | 3.

Secondly, the rest element may now be present at the end of the tuple.

typeOneNumberAndSomeStrings= [number, ...string[]];

Thanks to the rest elements, the tuples exhibit a very interesting behavior of "unbounded from the end." In the example above, the type OneNumberAndSomeStrings is required for the type of its first property to be number, and one or more properties of type string are allowed. Indexing this type of tuple with an arbitrary number number returns the type string | number because the index value is unknown. Similarly, since the length of the tuple is unknown, the value of the length property is just a number.

It should be noted that in the absence of other elements, the rest element in a tuple is identical to itself:

typeFoo= [...number[]]; // Equivalent to `number[]`.

Finally, tuples can now be empty! Although it is not very useful when used outside parameter lists, an empty tuple type can be defined as []:

typeEmptyTuple= [];

As you would expect, the empty tuple has a length property of 0, and indexing with number returns the type never.

Improved error diagnosis and user environment

Over time, we receive more and more requests from members of our community to improve error messages. Although this work is far from complete, we heard you and made a number of improvements in TypeScript 3.0.

Related error ranges

In part, the goal of a good error message is to indicate to the user also the way to correct it, or, first of all, to make it clear why this message appeared. In most cases, it contains a lot of information or indicates several reasons for its occurrence. From an analysis of these reasons, we can conclude that errors flow from different parts of the code.

Related error ranges are a new way to provide this information to users. In TypeScript 3.0, error messages can generate messages elsewhere in the code so that users can find out the cause and effect of the error.

In a sense, the associated error messages can not only give an explanation to the user, but also indicate the path to the place where everything went wrong.

These intervals will also appear in terminal mode when you run the tsc command with the --pretty mode enabled, although we are still working on improving the user interface and will take into account your feedback!

Improved diagnostics and error handling

In preparing the TypeScript 2.9 release, we began to pay more attention to error messages, and in Release 3.0, we really tried to solve the main tasks that would allow us to perform an intelligent, clear and accurate error diagnosis. This includes, in particular, the selection of appropriate types in case of inconsistencies in the types of union and output directly to the source of error for certain types of messages.

We believe that our efforts have been justified, and as a result you will receive shorter and clearer error messages.

Type unknown

The type any (any) is a type in TypeScript, suitable for anything. Since it covers the types of all possible values, it does not force us to do any checks before we try to call these values, construct them, or access their properties. It also allows you to assign any type values ​​to variables that expect values ​​of any other type.

This feature is generally useful, but cannot provide sufficient rigor.

let foo: any = 10;
// All of these will throw errors, but TypeScript// won't complain since `foo` has the type `any`.
new foo();
foo `hello world!`;
functionupperCase(x: string) {
    return x.toUpperCase();

Sometimes in TypeScript I want to describe a type that does not fit to anything. This is useful for the API that wants to signal: "there can be any value here, so you need to do some checking before using it." And users are forced to analyze the returned values ​​for security reasons.

TypeScript 3.0 introduces a new type called unknown, which does just that. Like the type of any, the type unknown is assigned any value, however, unlike any, the type unknown can be assigned to almost no other without a type approval. You cannot access objects of type unknown, nor can you call them or construct them.

If in the example above, we substitute unknown for any, then all cases of using the foo object will result in an error:

let foo: unknown = 10;
// Since `foo` has type `unknown`, TypeScript
// errors oneachof these locations.
new foo();
foo `hello world!`;
function upperCase(x: string) {
    return x.toUpperCase();

Instead, we have to either perform a check or use a type statement and convince the type-checking system that we know better what to do.

let foo: unknown = 10;
functionhasXYZ(obj: any): obj is{ x: any, y: any, z: any } {
    return !!obj &&
        typeof obj === "object" &&
        "x"in obj && "y"in obj && "z"in obj
// Using a user-defined type guard...if (hasXYZ(foo)) {
    // ...we're allowed to access certain properties again.
// We can also just convince TypeScript we know what we're doing// by using a type assertion.
upperCase(foo as string);
functionupperCase(x: string){
    return x.toUpperCase();

Note: if you use a type such as {} | null | undefined, the unknown type in constructions like conditional types usually behaves in a more desirable way, since conditional types apply to union types:

typeArrayify<T>= Textends any ? Array<T> : never;
typeA= Arrayify<{} | null | undefined>; // null[] | undefined[] | {}[]typeB= Arrayify<unknown>;               // unknown[]

Support defaultProps in JSX

Note that the .d.ts files of the React library at the time of their writing may not yet have supported this functionality.

If you have ever used TypeScript / JavaScript default initializers in modern language, you should know how convenient they are when writing function call statements. They give us useful syntaxes for calling functions in an easier way, allowing you to omit specific arguments. In this case, the authors of the functions are given the opportunity to make sure that the values ​​of the arguments are always clearly defined.

functionloudlyGreet(name = "world") {
    // Thanks to the default initializer, `name` will always have type `string` internally.// We don't have to check for `undefined` here.console.log("HELLO", name.toUpperCase());
// Externally, `name` is optional, and we can potentially pass `undefined` or omit it entirely.

In the React library, there is a similar concept for components and their properties (props). When creating a new item using the React component, it searches for a property, called defaultProps, to populate the omitted values ​​for props.

// Some non-TypeScript JSX fileimport * as React from"react";
import * as ReactDOM from"react-dom";
    render() {
        const { name } = this.props;
        return<div>Hello ${name.toUpperCase()}!</div>;
    static defaultProps = {
        name: "world",
//      Notice no `name` attribute was specified!//                                     vvvvvvvvvconst result = ReactDOM.renderToString(<Greet />);

Notice that the name is not specified in <Greet />. When the Greet element is created, the name constant will be initialized to the value "world", and this code will type: Hello world !.

Unfortunately, TypeScript did not understand that defaultProps has anything to do with JSX calls. Instead, users were often forced to declare properties as optional and use non-zero statements within the render function:

export interface Props { name?: string }
export classGreetextendsReact.Component<Props>{
    render() {
        const { name } = this.props;
        // Notice the `!` ------vreturn <div>Hello ${name!.toUpperCase()}!</div>;
    static defaultProps = { name: "world"}

Or use some sophisticated type statements to correct the type of a component before exporting it.

This is why TypeScript 3.0 supports a new type alias in the JSX namespace called LibraryManagedAttributes. Despite the long name, this is just an auxiliary type that tells TypeScript which attributes the JSX tag accepts. In short, using this generic type, we can model a React specific behavior for defaultProps and, to some extent, for propTypes.

export interface Props {
    name: string
export classGreetextendsReact.Component<Props>{
    render() {
        const { name } = this.props;
        return <div>Hello ${name.toUpperCase()}!</div>;
    static defaultProps = { name: "world"}
// Type-checks! No type assertions needed!
let el = <Greet />

Keep in mind that there are limitations. For defaultProps, which explicitly indicate their type as something like Partial, or stateless function components (SFC) functions, for which defaultProps are declared as Partial, all properties will become optional. As a workaround, you can completely eliminate the type annotation for defaultProps as a class component (see example above) or use default initializers for SFCs according to the ES2015 standard:

functionGreet({ name = "world" }: Props) {
    return<div>Hello ${name.toUpperCase()}!</div>;

And the last thing to note. Although support is built into TypeScript, the current .d.ts files in the DefinitelyTyped repository do not currently use it, so it is possible that @ types / react does not yet contain available changes. We are currently looking forward to stabilizing the entire DefinitelyTyped community in order to guarantee the minimum number of failures with the described changes.

Directives /// <reference lib = "..." />

One of the problems we see in the community is that polyfills — libraries that provide newer APIs in older runtimes — often have their own declaration files (.d.ts files) that they try to define themselves. for these APIs. Sometimes this is good, but these declarations are global and can create problems in combination with lib.d.ts files built into TypeScript, depending on the user's compiler options, such as --lib and --target. For example, declarations for the core-js library may conflict with the embedded declaration file lib.es2015.d.ts.

To solve this problem, TypeScript 3.0 proposes that the files declare embedded APIs that are expected to be present, by using a new reference directive: /// <reference lib = "..."

For example, a polyfill for a Promise object of the ES2015 standard can now simply contain the lines

/// <reference lib="es2015.promise" />export {};

With such a comment, even if the TypeScript 3.0 consumer explicitly used a target object that does not contribute the definition file lib.es2015.promise.d.ts, importing the specified library ensures that Promise is present.

Increase the speed of work in the editor

For those who are not yet familiar with the language, TypeScript provides services that make it easier to write code through the use of its syntactic and semantic knowledge. This service acts as a built-in handler for TypeScript and JavaScript under the control of editors such as Visual Studio, Visual Studio Code, and any other editor with a TypeScript plug-in. It provides features that users really like, such as code completion, the Go to Definition function, and even quick fixes and code refactoring. TypeScript 3.0 continues to support these services.

Refactoring named import statements

Sometimes the qualification of importing each object with the name of the module from which it came, clutters the code.

import * as dependency from "./dependency";
// look at all this repetition!

On the other hand, if we import the used objects separately, we can find out that after their multiple applications to the new code reader, it becomes unclear where they are imported from.

import { foo, bar, baz } from"./dependency";
// way lower in the file...

No matter what method you choose now, you can change your mind later. TypeScript 3.0 provides the ability to refactor, so that such a switch does not cause difficulties.

Completion of end tags and frame with contour

Currently, TypeScript provides two new features that enhance the usability of JSX tags:

  • completion of JSX closing tags;
  • generating frames with collapsible contour for JSX.

Quick fixes for unreachable code and unused tags

TypeScript now allows you to quickly correct the code - delete inaccessible code, as well as unused labels.

Critical changes

You can always follow upcoming critical changes in the language , as well as in our API .

We hope that in TypeScript 3 there are very few critical changes affecting the operation of applications. Changes in the language should be minimally destructive, and most of the critical changes in our API are focused on removing obsolete functions.

unknown - reserved type name

Since unknown is a new built-in type, this word can no longer be used in type declarations, such as interfaces, type aliases, or classes.

API Critical Changes

  • The outdated LanguageService # getSourceFile internal method has been removed because it has received disapproving feedback over the past two years. See # 24540 .
  • The deprecated TypeChecker # getSymbolDisplayBuilder function and its associated interfaces have been removed. See # 25331 . Instead, use emitter (event generator) and node builder.
  • The obsolete escapeIdentifier and unescapeIdentifier functions have been removed. Since the principle of the API identifier as a whole has changed, they served as identification functions in just a few releases. If you need your code to behave as before, simply remove the calls to these functions. Alternatively, type-safe escapeLeadingUnderscores and unescapeLeadingUnderscores should be used if the types indicate that they are required (because they are used to convert to or from proprietary types __String and string).
  • The methods TypeChecker # getSuggestionForNonexistentProperty, TypeChecker # getSuggestionForNonexistentSymbol and TypeChecker # getSuggestionForNonexistentModule are made internal, they are no longer part of our public API. See # 25520 .


TypeScript owes its great success to the community. We are indebted to those who contributed to the work on the compiler, the language service, the DefinitelyTyped repository, and the integration of tools for which any combination of the above was used. We are also grateful to our users who constantly shared useful feedback for us and encouraged us to improve.

In the future, we plan to pay more attention to the type system and tools, improve references to projects and fully expand the availability of TypeScript (both language and project). Plus, we would like to find out what can be done to provide more opportunities for authors and toolbox users in the JavaScript community. We would like to help those developers who can use TypeScript usefully, even if they don’t access it directly.

Follow our map as our ideas come to life, feel free to write a couple of lines to give us feedback, in the form of a comment, on Twitter or in the form of a request for a problem . We always try to work better.

Anyone who has accompanied us so far on a journey through TypeScript, thank you! We are planning to create for you the most convenient working environment. As for everyone else, we hope that you begin to learn and love TypeScript as much as we do.

Good luck in your development!
TypeScript command

Also popular now: