Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

Introduction to TypeScript 2.0


May 07, 2021 TypeScript


Table of contents


Introduction to TypeScript 2.0

Null and underfined types

TypeScript now has two special types: Null and Undefined, whose null and undefined Previously it was impossible to explicitly name these types, null undefined be used as type names regardless of type check mode.

Previous type inspectors thought null and undefined were assigned to everything. In fact, null and undefined for each type and cannot be explicitly excluded (so errors cannot be detected).

--strictNullChecks

--strictNullChecks be switched to the new strict air check mode.

In strictly empty check mode, null and undefined any type of value, only values of their own type any type (with one exception, undefined assigned to void Therefore, although T and T | undefined in general type check mode (because undefined sub-type of any T they are different in strict type check T | undefined are undefined as is the relationship between T T | null

Example

// 使用--strictNullChecks参数进行编译的
let x: number;
let y: number | undefined;
let z: number | null | undefined;
x = 1;  // 正确
y = 1;  // 正确
z = 1;  // 正确
x = undefined;  // 错误
y = undefined;  // 正确
z = undefined;  // 正确
x = null;  // 错误
y = null;  // 错误
z = null;  // 正确
x = y;  // 错误
x = z;  // 错误
y = x;  // 正确
y = z;  // 错误
z = x;  // 正确
z = y;  // 正确

Use the pre-assignment check

In strictly empty check mode, the compiler requires that local variables that do not contain the undefined must be assigned before they can be used.

Example

// 使用--strictNullChecks参数进行编译
let x: number;
let y: number | null;
let z: number | undefined;
x;  // 错误,使用前未赋值
y;  // 错误,使用前未赋值
z;  // 正确
x = 1;
y = null;
x;  // 正确
y;  // 正确

The compiler checks that variables are explicitly over-valued by performing type analysis based on the control flow. Further details will be provided later in this article.

Optional parameters and properties

Optional parameters and properties undefined their type, even if their type annotations explicitly do not undefined For example, the following two types are identical:

// 使用--strictNullChecks参数进行编译
type T1 = (x?: number) => string;              // x的类型是 number | undefined
type T2 = (x?: number | undefined) => string;  // x的类型是 number | undefined

Non-null and non-undefined type protection

If the object or function type null undefined compilation error occurs when the property is accessed or the function is called. As a result, type protection has been extended to support non-null and non-underfined checks.

Example

// 使用--strictNullChecks参数进行编译
declare function f(x: number): string;
let x: number | null | undefined;
if (x) {
    f(x);  // 正确,这里的x类型是number
}
else {
    f(x);  // 错误,这里的x类型是number?
}
let a = x != null ? f(x) : "";  // a的类型是string
let b = x && f(x);  // b的类型是 string | 0 | null | undefined

Non-null and non-undefined type === protection can !== be compared with the operator and null or undefined such as x != null or == x === undefined != The effect on the type of variable being tested accurately reflects the semantics of JavaScript (for example, the double equal operator checks two values whether you specify null or underfined, wherein yet the three-equal operator checks only that value specified).

The name of the point in type protection

Type protection previously supported only the examination of local variables and parameters. Type protection now supports checking for "point names" consisting of variable or parameter names followed by one or more access properties.

Example

interface Options {
    location?: {
        x?: number;
        y?: number;
    };
}

function foo(options?: Options) {
    if (options && options.location && options.location.x) {
        const x = options.location.x;  // x的类型是number
    }
}

Point name type protection and user-defined type protection functions, typeof instanceof and do not rely on -- --strictNullChecks parameters.

Assigning any part of the point name after type protection results in invalid type protection. For example, x.y.z x x.y to x.y.z protection results in x.y.z type protection.

Expression operator

The expression operator allows the type of the operator null and/or undefined but always produces result values that are not null and non-underfined types.

// 使用--strictNullChecks参数进行编译
function sum(a: number | null, b: number | null) {
    return a + b;  // 计算的结果值类型是number
}

&& The operator null undefined operating object depending on the type of the current left-hand operating object, || The operator removes both null and undefined from null the operation object of the union type on the left.

// 使用--strictNullChecks参数进行编译
interface Entity {
    name: string;
}
let x: Entity | null;
let s = x && x.name;  // s的类型是string | null
let y = x || { name: "test" };  // y的类型是Entity

Type extension

In strictly empty check mode, null and undefined are not extended any type.

let z = null;  // z的类型是null

In general type check mode, because of the extension, z is any in strict empty check z null type null null the unique z there are no type comments).

A non-empty assertion operator

In the context, a new suffix expression ! ! C an be used to assert that the action object is non-null and non-underfined types. S pecifically, operation x! P roduces a value of x that does not contain null and undefined x T he form of <T>x x as T ! Non-empty assertion operators are removed from compiled JavaScript code.

// 使用--strictNullChecks参数进行编译
function validateEntity(e?: Entity) {
    // 如果e是null或者无效的实体,就会抛出异常
}

function processEntity(e?: Entity) {
    validateEntity(e);
    let s = e!.name;  // 断言e是非空并访问name属性
}

Compatibility

These new features are designed to be available in both the strict air check mode and the regular type check mode. E specially in general type check mode, null undefined automatically removed from the union type (because they are sub-types of all other types), ! N on-empty assertion expression operators are also allowed to be used but have no effect. Therefore, after the claims file is updated with null and underfined sensitive types, it is still backward compatible in regular type mode.

In practice, strict air check mode requires that all files compiled are null and undefined sensitive types.

Type analysis based on the control flow

TypeScript 2.0 enables a flow of control flow type analysis of local variables and parameters. P reviously, type analysis of type protection was limited if ?: and did not include the effects of assignments and control return and break break With TypeScript 2.0, the type inspector analyzes all possible control flows of statements and expressions, producing the most likely specific type (the narrowed type) of local variables or parameters declared as union types at any given location.

Example

function foo(x: string | number | boolean) {
    if (typeof x === "string") {
        x; // 这里x的类型是string
        x = 1;
        x; // 这里x的类型是number
    }
    x; // 这里x的类型是number | boolean
}

function bar(x: string | number) {
    if (typeof x === "number") {
        return;
    }
    x; // 这里x的类型是string
}

Type analysis based on the control flow is particularly important in the --strictNullChecks pattern because empty types are represented by union types:

function test(x: string | null) {
    if (x === null) {
        return;
    }
    x; // 在函数的剩余部分中,x类型是string
}

Also, in --strictNullChecks on the control flow includes analysis that does not allow the type to have explicit assignments for local variables that are not allowed to be undefined

function mumble(check: boolean) {
    let x: number; // 类型不允许为undefined
    x; // 错误,x是undefined
    if (check) {
        x = 1;
        x; // 正确
    }
    x; // 错误,x可能是undefi
    x = 2;
    x; // 正确
}

Mark the union type

TypeScript 2.0 implements tag (or differentiated) union types. Specifically, the TS compiler now supports type protection, narrows the scope of the federation type based on the checks of the switch statement also supports this attribute.

Example

interface Square {
    kind: "square";
    size: number;
}

interface Rectangle {
    kind: "rectangle";
    width: number;
    height: number;
}

interface Circle {
    kind: "circle";
    radius: number;
}

type Shape = Square | Rectangle | Circle;

function area(s: Shape) {
    // 在下面的switch语句中,s的类型在每一个case中都被缩小
    // 根据判别属性的值,变量的其它属性不使用类型断言就可以被访问
    switch (s.kind) {
        case "square": return s.size * s.size;
        case "rectangle": return s.width * s.height;
        case "circle": return Math.PI * s.radius * s.radius;
    }
}

function test1(s: Shape) {
    if (s.kind === "square") {
        s;  // Square
    }
    else {
        s;  // Rectangle | Circle
    }
}

function test2(s: Shape) {
    if (s.kind === "square" || s.kind === "rectangle") {
        return;
    }
    s;  // Circle
}

The judged property type protection is an expression of x.p == v x.p === v x.p != v x.p !== v in which p and v are an expression of a literal type of property and string or a literal union of strings. The judged property type protects the x shrinks x to one of the p v the judged properties p and v.

Note that we currently only support the character properties of string literal types. We intend to add support for Boolean values and digital literal types later.

never type

TypeScript 2.0 introduces a new original never never type indicates that the type of the value never appears. Specifically, never the return type that never returns the function, and it is also the type of variable that never true in type protection.

never type has the following characteristics:

  • never is a sub-type of all types and can be assigned to all types.
  • No type is never of never or can be assigned to never (except for never type itself).
  • When a function expression or arrow function does not return a type annotation, if the return statement, or if there is only a return statement for the never return and if the function is not executable to the end (for example, determined by never the return type of the function is never.
  • In functions with never return type annotations, all return any, must have an expression of the never type and the end point of the function must be unexe executable.

Because never is a sub-type of each type, it is always omitted in the union type, and the type inference ignores the never type as long as other types never function.

Some examples that return the never function:

// 函数返回never必须无法执行到终点
function error(message: string): never {
    throw new Error(message);
}

// 推断返回类型是never
function fail() {
    return error("Something failed");
}

// 函数返回never必须无法执行到终点
function infiniteLoop(): never {
    while (true) {
    }
}

Some functions return examples of the use of never

// 推断返回类型是number
function move1(direction: "up" | "down") {
    switch (direction) {
        case "up":
            return 1;
        case "down":
            return -1; 
    }
    return error("Should never get here");
}

// 推断返回类型是number
function move2(direction: "up" | "down") {
    return direction === "up" ? 1 :
        direction === "down" ? -1 :
        error("Should never get here");
}

// 推断返回类型是T
function check<T>(x: T | undefined) {
    return x || error("Undefined value");
}

Because never can be assigned to each type, when a callback function is required to return a more specific type, the never can be used to detect that the return type is correct:

function test(cb: () => string) {
    let s = cb();
    return s;
}

test(() => "hello");
test(() => fail());
test(() => { throw new Error(); })

Read-only properties and index signatures

Property or index signatures can now be declared read-only using readonly modifiers.

Read-only properties can be initialized and assigned in constructors of the same class, but in other cases assignments to read-only properties are not allowed.

In addition, there are several cases where entities are implicitly read-only:

  • Property declarations are get read-only using set accessors and not set accessors.
  • In enumerity types, enumerity members are considered read-only properties.
  • In module types, exported const read-only properties.
  • Entities import in the import statement are considered read-only.
  • Entities imported through the ES2015 namespace are considered read-only foo example, foo.x is read-only when declared as import * as foo from "foo"

Example

interface Point {
    readonly x: number;
    readonly y: number;
}

var p1: Point = { x: 10, y: 20 };
p1.x = 5;  // 错误,p1.x是只读的

var p2 = { x: 1, y: 1 };
var p3: Point = p2;  // 正确,p2的只读别名
p3.x = 5;  // 错误,p3.x是只读的
p2.x = 5;  // 正确,但是因为别名使用,同时也改变了p3.x
class Foo {
    readonly a = 1;
    readonly b: string;
    constructor() {
        this.b = "hello";  // 在构造函数中允许赋值
    }
}
let a: Array<number> = [0, 1, 2, 3, 4];
let b: ReadonlyArray<number> = a;
b[5] = 5;      // 错误,元素是只读的
b.push(5);     // 错误,没有push方法(因为这会修改数组)
b.length = 3;  // 错误,length是只读的
a = b;         // 错误,缺少修改数组的方法

Specifies the this the function

Immediately following classes and interfaces, functions and methods can this type of this.

The default this in the function is any S tarting with TypeScript 2.0, you can provide a clear this parameter. this is a pseudo-argument, which is at the first place in the list of function parameters:

function f(this: void) {
    // 确保`this`在这个独立的函数中无法使用
}

This argument in the this function

The library can also this parameter to declare how callback functions are called.

Example

interface UIElement {
    addClickListener(onclick: (this: void, e: Event) => void): void;
}

this:void addClickListener onclick be a function of this argument this does not require a type.

Now if you typed this in this calling code:

class Handler {
    info: string;
    onClickBad(this: Handler, e: Event) {
        // 哎哟,在这里使用this.在运行中使用这个回调函数将会崩溃。
        this.info = e.message;
    };
}
let h = new Handler();
uiElement.addClickListener(h.onClickBad); // 错误!

--noImplicitThis

TypeScript 2.0 also adds a new compilation option to mark the use of all this in functions that do not have explicit this

tsconfig.json file wildcards

File wildcards are coming!! Supporting file wildcards has always been one of the most needed features.

File patterns like file wildcards support two "include" "exclude"

Example

{
    "compilerOptions": {
        "module": "commonjs",
        "noImplicitAny": true,
        "removeComments": true,
        "preserveConstEnums": true,
        "outFile": "../../built/local/tsc.js",
        "sourceMap": true
    },
    "include": [
        "src/**/*"
    ],
    "exclude": [
        "node_modules",
        "**/*.spec.ts"
    ]
}

The symbols that support file wildcards are:

  • * Match zero or more characters (excluding directory)
  • ? Match any one character (excluding directory)
  • **/ Recursively matches all subdirections

If the file wildcard mode * statement contains only .* then only files with .ts and .d.ts by default) are matched, and .js and .jsx are also default if .tsx allowJs is set to true .js

If "files" "include" the compiler defaults to including TypeScript files .ts .d.ts and .tsx in all directories, except those exclude property. If allowJs set to true, JS .js .jsx are also included.

If "files" "include" are specified, the compiler will contain the two properties that specify the set of files. Directory ourDir compilation option are always "exclude" property are files by the file property are not excluded.

"exclude" property "include" by the "include" property. H owever, it has no effect on the files specified by "files" When not explicitly specified, "exclude" property excludes node_modules bower_components jspm_packages default.

Module resolution increases: BaseUrl, path mapping, rootDirs, and tracking

TypeScript 2.0 provides a series of additional module resolution properties that tell the compiler where to find the declaration of a given module.

For more information, see the module resolution documentation.

Base URL

It is common practice to use the AMD module loader and for modules to baseUrl applications at runtime. All module imports with non-relative names are baseUrl

Example

{
  "compilerOptions": {
    "baseUrl": "./modules"
  }
}

Importing moduleA ./modules/moduleA

import A from "moduleA";

The path map

Sometimes the module is not directly in baseUrl. The loader uses a mapping configuration to map module names and files at runtime, see the RequireJs and SystemJS documentation.

The TypeScript compiler tsconfig files that "paths" property map.

Example

For example, "jquery" module is converted "node_modules/jquery/dist/jquery.slim.min.js"

{
  "compilerOptions": {
    "baseUrl": "./node_modules",
    "paths": {
      "jquery": ["jquery/dist/jquery.slim.min"]
    }
}

Using "paths" allows for more complex mapping, including multiple regressing locations. Consider that one module with only one place is available, and the others are configured in another place for the project.

rootDirs and virtual directories

With rootDirs you can tell the compiler's root to combine these "virtual" directories. So the compiler parses relative import modules in these "virtual" directories as if they were merged into one directory.

Example

The given project structure

 src
 └── views
     └── view1.ts (imports './template1')
     └── view2.ts

 generated
 └── templates
         └── views
             └── template1.ts (imports './view2')

The build step /src/views /generated/templates/views directories to the same directory. At runtime, the view expects its template to exist in the same directory as it does, so it should be "./template"

The contents of a set of root directories specified by "rootDir" will be merged at runtime. So in our case, tsconfig.json should look like this:

{
  "compilerOptions": {
    "rootDirs": [
      "src/views",
      "generated/templates/views"
    ]
  }
}

Trace module resolution

--traceResolution a convenient way to understand how modules are parsed by the compiler.

tsc --traceResolution

Quick external module declaration

When you use a new module, if you don't want to spend time writing a declaration, you can now use a shortcut declaration to get started quickly.

declarations.d.ts

declare module "hot-new-module";

All imports from shortcut modules have any type.

import x, {y} from "hot-new-module";
x(y);

A wildcard in the module name

Previously, it was not easy to import code-free resources using module loaders such as AMD and SystemJS. Previously, an external module declaration must be defined for each resource.

TypeScript 2.0 supports the use of wildcard * to define a class of module names. In this way, a claim requires only one extension, not every resource.

Example

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;
}

Now you can import "*!text" or "json!*"

import fileContent from "./xyz.txt!text";
import data from "json!http://example.com/data.json";
console.log(data, fileContent);

The name of a wildcard module may be more useful when migrating from a non-typed code. Combined with a shortcut external module declaration, a set of modules can be easily declared any

Example

declare module "myLibrary/*";

Imports of myLibrary under the myLibrary directory are considered any so any type checks for these modules are turned off.

import { readFile } from "myLibrary/fileSystem/readFile`;

readFile(); // readFile是'any'类型

UMD module definitions are supported

Some libraries are designed to be used using multiple module loaders or not with module loaders (global variables), which are called UMD or omogeneous modules. These libraries can be accessed through imports or global variables.

Example:

math-lib.d.ts
export const isPrime(x: number): boolean;
export as namespace mathLib;

The library can then be used as a module import:

import { isPrime } from "math-lib";
isPrime(2);
mathLib.isPrime(2); // 错误:无法在模块内部使用全局定义

It can also be used as a global variable, limited to files import export scripts.

mathLib.isPrime(2);

Optional class properties

You can now declare optional properties and methods in a class, similar to interfaces.

Example

class Bar {
    a: number;
    b?: number;
    f() {
        return 1;
    }
    g?(): number;  // 可选方法的方法体可以省略
    h?() {
        return 2;
    }
}

When --strictNullChecks optional properties and methods undefined to their type. T herefore, b number | undefined and the type of g method above is (()=> number) | undefined Use type protection to remove undefined

Private and protected constructors

The constructor of the class can be marked private protected A class of a private constructor cannot be instantiated outside the class and cannot be inherited. The class of a protected constructor can no longer be instantiated externally by the class, but can be inherited.

Example

class Singleton {
    private static instance: Singleton;

    private constructor() { }

    static getInstance() {
        if (!Singleton.instance) {
            Singleton.instance = new Singleton();
        }
        return Singleton.instance;
    } 
}

let e = new Singleton(); // 错误:Singleton的构造函数是私有的。
let v = Singleton.getInstance();

Abstract properties and accessors

Abstract classes can declare abstract properties and,or accessors. A ll sub-classes will need to declare abstract properties or be marked as abstract. A bstract properties cannot be initialized. Abstract accessors cannot have concrete blocks of code.

Example

abstract class Base {
    abstract name: string;
    abstract get value();
    abstract set value(v: number);
}

class Derived extends Base {
    name = "derived";

    value = 1;
}

Implicit index signature

If all known properties in the object literal quantity are assigned to the index signature, the object literal type can now be assigned to the index signature type. This makes it possible to pass a variable that uses object literally initialization as an argument to a function that expects the argument to be map or dictionary:

function httpService(path: string, headers: { [x: string]: string }) { }

const headers = {
    "Content-Type": "application/x-www-form-urlencoded"
};

httpService("", { "Content-Type": "application/x-www-form-urlencoded" });  // 可以
httpService("", headers);  // 现在可以,以前不可以。

Compiling --lib contains built-in type declarations

Getting the ES6/ES2015 built-in API declaration is target: ES6 E nter --lib you can use to --lib API required for a set of projects. F or example, if you want your project to run Map Set and Promise (for example, now silently update your --lib es2015.collection,es2015.promise Similarly, you can exclude claims that are not needed in a project, such as --lib es5,es6 node project.

Here's a list of the available APIs:

  • Dom
  • webworker
  • es5
  • es6 / es2015
  • es2015.core
  • es2015.collection
  • es2015.iterable
  • es2015.promise
  • es2015.proxy
  • es2015.reflect
  • es2015.generator
  • es2015.symbol
  • es2015.symbol.wellknown
  • es2016
  • es2016.array.include
  • es2017
  • es2017.object
  • es2017.sharedmemory
  • scripthost

Example

tsc --target es5 --lib es5,es2015.promise
"compilerOptions": {
    "lib": ["es5", "es2015.promise"]
}

Use --noUnusedParameters --noUnusedLocals claims

TypeScript 2.0 has two new compilation parameters to help you keep a clean code base. -noUnusedParameters to mark all unused function or method parameter errors. --noUnusedLocals like variables, functions, classes, imports, and so on, and unused private class members are also marked as errors under --noUnusedLocals

Example

import B, { readFile } from "./b";
//     ^ 错误:`B`声明了,但是没有使用。
readFile();


export function write(message: string, args: string[]) {
    //                                 ^^^^  错误:'arg'声明了,但是没有使用。
    console.log(message);
}

Argument declarations _ named after the beginning of the s are not checked by unused parameters. For example:

function returnNull(_a) { // 正确
    return null;
}

The module name .js to use the extension

Until TypeScript 2.0, the module name was always considered to have no extension. F or example, importing a import d from "./moduleA.js" ./moduleA.js.ts or ./moduleA.js.d.ts "moduleA.js" This makes packaging or loading tools like SystemJS that expect module names to be URIs difficult to use.

Using TypeScript 2.0, the compiler will look for the definition of ./moduleA.ts in ./moduleA.d.ts "moduleA.js"

Supports the compilation target : es5 module: es6 at the same time

Previously compiled target : es5 module: es6 considered invalid at the same time, but are now valid. This will help with ES2015-based tree-shaking (removing useless code) such as rollup.

Commas are supported at the end of the function parameter and argument list

A comma is now allowed at the end of the function parameter and argument list. This is an implementation of the phase III ECMAScript proposal and compiles into available ES3/ES5/ES6.

Example

function foo(
  bar: Bar, 
  baz: Baz, // 形参列表末尾添加逗号是没有问题的。
) {
  // 具体实现……
}

foo(
  bar,
  baz, // 实参列表末尾添加逗号同样没有问题
);

New compilation parameter --skipLibCheck

TypeScript 2.0 adds a new compilation --skipLibCheck that skips the type check of the declaration .d.ts as its extension). When a program contains a large number of claims files, the compiler spends a lot of time type checking claims that are known to contain errors, and by skipping the type check of the claims file, the compilation time can be greatly reduced.

Because declarations in one file can affect type checks in other files, some errors may not be detected when you specify --skipLibCheck F or example, if a type in a non-declared file is used by a declared file, an error may only be found when the declaration file is checked. However, this is not common in real-world use.

Allow duplicate identifiers in declarations

This is a common source of repeated definition errors. Multiple claims files define the same interface members.

TypeScript 2.0 relaxes this constraint and allows duplicate identifiers to appear in different blocks of code, as long as they have exactly the same type.

Repeated definitions in the same block of code are still not allowed.

Example

interface Error {
    stack?: string;
}


interface Error {
    code?: string;
    path?: string;
    stack?: string;  // OK
}

The newly compiled --declarationDir

--declarationDir make the resulting claims file and JavaScript file out of the same place.