TypeScript 基本概念回顧整理


title: TypeScript 回顧整理

date: 2019/10/08 15:04:01

tags:

  • 前端

  • TS

categories:

  • 前端

Vue3.0 最近發布了pre-alpha版本,基本都是由TS編寫的,借此機會回顧一下TypeScript相關的概念和知識點

基礎用法

基本數據類型

JS中的原始基本數據類型:布爾值、數值、字符串、nullundefined 以及 ES6 中的新類型 Symbol

boolean

布爾值是最基礎的數據類型,在 TypeScript 中,使用 boolean 定義布爾值類型:


let isDone: boolean = false;

注意,使用構造函數 Boolean 創造的對象不是布爾值


let createdByNewBoolean: boolean = new Boolean(1);
// Type 'Boolean' is not assignable to type 'boolean'.

//   'boolean' is a primitive, but 'Boolean' is a wrapper object. Prefer using 'boolean' when possible.

事實上 new Boolean() 返回的是一個 Boolean 對象


let createdByNewBoolean: Boolean = new Boolean(1);

直接調用 Boolean 也可以返回一個 boolean 類型:


let createdByBoolean: boolean = Boolean(1);

在 TypeScript 中,boolean 是 JavaScript 中的基本類型,而 Boolean 是 JavaScript 中的構造函數, 不屬于基本類型。其他基本類型(除了 null 和 undefined)一樣。

number


let decLiteral: number = 6;

let hexLiteral: number = 0xf00d;

// ES6 中的二進制表示法

let binaryLiteral: number = 0b1010;

// ES6 中的八進制表示法

let octalLiteral: number = 0o744;
let notANumber: number = NaN;

let infinityNumber: number = Infinity;

上述的二進制和八進制編譯為js之后都會轉為十進制。

string


let myName: string = 'Tom';

let myAge: number = 25;

let info: string = `Hello, i am ${myName} and i am ${myAge} years old`;

對于模板字符串來講,編譯為JS時會改為字符串拼接的形式

空值void

js中不存在void的概念,在ts中也只有聲明一個函數沒有返回值的時候才使用void(注意不是undefined!!!)


function alertName(): void {

    alert('My name is Tom');

}

而如果要聲明一個void變量的話(無意義的操作),只能賦值為null或者undefined


let voidVal: void = undefined;

null和undefined


let u: undefined = undefined;

let n: null = null;

在TS中,undefined和null是所有類型的子類!這意味著你可以賦值給所有的類型一個null或者undefined


let num: number = undefined;

但是void類型就不行

任意值 Any

任意值(Any)用來表示允許賦值為任意類型

聲明為any類型的變量可以進行下面的操作:


let myFavoriteNumber: any = 'seven';

myFavoriteNumber = 7;

any的屬性和方法

在任意值上訪問任何屬性都是允許的:


// 編譯通過

let anyThing: any = 'hello';

console.log(anyThing.myName);

console.log(anyThing.myName.firstName);


// 編譯通過

let anyThing: any = 'Tom';

anyThing.setName('Jerry');

anyThing.setName('Jerry').sayHello();

anyThing.myName.setFirstName('Cat');

聲明一個變量為任意值之后,對它的任何操作,返回的內容的類型都是任意值。

未聲明類型的變量

變量如果在聲明的時候,未指定其類型,那么它會被識別為any類型:


// 編譯通過

let something;

something = 'seven';

something = 7;



something.setName('Tom');

類型推斷

TypeScript 會在沒有明確的指定類型的時候推測出一個類型,這就是類型推論。


let myFavoriteNumber = 'seven';

myFavoriteNumber = 7;

// index.ts(2,1): error TS2322: Type 'number' is not assignable to type 'string'.

但是值得注意的是:

如果定義的時候沒有賦值,不管之后有沒有賦值,都會被推斷成 any 類型而完全不被類型檢查:

聯合類型Union Types


let myFavoriteNumber: string | number;

myFavoriteNumber = 'seven';

myFavoriteNumber = 7;

訪問聯合類型的屬性或者方法

當 TypeScript 不確定一個聯合類型的變量到底是哪個類型的時候,我們只能訪問此聯合類型的所有類型里共有的屬性或方法


function getLength(something: string | number): number {

    return something.length;

}



// index.ts(2,22): error TS2339: Property 'length' does not exist on type 'string | number'.

//   Property 'length' does not exist on type 'number'.

聯合類型的變量在被賦值的時候,會根據類型推論的規則推斷出一個類型,此后再被賦值為第二種類型之后,訪問其不存在的屬性或者方法就會報錯


let myFavoriteNumber: string | number;

myFavoriteNumber = 'seven';

console.log(myFavoriteNumber.length); // 5

myFavoriteNumber = 7;

console.log(myFavoriteNumber.length); // 編譯時報錯



// index.ts(5,30): error TS2339: Property 'length' does not exist on type 'number'.

類型守衛(Type Guards)與類型區分(Differentiating Types)

假設現在有如下兩個類型:


interface Fish {

    swim(): void;

    sayHi(): void;

}



interface Bird {

    fly(): void;

    sayHi(): void

}

對于聯合類型Fish | Bird,引出來一個問題,當對于一個聯合類型來講,怎么確切的知道當前數據為哪個具體類型呢?


// 每一個成員訪問都會報錯

if(animal.swim) { // 報錯 animal類型未知  不可訪問swim方法

    animal.swim();

}else {

    animal.fly();

}

上例中由于類型未定,所以訪問每一個類型獨有的方法都會報錯,所以為了使其生效,只能使用類型斷言:


if((animal as Fish).swim) {

    (animal as Fish).swim();

}else {

    (animal as Bird).fly();

}

這里我們注意到,雖然在if語句里已經判斷了類型,但是在分支內部想調用特有方法時,還是需要再使用一次斷言(animal as Fish).swim();,而TypeScript里的類型守衛機制可以使我們一旦檢查過類型,就能在之后的每個分支里清楚地知道變量的類型。

用戶自定義類型守衛

類型守衛主要分為:

  1. 使用類型判定

  2. 使用in操作符

  3. typeof類型守衛

  4. instanceof類型守衛

使用類型判定

要定義一個類型守衛,我們只要簡單地定義一個函數,它的返回值是一個類型謂詞


function isFish(animal: Fish | Bird): animal is Fish {

    // return !!(animal as Fish).swim;

    return (animal as Fish).swim !== undefined;

}

在這個例子里,animal is Fish就是類型謂詞。 謂詞為parameterName is Type這種形式,parameterName必須是來自于當前函數簽名里的一個參數名。

每當使用一些變量調用isFish時,TypeScript會將變量縮減為那個具體的類型,只要這個類型與變量的原始類型是兼容的。


if(isFish(animal)) {

    animal.swim(); // 調用類型判定之后  在這個if分支里 TS已經知道animal確定是Fish類型的了

    // 所以不需要再使用類型斷言進行調用

}else {

    // 同理 在這個分支里 已經知道是Bird類型了

    animal.fly();

}

注意TypeScript不僅知道在if分支里pet是Fish類型; 它還清楚在else分支里,一定不是Fish類型,即一定是Bird類型。

使用in操作符

in操作符可以作為類型細化表達式來使用。

對于n in x表達式,其中n是字符串字面量或字符串字面量類型且x是個聯合類型,那么true分支的類型細化為有一個可選的或必須的屬性nfalse分支的類型細化為有一個可選的或不存在屬性n


if("swim" in animal) {

    animal.swim();

}else {

    animal.fly();

}

typeof類型守衛

對于原始類型來說,可以不用那么麻煩去寫斷言,直接通過typeof就可以進行類型判定:


function getLength(s: string | number) {

    if(typeof s === "string") {

        return s.length;

    }

    return s.toString().length;

}

這些typeof類型守衛只有兩種形式能被識別:typeof v === "typename"typeof v !== "typename""typename"必須是"number""string""boolean""symbol"。 但是TypeScript并不會阻止你與其它字符串比較,語言不會把那些表達式識別為類型守衛。

instanceof類型守衛

instanceof類型守衛是通過構造函數來細化類型的一種方式。


abstract class Fish {

    abstract swim(): void;

    abstract sayHi(): void;

}



abstract class Bird {

    abstract fly(): void;

    abstract sayHi(): void

}



let animal: Fish | Bird;

if(animal instanceof Fish) {

    animal.swim(); // 通過instanceof也可以進行區分

}else {

    animal.fly();

}

instanceof的右側要求是一個構造函數,TypeScript將細化為:

  1. 此構造函數的prototype屬性的類型,如果它的類型不為any的話

  2. 構造簽名所返回的類型的聯合

以此順序。

交叉類型(Intersection Types)

交叉類型是將多個類型合并為一個類型。 這讓我們可以把現有的多種類型疊加到一起成為一種類型,它包含了所需的所有類型的特性:


interface Fish {

    swim(): void;

    sayHi(): void;

}



interface Bird {

    fly(): void;

    sayHi(): void

}


let strangeAnimal: Fish & Bird = {

    fly(): void {

        console.log("flying");

    },

    swim(): void {

        console.log("swimming");

    },

    sayHi(): void {

        console.log("hi~");

    }

};



上例中的strangeAnimal的類型為Fish & Bird,這個類型的對象同時擁有了這兩種類型的成員。這個變量既為Fish類型,同時也是Bird類型。

接口——對象的類型

TypeScript 中的接口是一個非常靈活的概念,除了可用于對類的一部分行為進行抽象以外,也常用于對「對象的形狀(Shape)」進行描述。一般首字母大寫.

基本用法


interface Person {

    name: string;

    age: number;

}



let tom: Person = {

    name: 'Tom',

    age: 25

};

此時約束的對象不允許缺少接口的定義的屬性也不允許多加屬性。

可選屬性


interface Person {

    name: string;

    age?: number;

}



let tom: Person = {

    name: 'Tom'

};

此時age屬性可有可無,但是依舊不可以多加屬性

任意屬性


interface Person {

    name: string;

    age?: number;

    [propName: string]: any;

}



let tom: Person = {

    name: 'Tom',

    gender: 'male'

};

此時可以多加任意的屬性。

使用[propName: string]定義了任意屬性取 string 類型的值。

需要注意的是,一旦定義了任意屬性,那么確定屬性和可選屬性的類型都必須是它的類型的子集:

只讀屬性


interface Person {

    readonly id: number;

    name: string;

    age?: number;

    [propName: string]: any;

}



let tom: Person = {

    id: 89757,

    name: 'Tom',

    gender: 'male'

};



tom.id = 9527;



// index.ts(14,5): error TS2540: Cannot assign to 'id' because it is a constant or a read-only property.

只讀屬性只讀的約束存在于第一次給對象賦值的時候,而不是第一次給只讀屬性賦值的時候:

數組的類型

數組定義有幾種方法:

「類型 + 方括號」表示法


let fibonacci: number[] = [1, 1, 2, 3, 5];

number[]類型的數組中不允許出現第二種類型(如string)的數據,相應的,如果對數組進行操作進行新增時,也不允許加入其他類型的數據。


let fibonacci: number[] = [1, 1, 2, 3, 5];

fibonacci.push('8');



// Argument of type '"8"' is not assignable to parameter of type 'number'.

數組泛型

使用Array<elemType>來表示數組


let fibonacci: Array<number> = [1, 1, 2, 3, 5];

用接口表示數組(不常用)


interface NumberArray {

    [index: number]: number;

}

let fibonacci: NumberArray = [1, 1, 2, 3, 5];

注意這么寫之后,變量其實不是數組類型,如push等方法是不可使用的。 本質上僅僅是個類數組類型。

類數組

對于arguments 等類數組, 不能用普通的數組的方式來描述,而應該用接口

TS中的內置對象中,就使用了這種方法:


interface IArguments {

    [index: number]: any;

    length: number;

    callee: Function;

}

上述的IArguments接口即規定了arguments的類型:


function sum() {

    let args: IArguments = arguments;

}

函數的類型

聲明式定義

對于聲明式的函數定義,規定輸入參數的個數和類型即可以及輸出的類型即可。


function sum(x: number, y: number): number {

    return x + y;

}

注意: 參數不得少于或者多余函數的規定

函數表達式

如果想通過表達式方式定義函數的話,可以直接寫為:


let mySum = function (x: number, y: number): number {

    return x + y;

};

或者寫為箭頭函數形式:


let mySum = (x: number, y: number): number => {

    return x + y;

};

但是值得注意的是,這塊的sum其實是有類型的,只不過沒有顯示定義而TS幫我們進行了類型推斷而已,如果要顯式的定義類型,需要寫為:


let mySum: (x: number, y: number) => number = (x: number, y: number): number => {

    return x + y;

};

注意這邊的第一個=>代表著函數的類型,規定了函數的返回值,而第二個=>則是箭頭函數

接口表示函數

還可以使用接口表示函數:


interface SearchFunc {

    (source: string, subString: string): boolean;

}



let mySearch: SearchFunc;

mySearch = function(source: string, subString: string) {

    return source.search(subString) !== -1;

}

函數的可選參數


function buildName(firstName: string, lastName?: string) {

    if (lastName) {

        return firstName + ' ' + lastName;

    } else {

        return firstName;

    }

}

可選參數必須接在必需參數后面

函數參數的默認值


function buildName(firstName: string, lastName: string = 'Cat') {

    return firstName + ' ' + lastName;

}

函數的...rest參數(剩余參數)


function push(array: any[], ...items: number[]) {

    items.forEach(function(item) {

        array.push(item);

    });

}

push([], 1, 2, 3);

這邊的...items代表1,2,3, items即為[1,2,3]為number[]類型

rest 參數只能是最后一個參數

函數的重載


function reverse(x: number): number;

function reverse(x: string): string;

function reverse(x: number | string): number | string {

    if (typeof x === 'number') {

        return Number(x.toString().split('').reverse().join(''));

    }

    return x.split('').reverse().join('');

}



let a: number = reverse(123);

let b: number = reverse("abc"); // 編譯報錯  Type 'string' is not assignable to type 'number' .

前2次函數定義代表傳入的是number返回的也會是number,傳入string返回的也是string,最后的才為函數的實現

重載時會從最前面的定義開始進行匹配,所以優先把最精確的寫在最前

類型斷言(Type Assertion)

用來手動指定一個值的類型

斷言語法

  1. <類型>值

  2. 值 as 類型

在tsx中只能使用 值 as 類型的語法

可以使用類型斷言對聯合類型進行斷言,但是不可斷言聯合類型之外的類型:


function getLength(something: string | number): number {

    if ( (something as string).length ) {

        return (<string>something).length;

    }

    return something.toString().length;

}

對于上例來說,參數為string|number,這里的參數直接可以斷言為string,但是不可以斷言為string | number之外的類型

類型斷言并不是類型轉換

聲明文件

// TODO

待補充

內置對象

ECMAScript的內置對象

ECMAScript中一些內置對象如BooleanErrorDateRegExp 等,在TS中可以直接定義:


let b: Boolean = new Boolean(1);

let e: Error = new Error('Error occurred');

let d: Date = new Date();

let r: RegExp = /[a-z]/;

因為在 TypeScript 核心庫的定義文件中定義了這些內置對象。

DOM 和 BOM 的內置對象

例如DocumentHTMLElementEventNodeList 等。


let body: HTMLElement = document.body;

let allDiv: NodeList = document.querySelectorAll('div');

document.addEventListener('click', function(e: MouseEvent) {

  // Do something

});

進階

類型別名

類型別名用來給一個類型起個新名字。


type Name = string;

let s: Name = "abc";

字符串字面量類型

字符串字面量類型用來約束取值只能是某幾個字符串中的一個。


type EventNames = 'click' | 'scroll' | 'mousemove';

function handleEvent(ele: Element, event: EventNames) {

    // do something

}



handleEvent(document.getElementById('hello'), 'scroll');  // 沒問題

handleEvent(document.getElementById('world'), 'dbclick'); // 報錯,event 不能為 'dbclick'



// index.ts(7,47): error TS2345: Argument of type '"dbclick"' is not assignable to parameter of type 'EventNames'.

元組

數組合并了相同類型的對象,而元組(Tuple)合并了不同類型的對象。


let tom: [string, number] = ['Tom', 25];

基本操作不贅述,值得注意的是,元組支持越界,但是越界時新增的元素必須是前面規定的那些類型之一


let tom: [string, number];

tom = ['Tom', 25];

tom.push('male'); // 越界時允許push的類型 本例中為 string | number

tom.push(true); // 編譯報錯

枚舉

枚舉(Enum)類型用于取值被限定在一定范圍內的場景,比如一周只能有七天,顏色限定為紅綠藍等。

枚舉成員會被賦值為從 0 開始遞增的數字,同時也會對枚舉值到枚舉名進行反向映射

enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};

console.log(Days["Sun"] === 0); // true
console.log(Days["Mon"] === 1); // true
console.log(Days["Tue"] === 2); // true
console.log(Days["Sat"] === 6); // true

console.log(Days[0] === "Sun"); // true
console.log(Days[1] === "Mon"); // true
console.log(Days[2] === "Tue"); // true
console.log(Days[6] === "Sat"); // true

給枚舉手動賦值

枚舉可以在初始化的時候手動為其每一項賦值,未被賦值的成員會上一個枚舉項遞增

enum Days {Sun = 7, Mon = 1, Tue, Wed, Thu, Fri, Sat};

console.log(Days["Sun"] === 7); // true
console.log(Days["Mon"] === 1); // true
console.log(Days["Tue"] === 2); // true
console.log(Days["Sat"] === 6); // true

本例中Tue開始就未賦值,所以會接著上一個枚舉項即Mon開始接著遞增。

賦值時允許多個項的值相等,但是會造成覆蓋情況!

對于上述的覆蓋情況,舉個例子解釋一下:

enum Colors = {red = 1, blue = 1, green};

會被編譯為:

var Colors ;
(function (Colors ) {
    Colors [Colors ["red"] = 1] = "red";
    Colors [Colors ["blue "] = 1] = "blue ";
    Colors [Colors ["green"] = 2] = "green";
})(Colors || (Colors = {}));

實際最后的Colors為:

{
    1: "blue",
    2: "green",
    blue: 1,
    red: 1,
    green: 2
}

可以看到,對于index的訪問方式來說,由于下標相同,之前的1: red已經被覆蓋為1: blue,所以要避免枚舉值賦值重復。

枚舉的計算所得項

對于上述的

enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};

即為常數項的枚舉,在枚舉中也可以使用計算所得項:

enum Color {Red, Green, Blue = "blue".length};

但是要注意的是:如果緊接在計算所得項后面的是未手動賦值的項,那么它就會因為無法獲得初始值而報錯

enum Color {Red = "red".length, Green, Blue};

// index.ts(1,33): error TS1061: Enum member must have initializer.
// index.ts(1,40): error TS1061: Enum member must have initializer.

詳見中文手冊

常數枚舉

注意和上面的常數項不是一個東西,常數枚舉值得是通過const定義的枚舉,即:

使用const enum定義的即為常數枚舉,常數枚舉會在編譯階段被刪除,并且不能包含計算成員。

const enum Directions {
    Up,
    Down,
    Left,
    Right
}

let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];

編譯為JS后代碼為:

var directions = [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */];

外部枚舉

指的是通過declare enum定義的枚舉

declare enum Directions {
    Up,
    Down,
    Left,
    Right
}

let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];

編譯為:

var directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];

declare 定義的類型只會用于編譯時的檢查,編譯結果中會被刪除。

另外如果搭配const定義,會被編譯為常數枚舉。

TS中的類

public private 和 protected

TypeScript 可以使用三種訪問修飾符(Access Modifiers),分別是 publicprivateprotected

constructor 設為private和protected

防止類被繼承(即final class)且不能實例化的話,在TS中要講其constructor設為private

class Animal {
    public name;
    private constructor (name) {
        this.name = name;
  }
}
class Cat extends Animal {
    constructor (name) {
        super(name);
    }
}

let a = new Animal('Jack');

// index.ts(7,19): TS2675: Cannot extend a class 'Animal'. Class constructor is marked as private.
// index.ts(13,9): TS2673: Constructor of class 'Animal' is private and only accessible within the class declaration.

而要類只不能實例話還可以被繼承的話,要使用protected修飾constructor

class Animal {
    public name;
    protected constructor (name) {
        this.name = name;
  }
}
class Cat extends Animal {
    constructor (name) {
        super(name);
    }
}

let a = new Animal('Jack');

// index.ts(13,9): TS2674: Constructor of class 'Animal' is protected and only accessible within the class declaration.

此外修飾符還可以使用在構造函數參數中,等同于類中定義該屬性,使代碼更簡潔:

class Animal {
    // public name: string;
    public constructor (public name) {
        this.name = name;
    }
}

抽象類

TS中的抽象類基本和Java一致,不多贅述:

abstract class Animal {
    public name;
    public constructor(name) {
        this.name = name;
    }
    public abstract sayHi();
}

let a = new Animal('Jack');

類與接口

接口還可以對類的一部分行為進行抽象

interface Alarm {
    alert();
}

interface Light {
    lightOn();
    lightOff();
}

class Car implements Alarm, Light {
    alert() {
        console.log('Car alert');
    }
    lightOn() {
        console.log('Car light on');
    }
    lightOff() {
        console.log('Car light off');
    }
}

補充一點: TS中接口可以繼承類

// TS中接口可以繼承類:
abstract class Point {
    x: number;
    y: number;
    abstract showPoint(): void;
}

interface Point3d extends Point {
    z: number;
}

let point3d: Point3d = {
    x: 1, y : 1, z: 1,
    showPoint(): void {
      console.log("");
    }
};

此外接口也可以繼承接口

函數的屬性和方法

函數可以擁有自己的屬性和方法:

interface Counter {
    (start: number): string;
    interval: number;
    reset(): void;
}

let counter = <Counter>function (start: number) { };
counter.interval = 123;
counter.reset = function () { };

// 這里的counter既是一個函數,也擁有自己的屬性和方法

泛型

泛型的概念不再贅述,來看基本語法:

function createArray<T>(length: number, value: T): Array<T> {
    let result: T[] = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}

createArray<string>(3, 'x'); // ['x', 'x', 'x']

泛型約束

在函數內部使用泛型變量的時候,由于事先不知道它是哪種類型,所以不能隨意的操作它的屬性或方法

function getLength<T>(x: T): number() {
    return x.length;
}
// error TS2339: Property 'length' does not exist on type 'T'.

上述例子中由于T的類型不明,所以無法訪問length屬性。

這時可以對泛型進行約束,只允許這個函數傳入那些包含 length 屬性的變量,即泛型約束:

interface LengthAble {
    length: number;
}

function getLength<T extends LengthAble>(x: T): number {
    return x.length;
}

另外泛型之間也可以相互約束,比如在function fn<T extends U, U>(x: T, y: U) {},強制要求了前一個參數的類型繼承自后一個類型。

泛型接口

泛型也可以應用在接口上:

interface CreateArrayFunc {
    <T>(length: number, value: T): Array<T>;
}

let createArray: CreateArrayFunc;
createArray = function<T>(length: number, value: T): Array<T> {
    let result: T[] = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}

createArray(3, 'x'); // ['x', 'x', 'x']

也可以直接將泛型寫到接口上:

interface CreateArrayFunc<T> {
    (length: number, value: T): Array<T>;
}

let createArray: CreateArrayFunc<any>;
createArray = function<T>(length: number, value: T): Array<T> {
    let result: T[] = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}

createArray(3, 'x'); // ['x', 'x', 'x']

泛型類

class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
}

let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };

泛型參數的默認類型

TS2.3以后新增了一個泛型參數的默認類型,使用泛型時沒有在代碼中直接指定類型參數,從實際值參數中也無法推測出時,會采用這個默認類型

function createArray<T = string>(length: number, value: T): Array<T> {
    let result: T[] = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}

聲明合并

如果定義了兩個相同名字的函數接口命名空間等,那么它們會合并成一個類型

函數合并

函數的重載就是函數的合并

function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string {
    if (typeof x === 'number') {
        return Number(x.toString().split('').reverse().join(''));
    } else if (typeof x === 'string') {
        return x.split('').reverse().join('');
    }
}

接口的合并

  1. 屬性合并:
interface Alarm {
    price: number;
}
interface Alarm {
    weight: number;
}

等價于:

interface Alarm {
    price: number;
    weight: number;
}

合并的屬性的類型必須是唯一的

interface Alarm {
    price: number;
}
interface Alarm {
    price: number;  // 雖然重復了,但是類型都是 `number`,所以不會報錯
    weight: number;
}
interface Alarm {
    price: number;
}
interface Alarm {
    price: string;  // 類型不一致,會報錯
    weight: number;
}

// index.ts(5,3): error TS2403: Subsequent variable declarations must have the same type.  Variable 'price' must be of type 'number', but here has type 'string'.
  1. 方法的合并

內部方法的合并和函數的合并原則相同

interface Alarm {
    price: number;
    alert(s: string): string;
}
interface Alarm {
    weight: number;
    alert(s: string, n: number): string;
}

等價于:

interface Alarm {
    price: number;
    weight: number;
    alert(s: string): string;
    alert(s: string, n: number): string;
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,702評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,143評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,553評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,620評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,416評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,940評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,024評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,170評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,709評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,597評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,784評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,291評論 5 357
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,029評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,407評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,663評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,403評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,746評論 2 370

推薦閱讀更多精彩內容