TypeScript 代码风格指南

@typescript-eslint/adjacent-overload-signaturesopen in new window

重载的函数必须写在一起

// bad
declare namespace NSFoo1 {
    export function foo(s: string): void;

    export function foo(n: number): void;

    export function bar(): void;

    export function foo(sn: string | number): void;
}

type TypeFoo1 = {
    foo(s: string): void;
    foo(n: number): void;
    bar(): void;
    foo(sn: string | number): void;
};

interface IFoo1 {
    foo(s: string): void;

    foo(n: number): void;

    bar(): void;

    foo(sn: string | number): void;
}
// good
declare namespace NSFoo2 {
    export function foo(s: string): void;

    export function foo(n: number): void;

    export function foo(sn: string | number): void;

    export function bar(): void;
}

type TypeFoo2 = {
    foo(s: string): void;
    foo(n: number): void;
    foo(sn: string | number): void;
    bar(): void;
};

interface IFoo2 {
    foo(s: string): void;

    foo(n: number): void;

    foo(sn: string | number): void;

    bar(): void;
}

增加可读性

@typescript-eslint/naming-conventionopen in new window

类名与接口名必须为驼峰式

// bad
class Invalid_Class_Name {
}

interface invalidInterface {
}
// good
class ValidClassName {
}

interface ValidInterface {
}

@typescript-eslint/consistent-type-assertionsopen in new window

类型断言必须使用 as Type,禁止使用 <Type>,禁止对对象字面量进行类型断言(断言成 any 是允许的)

// bad
let bar1: string | number;
const foo1 = <string>bar1;

const baz1 = {
    bar: 1
} as object;
// good
let bar2: string | number;
const foo2 = bar2 as string;

const baz2 = {
    bar: 1
} as any;

<Type> 容易被理解为 jsx

@typescript-eslint/consistent-type-definitionsopen in new window

优先使用 interface 而不是 type

// bad
type Foo1 = {
    foo: string;
};
// good
interface Foo2 {
    foo: string;
}

interface 可以 implement, extend 和 merge

@typescript-eslint/explicit-member-accessibilityopen in new window

必须设置类的成员的可访问性

// bad
class Foo2 {
    static foo = 'foo';

    static getFoo() {
        return Foo2.foo;
    }

    constructor() {
    }

    bar = 'bar';

    getBar() {
    }

    get baz() {
        return 'baz';
    }

    set baz(value) {
        console.log(value);
    }
}
// good
class Foo2 {
    private static foo = 'foo';

    public static getFoo() {
        return Foo2.foo;
    }

    public constructor() {
    }

    protected bar = 'bar';

    public getBar() {
    }

    public get baz() {
        return 'baz';
    }

    public set baz(value) {
        console.log(value);
    }
}

将不需要公开的成员设为私有的,可以增强代码的可理解性,对文档输出也很友好

@typescript-eslint/member-orderingopen in new window

指定类成员的排序规则

// bad
class Foo1 {
    private getBar3() {
        return this.bar3;
    }

    protected getBar2() {
    }

    public getBar1() {
    }

    public constructor() {
        console.log(Foo1.getFoo3());
        console.log(this.getBar3());
    }

    private bar3 = 'bar3';
    protected bar2 = 'bar2';
    public bar1 = 'bar1';

    private static getFoo3() {
        return Foo1.foo3;
    }

    protected static getFoo2() {
    }

    public static getFoo1() {
    }

    private static foo3 = 'foo3';
    protected static foo2 = 'foo2';
    public static foo1 = 'foo1';
}
// good
class Foo2 {
    public static foo1 = 'foo1';
    protected
    static foo2 = 'foo2';
    private static foo3 = 'foo3';

    public static getFoo1() {
    }

    protected static getFoo2() {
    }

    private static getFoo3() {
        return Foo2.foo3;
    }

    public bar1 = 'bar1';
    protected bar2 = 'bar2';
    private bar3 = 'bar3';

    public constructor() {
        console.log(Foo2.getFoo3());
        console.log(this.getBar3());
    }

    public getBar1() {
    }

    protected getBar2() {
    }

    private getBar3() {
        return this.bar3;
    }
}

TIP

优先级:

  1. static > instance
  2. field > constructor > method
  3. public > protected > private

@typescript-eslint/no-empty-interfaceopen in new window

禁止定义空的接口

// bad
interface Foo1 {
}
// good
interface Foo2 {
    foo: string;
}

@typescript-eslint/no-inferrable-typesopen in new window

禁止给一个初始化时直接赋值为 number, string 的变量显式的声明类型

// bad
const foo1: number = 1;
const bar1: string = '';
// good
const foo2 = 1;
const bar2 = '';

可以简化代码

@typescript-eslint/no-namespaceopen in new window

禁止使用 namespace 来定义命名空间

// bad
namespace foo1 {
}
// good
declare namespace foo1 {
}

使用 es6 引入模块,才是更标准的方式。 但是允许使用 declare namespace ... {} 来定义外部命名空间

@typescript-eslint/no-parameter-propertiesopen in new window

禁止给类的构造函数的参数添加修饰符

// bad
class Foo1 {
    constructor(private name: string) {
    }
}
// good
class Foo2 {
    constructor(name: string) {
    }
}

@typescript-eslint/no-require-importsopen in new window

禁止使用 require

// bad
const fs = require('fs');
// good
import * as fs from 'fs';

统一使用 import 来引入模块,特殊情况使用单行注释允许 require 引入

@typescript-eslint/no-this-aliasopen in new window

禁止将 this 赋值给其他变量,除非是解构赋值

// bad
function foo() {
    const self = this;
    setTimeout(function () {
        self.doWork();
    });
}
// good
function foo() {
    const {bar} = this;
    setTimeout(() => {
        this.doWork();
    });
}

@typescript-eslint/no-useless-constructoropen in new window

禁止出现没必要的 constructor

// bad
class Foo1 {
    constructor() {
    }
}

class Bar1 extends Foo1 {
    constructor() {
        super();
    }
}
// good
class Foo2 {
    constructor() {
        this.doSomething();
    }

    doSomething() {
    }
}

class Bar2 extends Foo1 {
    constructor() {
        super();
        this.doSomething();
    }

    doSomething() {
    }
}

@typescript-eslint/prefer-for-ofopen in new window

使用 for 循环遍历数组时,如果索引仅用于获取成员,则必须使用 for of 循环替代 for 循环

// bad
const arr1 = [1, 2, 3];

for (let i = 0; i &lt; arr1.length; i++) {
    console.log(arr1[i]);
}
// good
const arr2 = [1, 2, 3];

for (const x of arr2) {
    console.log(x);
}

for (let i = 0; i &lt; arr2.length; i++) {
    // i is used to write to arr, so for-of could not be used.
    arr2[i] = 0;
}

for (let i = 0; i &lt; arr2.length; i++) {
    // i is used independent of arr, so for-of could not be used.
    console.log(i, arr2[i]);
}

for of 循环更加易读

@typescript-eslint/prefer-function-typeopen in new window

可以简写为函数类型的接口或字面类型的话,则必须简写

// bad
interface Foo1 {
    (): string;
}
// good
type Foo2 = () => string;

@typescript-eslint/prefer-namespace-keywordopen in new window

禁止使用 module 来定义命名空间

// bad
module Foo1 {
}
// good
namespace Foo2 {
}

module 已成为 js 的关键字

@typescript-eslint/triple-slash-referenceopen in new window

禁止使用三斜杠导入文件

// bad
/// <reference path="./Animal">
// good
import Animal from './Animal';

三斜杠是已废弃的语法,但在类型声明文件中还是可以使用的

@typescript-eslint/typedefopen in new window

interface 和 type 定义时必须声明成员的类型

// bad
type Foo1 = {
    bar;
    baz;
};
// good
type Foo2 = {
    bar: boolean;
    baz: string;
};

@typescript-eslint/unified-signaturesopen in new window

函数重载时,若能通过联合类型将两个函数的类型声明合为一个,则使用联合类型而不是两个函数声明

// bad
function foo1(x: number): void;

function foo1(x: string): void;

function foo1(x: any): any {
    return x;
}
// good
function foo2(x: number | string): void;

function foo2(x: any): any {
    return x;
}