2019-11-14 typeScript 總結(jié):

簡(jiǎn)介

TypeScript 是 JavaScript 的一個(gè)超集,主要提供了 類型系統(tǒng) 和對(duì) ES6 的支持,由 Microsoft 開發(fā)厚脉。

文檔地址:https://ts.xcatliu.com/basics/declaration-files

應(yīng)用:vue3.0攀细,angular2.0,vscode...

  1. 編譯型語(yǔ)言:編譯為 js 后運(yùn)行腹备,單獨(dú)無(wú)法運(yùn)行;
  2. 強(qiáng)類型語(yǔ)言;
  3. 面向?qū)ο蟮恼Z(yǔ)言;

優(yōu)勢(shì)

  1. 類型系統(tǒng)實(shí)際上是最好的文檔,大部分的函數(shù)看看類型的定義就可以知道如何使用斤蔓;
  2. 可以在編譯階段就發(fā)現(xiàn)大部分錯(cuò)誤箭跳,這總比在運(yùn)行時(shí)候出錯(cuò)好;
  3. 增強(qiáng)了編輯器和 IDE 的功能慨削,包括代碼補(bǔ)全旨涝、接口提示、跳轉(zhuǎn)到定義驾锰、重構(gòu)等卸留;

總結(jié):TypeSctipt增加了代碼的可讀性和可維護(hù)性。

安裝

需要有node環(huán)境椭豫,通過npm安裝

npm install -g typescript

編碼

在線編譯預(yù)覽 TS

使用 .ts 文件擴(kuò)展名耻瑟, 使用 typescript 編寫使用 React 時(shí),使用 .tsx 擴(kuò)展名赏酥。

使用 : 指定變量的類型喳整,: 的前后有沒有空格都可以;

function sayHello(person: string) {
    return 'Hello, ' + person;
}

let user = 'Tom';
console.log(sayHello(user));

編譯

使用tsc 命令可編譯 .ts 文件, 生成一個(gè)同名 .js 文件裸扶;編譯的時(shí)候即使報(bào)錯(cuò)了框都,還是會(huì)生成編譯結(jié)果(.js),可通過 tsconfig.json 文件配置

tsc demo.ts

基礎(chǔ)類型

布爾值 boolean

let isDone: boolean = false;

注意呵晨,使用構(gòu)造函數(shù) Boolean 創(chuàng)造的對(duì)象不是布爾值

let newBool: boolean = new Boolean(true);
// 編譯報(bào)錯(cuò): 不能將類型“Boolean”分配給類型“boolean”魏保≌崽#“boolean”是基元,但“Boolean”是包裝器對(duì)象囱淋。如可能首選使用“boolean”猪杭。ts(2322)

數(shù)字 number

let number: number = 6;
let notANumber: number = NaN;

字符串 string

let  string: string = 'Tom';
let sentence: string = `my name is ${aString}`;

空值 void

void 類型的變量只能賦值為 undefined 和 null

let unusable: void = undefined;

可以用 void 表示沒有任何返回值的函數(shù)

function alertName(): void {
  alert('My name is Tom');
}

null 和 undefined

undefined 類型的變量只能被賦值為 undefined,null 類型的變量只能被賦值為 null

let u: undefined = undefined;
let n: null = null;

與 void 的區(qū)別是妥衣,undefined 和 null 是所有類型的子類型皂吮。也就是說 undefined 類型的變量,可以賦值給 number 類型的變量:

let u: undefined;
let num: number = u;
let num2:number = undefined;
// 編譯合法 undefined是number的子類型

let unm2: void;
let num3: number = unm2;
// => 不合法 (void不是number的子類型)

任意值 any

any 用來(lái)表示允許賦值為任意類型

let anyType:any = 'seven';
anyType = 7;

在任意值上訪問任何屬性和方法都是允許的税手,即不做類型檢查

let anyType:any = 'seven';
console.log(anyType.name().age) 
// => 允許編譯蜂筹,但是js執(zhí)行會(huì)報(bào)錯(cuò)

變量如果在聲明的時(shí)候,未指定其類型芦倒, 也沒有賦值艺挪, 那么它會(huì)被推斷(類型推論)為任意值類型而完全不被類型檢查

let something; 
// 等價(jià)于 let something: any;
something = 'seven';
something = 7;

數(shù)組

可理解為相同類型的一組數(shù)據(jù),數(shù)組類型有多種定義方式

1兵扬,類型 + 方括號(hào)( type [ ] )

這種方式定義的數(shù)組項(xiàng)中不允許出現(xiàn)其他的類型

let list: number[] = [1, 2, 3];

2麻裳,數(shù)組泛型 Array < type >

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

元祖 Tuple

元組類型允許表示一個(gè)已知元素?cái)?shù)量和類型的數(shù)組,各元素的類型不必相同器钟,簡(jiǎn)單理解為可定義一組不同類型的數(shù)據(jù):

let arr:[string, number] = ['name', 20];
console.log(arr[0]); 
// => 'name' 

越界元素:當(dāng)訪問超出元祖長(zhǎng)度的元素時(shí)津坑,它的類型會(huì)被限制為元祖中每個(gè)類型的聯(lián)合類型

let arr:[string, number] = ['name', 20];
arr[0] = 'age';
arr[2] = 'string';
arr[3] = 40;
arr[4] = true; // 編譯報(bào)錯(cuò)

枚舉 enum

['en?m]

枚舉類型用于取值被限定在一定范圍內(nèi)的場(chǎng)景,如一周只有7天傲霸,一年只有4季等疆瑰。

枚舉初始化

枚舉初始化可以理解為給枚舉成員賦值。每個(gè)枚舉成員都需要帶有一個(gè)值昙啄,在未賦值的情況下穆役, 枚舉成員會(huì)被賦值為從 0 開始, 步長(zhǎng)為 1 遞增的數(shù)字:

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

console.log(Weeks['Mon']); // => 0
console.log(Weeks[0]); // => 'Mon'
console.log(Weeks.Tue); // => 1

手動(dòng)賦值時(shí)梳凛, 未賦值的枚舉成員會(huì)接著上一個(gè)枚舉項(xiàng)遞增(初始化):

enum Weeks {
    Mon, Tue, Wed, Thu = 2, Fri, Sat = -1.5, Sun
};

console.log(Weeks['Mon']); // => 0
console.log(Weeks.Wed); // => 2
console.log(Weeks.Thu); // => 2
console.log(Weeks.Fri); // => 3
console.log(Weeks.Sun); // => -0.5

上例中耿币,未手動(dòng)賦值的 Wed 和手動(dòng)賦值的 Thu 取值重復(fù)了,但是 TypeScript 并不會(huì)報(bào)錯(cuò)伶跷,該種情況可能會(huì)引起取值錯(cuò)誤掰读,所以使用的時(shí)候最好避免出現(xiàn)取值重復(fù)的情況秘狞。

TypeScript 支持 數(shù)字 的和基于字符串的枚舉叭莫。

數(shù)字枚舉

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

字符串枚舉

enum Direction {
    Up = "UP",
    Down = "DOWN",
    Left = "LEFT",
    Right = "RIGHT",
}

異構(gòu)枚舉(Heterogeneous enums)

可以混合字符串和數(shù)字,但通常不這么做

enum Gender {
    Male = 0,
    Female = "1",
}

常量成員和計(jì)算所得成員

枚舉成員的值可以是 常量 或 計(jì)算出來(lái)的烁试。

上面所舉的例子都是常量成員雇初,官網(wǎng)定義如下:

當(dāng)滿足以下條件時(shí),枚舉成員被當(dāng)作是常數(shù):

  • 不具有初始化函數(shù)并且之前的枚舉成員是常數(shù)减响。在這種情況下靖诗,當(dāng)前枚舉成員的值為上一個(gè)枚舉成員的值加 1郭怪。但第一個(gè)枚舉元素是個(gè)例外。如果它沒有初始化方法刊橘,那么它的初始值為 0鄙才。
  • 枚舉成員使用常數(shù)枚舉表達(dá)式初始化。常數(shù)枚舉表達(dá)式是 TypeScript 表達(dá)式的子集促绵,它可以在編譯階段求值攒庵。當(dāng)一個(gè)表達(dá)式滿足下面條件之一時(shí),它就是一個(gè)常數(shù)枚舉表達(dá)式:
    • 數(shù)字字面量
    • 引用之前定義的常數(shù)枚舉成員(可以是在不同的枚舉類型中定義的)如果這個(gè)成員是在同一個(gè)枚舉類型中定義的败晴,可以使用非限定名來(lái)引用
    • 帶括號(hào)的常數(shù)枚舉表達(dá)式
    • +, -, ~ 一元運(yùn)算符應(yīng)用于常數(shù)枚舉表達(dá)式
    • +, -, *, /, %, <<, >>, >>>, &, |, ^ 二元運(yùn)算符浓冒,常數(shù)枚舉表達(dá)式做為其一個(gè)操作對(duì)象。若常數(shù)枚舉表達(dá)式求值后為 NaN 或 Infinity尖坤,則會(huì)在編譯階段報(bào)錯(cuò)

所有其它情況的枚舉成員被當(dāng)作是需要計(jì)算得出的值稳懒。

常量枚舉 const enum

常數(shù)枚舉與普通枚舉的區(qū)別是,它會(huì)在編譯階段被刪除慢味,并且不能包含計(jì)算成員场梆。

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

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

編譯后:

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

外部枚舉 declare enum

外部枚舉與聲明語(yǔ)句一樣,常出現(xiàn)在聲明文件中纯路。

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

同時(shí)使用 declareconst 也是可以的辙谜,編譯結(jié)果同常量枚舉一致。

never

永遠(yuǎn)不存在值的類型感昼,一般用于錯(cuò)誤處理函數(shù)装哆。

// 返回never的函數(shù)必須存在無(wú)法達(dá)到的終點(diǎn)
function error(message: string): never {
    throw new Error(message);
}

symbol

自ECMAScript 2015起,symbol成為了一種新的原生類型定嗓,就像 numberstring 一樣蜕琴。

symbol類型的值是通過Symbol構(gòu)造函數(shù)創(chuàng)建的。

let sym1 = Symbol();

Symbols是不可改變且唯一的宵溅。

let sym2 = Symbol("key");
let sym3 = Symbol("key");
sym2 === sym3; // false, symbols是唯一的

更多用法參看 阮一峰ES6的symbol

object

object表示非原始類型凌简,也就是除numberstring恃逻,boolean雏搂,symbolnullundefined之外的類型寇损。

function create(o: object | null): void;

create({ prop: 0 }); // OK
create(null); // OK

create(42); // Error
create("string"); // Error
create(false); // Error
create(undefined); // Error

內(nèi)置對(duì)象

JavaScript 中有很多內(nèi)置對(duì)象凸郑,它們可以直接在 TypeScript 中當(dāng)做定義好了的類型。

ECMAScript 的內(nèi)置對(duì)象

Boolean矛市、Error芙沥、DateRegExp 等。更多的內(nèi)置對(duì)象而昨,可以查看 MDN 的文檔救氯。

let b: Boolean = new Boolean(1);
let e: Error = new Error('Error occurred');
let d: Date = new Date();
let r: RegExp = /[a-z]/;

DOM 和 BOM 的內(nèi)置對(duì)象

DocumentHTMLElement歌憨、Event着憨、NodeList 等。

let body: HTMLElement = document.body;
let allDiv: NodeList = document.querySelectorAll('div');
document.addEventListener('click', function(e: MouseEvent) {
  // Do something
});

類型推論

變量申明如果沒有明確的指定類型务嫡,那么 TypeScript 會(huì)依照類型推論的規(guī)則推斷出一個(gè)類型

let string = 'seven';
// 等價(jià)于 let string: string = 'seven';
string = 4;
// 編譯報(bào)錯(cuò): error TS2322: Type 'number' is not assignable to type 'string'

變量聲明但是未賦值享扔,會(huì)推論為 any

let x;
x = 1;
x = 'aaa'

聯(lián)合類型

表示取值可以為多種類型中的一種,使用 | 分隔每個(gè)類型

let stringOrNumber:string | number;
stringOrNumber = 'seven';

當(dāng) TypeScript 不確定一個(gè)聯(lián)合類型的變量到底是哪個(gè)類型的時(shí)候, 我們只能訪問此聯(lián)合類型的所有類型里共有的屬性或方法

function getString(something: string | number): string {
  // toString 是 string類型 和 number類型 的共有屬性
  return something.toString();
}

function getLength(something: string | number): number {
  return something.length;
  // => 編譯報(bào)錯(cuò): length 不是 string類型 和 number類型 的共有屬性, 所以報(bào)錯(cuò)
}

類型斷言

類型斷言(Type Assertion)可以用來(lái)手動(dòng)指定一個(gè)值的類型植袍。

類型斷言有2種形式:

1惧眠,<類型>值 ( 尖括號(hào)語(yǔ)法 )

let someValue: any = "this is a string";

let strLength: number = (<string>someValue).length;

2,值 as 類型 ( as 語(yǔ)法 )

當(dāng)使用 tsx 時(shí)于个,只有 as語(yǔ)法斷言是被允許的

let someValue: any = "this is a string";

let strLength: number = (someValue as string).length;

在上述 聯(lián)合類型 的例子中氛魁, getLength 方法會(huì)編譯報(bào)錯(cuò),此時(shí)我們可以使用類型斷言厅篓,將 something 斷言成 string 就不會(huì)報(bào)錯(cuò)了:

function getLength(something: string | number): number {
    if ((<string>something).length) {
        // 將 something 斷言為 string類型
        return (<string>something).length;
    } else {
        return something.toString().length;
    }
}

注意 : 類型斷言不是類型轉(zhuǎn)換秀存,斷言成一個(gè)聯(lián)合類型中不存在的類型是不允許的:

function toBoolean(something: string | number): boolean {
    return <boolean>something;
    // => 報(bào)錯(cuò)
}

類型別名 type

類型別名用來(lái)給一個(gè)類型起個(gè)新名字,多用于聯(lián)合類型:

type Name = string;
type GetName = () => string;
type NameOrGetter = Name | GetName;
function getName(n: NameOrGetter): Name {
    if (typeof n === 'string') {
        return n;
    } else {
        return n();
    }
}

type 聲明可以定義聯(lián)合類型羽氮,基本類型等多種類型或链,而 interface 只能定義對(duì)象類型

字符串字面量類型

字符串字面量類型用來(lái)約束取值只能是某幾個(gè)字符串中的一個(gè)。

type EventNames = 'click' | 'scroll' | 'mousemove';
function handleEvent(ele: Element, event: EventNames) {
    // do something
}

handleEvent(document.getElementById('hello'), 'scroll');  // 沒問題
handleEvent(document.getElementById('world'), 'dbclick'); // 報(bào)錯(cuò)档押,event 不能為 'dbclick'

接口 Interfaces

接口(Interfaces)是一個(gè)很重要的概念澳盐,可以理解為一種規(guī)范或者約束,用來(lái)描述 對(duì)象(object) 的形狀 或者對(duì) 類(class) 的行為 進(jìn)行抽象令宿。對(duì)類的行為抽象將在后面 類與接口 一章中介紹叼耙,下面主要介紹對(duì)對(duì)象的形狀進(jìn)行描述。

接口定義

使用 interface 定義接口, 接口名稱一般首字母大寫粒没,定義接口的時(shí)候筛婉,只定義聲明即可,不包含具體內(nèi)容:

// 定義一個(gè)接口 Person
interface Person {
  name: string;
  age: number;
}

// 定義一個(gè)個(gè)變量癞松,它的類型是 Person
let tom: Person = {
  name: 'Tom',
  age: 25
};

實(shí)現(xiàn)接口的時(shí)候爽撒,要實(shí)現(xiàn)里面的內(nèi)容,定義的變量比接口少了或多了屬性都是不允許的:

let tom: Person = {
  name: 'tom'
}
// => 編譯報(bào)錯(cuò)响蓉,少了age屬性

可選屬性

使用 ? 代表可選屬性, 即該屬性可以不存在, 但不允許添加未定義的屬性

interface Person {
  name: string;
  age?: number;
}
let tom: Person = {
  name: 'tom'
}
// age是可選屬性

任意屬性

定義了任意屬性后可以添加未定義的屬性硕勿,并可以指定屬性值的類型

interface Person03 {
  name: string;
  age?: number;
  [propName: string]: any;
}
let tom04: Person03 = {
  name: 'Tom',
  age: 25,
  gender: 'male'
};

定義了任意屬性,那么確定屬性和可選屬性都必須是它的子屬性

interface Person {
  name: string;
  age?: number;
  [propName: string]: string;
}
// 編譯報(bào)錯(cuò):Person定義了一個(gè)任意屬性厕妖,其值為string類型首尼。則Person的所有屬性都必須為string類型,而age為number類型

只讀屬性 readonly

interface Person {
  readonly id: number;
  name: string;
  age?: number;
  [propName: string]: any;
}

只讀的約束存在于第一次給對(duì)象賦值的時(shí)候言秸,而不是第一次給只讀屬性賦值的時(shí)候

let person: Person = {
  id: 100,
  name: 'tom',
}
person05.id = 90;
// => 編譯報(bào)錯(cuò):id為只讀, 不可修改

let person2: Person = {
  name: 'welson',
  age: 2
}
// => 編譯報(bào)錯(cuò):給對(duì)象 person2 賦值软能,未定義只讀屬性id
person2.id = 1;
// => 編譯報(bào)錯(cuò):id為只讀, 不可修改

函數(shù)類型接口

// 只有參數(shù)列表和返回值類型的函數(shù)定義, 參數(shù)列表里的每個(gè)參數(shù)都需要名字和類型
interface SearchFunc {
  (source: string, subString: string): boolean;
}

函數(shù)

函數(shù)聲明

function sum(x: number, y: number): number {
    return x + y;
}

輸入多余的(或者少于要求的)參數(shù),是不被允許的

sum(1, 2, 3);
// 編譯報(bào)錯(cuò):多了1個(gè)參數(shù)

匿名函數(shù)(函數(shù)表達(dá)式)

let mySum = function (x: number, y: number): number {
    return x + y;
};

上面的代碼只對(duì)等號(hào)右側(cè)的匿名函數(shù)進(jìn)行了類型定義举畸,而等號(hào)左邊的 mySum查排,是通過賦值操作進(jìn)行類型推論而推斷出來(lái)的。如果需要我們手動(dòng)給 mySum 添加類型抄沮,則應(yīng)該是這樣:

let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
    return x + y;
};
// 注意不要混淆了 TypeScript 中的 => 和 ES6 中的 =>

用接口定義函數(shù)的形狀

interface FuncAdd {
  (value: number, increment: number): number
}
let add: FuncAdd;
add = function(value: number, increment: number): number {
  return value + increment;
}
// 函數(shù)的參數(shù)名不需要與接口里定義的名字相匹配
let add2: FuncAdd;
add2 = function(a: number, b: number) {
  return a + b;
}

可選參數(shù)

可選參數(shù)必須接在必需參數(shù)后面跋核,換句話說,可選參數(shù)后面不允許再出現(xiàn)必須參數(shù)了

function addNum(a: number, b: number, c? :number): number {
    if(c) {
        return a + b + c;
    } else {
        return a + b;
    }
}
console.log(add(1, 2));

默認(rèn)參數(shù)

類比 ES6 中的默認(rèn)值

function add(a: number = 1, b: number): number {
    return a + b;
}
console.log(add(undefined, 1));

剩余參數(shù)

類比 Es6 中對(duì)象展開

interface AddFunc {
  (num1: number, ...rest: number[]): number
}
let add: AddFunc;
add = function(a: number, ...rest: number[]): number {
    let result = a; 
    rest.map(v => result += v);
    return result;
}
console.log(add(1,2,3,4));

函數(shù)重載

重載是為同一個(gè)函數(shù)提供多個(gè)函數(shù)類型定義叛买,允許函數(shù)對(duì)傳入不同的參數(shù)返回不同的的結(jié)果分別做類型檢查

比如實(shí)現(xiàn)一個(gè)數(shù)字或字符串的反轉(zhuǎn)函數(shù):

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

上述函數(shù)利用聯(lián)合類型實(shí)現(xiàn)砂代,但有一個(gè)缺點(diǎn),無(wú)法精確檢查輸入和輸出類型率挣,即輸入數(shù)字輸出也應(yīng)該為數(shù)字刻伊,這時(shí)就可以使用重載定義多個(gè)函數(shù)類型:

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

重復(fù)定義多次函數(shù) reverse,前幾次都是函數(shù)定義椒功,最后一次是函數(shù)實(shí)現(xiàn)捶箱。

TypeScript與JavaScript的處理流程相似,它會(huì)查找重載列表动漾,從第一個(gè)重載定義開始匹配丁屎,如果匹配的話就使用這個(gè)定義,所以多個(gè)函數(shù)定義如果有包含關(guān)系旱眯,需要優(yōu)先把精確的定義寫在前面晨川。

類 class

同ES6 的 class

相關(guān)概念

  • 類(Class):定義了一件事物的抽象特點(diǎn),包含它的屬性和方法
  • 對(duì)象(Object):類的實(shí)例删豺,通過 new 生成
  • 面向?qū)ο螅∣OP)的三大特性:封裝础爬、繼承、多態(tài)
  • 封裝(Encapsulation):將對(duì)數(shù)據(jù)的操作細(xì)節(jié)隱藏起來(lái)吼鳞,只暴露對(duì)外的接口看蚜。外界調(diào)用端不需要(也不可能)知道細(xì)節(jié),就能通過對(duì)外提供的接口來(lái)訪問該對(duì)象赔桌,同時(shí)也保證了外界無(wú)法任意更改對(duì)象內(nèi)部的數(shù)據(jù)
  • 繼承(Inheritance):子類繼承父類供炎,子類除了擁有父類的所有特性外,還有一些更具體的特性
  • 多態(tài)(Polymorphism):由繼承而產(chǎn)生了相關(guān)的不同的類疾党,對(duì)同一個(gè)方法可以有不同的響應(yīng)音诫。比如 CatDog 都繼承自 Animal,但是分別實(shí)現(xiàn)了自己的 eat 方法雪位。此時(shí)針對(duì)某一個(gè)實(shí)例竭钝,我們無(wú)需了解它是 Cat 還是 Dog,就可以直接調(diào)用 eat 方法,程序會(huì)自動(dòng)判斷出來(lái)應(yīng)該如何執(zhí)行 eat
  • 存取器(getter & setter):用以改變屬性的讀取和賦值行為
  • 修飾符(Modifiers):修飾符是一些關(guān)鍵字香罐,用于限定成員或類型的性質(zhì)卧波。比如 public 表示公有屬性或方法
  • 抽象類(Abstract Class):抽象類是供其他類繼承的基類,抽象類不允許被實(shí)例化庇茫。抽象類中的抽象方法必須在子類中被實(shí)現(xiàn)
  • 接口(Interfaces):不同類之間公有的屬性或方法港粱,可以抽象成一個(gè)接口。接口可以被類實(shí)現(xiàn)(implements)旦签。一個(gè)類只能繼承自另一個(gè)類查坪,但是可以實(shí)現(xiàn)多個(gè)接口

類的定義

使用 class 定義類,使用 constructor 定義構(gòu)造函數(shù)宁炫。

通過 new 生成新實(shí)例的時(shí)候偿曙,會(huì)自動(dòng)調(diào)用構(gòu)造函數(shù)。

class Animal {
        name:string; // 定義屬性
    constructor(name) {
        this.name = name; // 屬性賦值
    }
    sayHi() {
        return `我叫 ${this.name}`;
    }
}

let cat = new Animal('Tom');
console.log(cat.sayHi()); // 我叫 Tom

類的繼承

使用 extends 關(guān)鍵字實(shí)現(xiàn)繼承羔巢,子類中使用 super 關(guān)鍵字來(lái)調(diào)用父類的構(gòu)造函數(shù)和方法遥昧。

class Cat extends Animal {
    color: string;
    constructor(name, color) {
        super(name); // 調(diào)用父類Animal的 constructor(name)
        this.color = color
    }
    sayHi() {
        // 調(diào)用父類的 sayHi();
        return super.sayHi() + '我是一只'+ this.color + ' 色的貓朵纷,'; 
    }
}

let c = new Cat('Tom', '橘黃'); // Tom
console.log(c.sayHi()); // 我叫 Tom炭臭,我是一只橘黃色的貓;

let cat2 = new Cat('Jerry');
cat2.color = '黑';
console.log(c.sayHi()); // 我叫 Jerry袍辞,我是一只黑色的貓鞋仍;

存取器

使用 getter 和 setter 可以改變屬性的賦值和讀取行為:

class Animal {
        name:string;
    constructor(name) {
        this.name = name;
    }
    get name() {
        return 'Jack';
    }
    set name(value) {
        console.log('setter: ' + value);
    }
}

let a = new Animal('Kitty'); // setter: Kitty
a.name = 'Tom'; // setter: Tom
console.log(a.name); // Jack

實(shí)例屬性和方法

js中的屬性和方法:

// js中
function Person(name) {
  this.name = name; // 實(shí)例屬性
  this.eat = function(){ console.log('eat') };  // 實(shí)例方法
}
Person.age = 19; // 靜態(tài)屬性
Person.sleep = function(){ console.log('sleep') }; // 靜態(tài)方法

// 訪問實(shí)例方法和屬性:
var tom = new Person('tom');
console.log(tom.name) // tom
tom.eat();
tom.sleep() // error: tom.sleep is not a function

// 訪問靜態(tài)方法和屬性:
console.log(Person.age); // 19
Person.sleep();
Person.eat(); // error: Person.eat is not a function

ES6 中實(shí)例的屬性只能通過構(gòu)造函數(shù)中的 this.xxx 來(lái)定義:

class Animal {
    constructor(){
            this.name = 'tom';
        }
    eat() {}
}

let a = new Animal();
console.log(a.name); // tom

ES7 提案中可以直接在類里面定義:

// ts
class Animal {
    name = 'tom';
    eat() {}
}

let a = new Animal();
console.log(a.name); // Jack

靜態(tài)屬性和方法

ES7 提案中,可以使用 static 定義一個(gè)靜態(tài)屬性或方法搅吁。靜態(tài)方法不需要實(shí)例化威创,而是直接通過類來(lái)調(diào)用:

// ts
class Animal {
    static num = 42;
    static isAnimal(a) {
        return a instanceof Animal;
    }
}

console.log(Animal.num); // 42
let a = new Animal('Jack');
Animal.isAnimal(a); // true
a.isAnimal(a); // TypeError: a.isAnimal is not a function

訪問修飾符

public

公有屬性或方法,可以在任何地方被訪問到谎懦,默認(rèn)所有的屬性和方法都是 public

private

私有屬性或方法肚豺,不能在聲明它的類的外部訪問,也不可以在子類中訪問

protected

受保護(hù)的屬性或方法界拦,它和 private 類似吸申,區(qū)別是它可以在子類中訪問

class Person {
    public name:string;
    private idCard:number;
    protected phone:number;
    constructor(name,idCard,phone) {
        this.name = name;
        this.idCard = idCard;
        this.phone = phone;
    }
}

let tom = new Person('tom',420000,13811110000);
console.log(tom.name) // tom

console.log(tom.idCard) 
// error:Property 'idCard' is private and only accessible within class 'Person'.

console.log(tom.phone)
// error:Property 'phone' is protected and only accessible within class 'Person' and its subclasses

class Teacher extends Person {
    constructor(name,idCard,phone) {
        super(name,idCard,phone);
        console.log(this.name)
        console.log(this.phone)
                console.log(this.idCard)
                // error:Property 'idCard' is private and only accessible within class 'Person'.
    }
}

多態(tài)

同一個(gè)父類的多個(gè)子類,可以有不同結(jié)果的同名方法:

class Person {
  eat(){ console.log('eat') }
}
class A extends Person {
  eat(){ console.log('A eat') }
}
class B extends Person {
  eat(){ console.log('B eat') }
}

抽象類/抽象方法 abstract

abstract 用于定義抽象類和其中的抽象方法享甸。

  1. 抽象類是提供給其他類繼承的基類(父類)截碴,是不允許被實(shí)例化
  2. 抽象方法只能包含在抽象類中
  3. 子類繼承抽象類,必須實(shí)現(xiàn)抽象類中的抽象方法
abstract class Animal {
    abstract eat(); // 抽象方法
    // 普通方法
    sleep(){
      console.log('sleep')
    }
}

let a = new Animal(); // 報(bào)錯(cuò)蛉威,抽象類不能被實(shí)例化

class Cat extends Animal {
    eat(){ 
        // 父類的eat方法必須被實(shí)現(xiàn)
      console.log('eat')
    }
}

類與接口

前面介紹了 接口 可以用來(lái)描述 對(duì)象(object)的形狀日丹,這一章主要介紹 接口 對(duì) 類(class)的行為 進(jìn)行抽象。

類實(shí)現(xiàn)接口 implements

實(shí)現(xiàn)(implements)是面向?qū)ο笾械囊粋€(gè)重要概念蚯嫌。一個(gè)類只能繼承自另一個(gè)類哲虾,不同類之間可能會(huì)有一些共有特性丙躏,提取多個(gè)類的共有特性,作為一個(gè)接口束凑,再用 implements 關(guān)鍵字來(lái)實(shí)現(xiàn)就可以大大提高面向?qū)ο蟮撵`活性晒旅。

舉例: 人是一個(gè)類,人需要吃東西湘今。動(dòng)物是一個(gè)類敢朱,動(dòng)物也需要吃東西剪菱。這種情況就可以把 吃東西 提取出來(lái)作為一個(gè)接口:

interface Ieat {
   eat();
}

class Person implements Ieat{
  eat(){}
}

class Animal implements Ieat {
  eat(){}
}

一個(gè)類也可以實(shí)現(xiàn)多個(gè)接口:

interface Ieat {
   eat();
}

interface Isleep {
    sleep();
}

class Person implements Ieat, Isleep{
  eat(){}
  sleep() {}
}

接口繼承接口

interface Alarm {
    alert();
}

interface LightableAlarm extends Alarm {
    lightOn();
    lightOff();
}

接口繼承類

class Point {
    x: number;
    y: number;
}

interface Point3d extends Point {
    z: number;
}

let point3d: Point3d = {x: 1, y: 2, z: 3};

混合類型

前面介紹了接口可以用來(lái)定義函數(shù)的形狀摩瞎,有時(shí)候,一個(gè)函數(shù)還可以有自己的屬性和方法:

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

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

let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;

泛型

泛型(Generics)是指在定義函數(shù)孝常、接口或類的時(shí)候旗们,不預(yù)先指定具體的類型,而在使用的時(shí)候再指定類型的一種特性构灸。

打印number:

function printer(arr:number[]):void {
  for(var item of arr) {
    console.log(item)
  }
}
printer([1,2,3,4])

打印字符串:

// 打印字符串
function printer1(arr:string[]):void {
  for(var item of arr) {
    console.log(item)
  }
}
printer1(['a','b','c','d'])

使用 any 也可以通過編譯上渴,但是無(wú)法準(zhǔn)確定義返回值的類型,這個(gè)時(shí)候就可以使用泛型函數(shù)

泛型函數(shù)

在函數(shù)名后加上 <T> (也可以是其他別的字母)喜颁,其中 T 用來(lái)指代輸入的類型稠氮,在函數(shù)內(nèi)部就可以使用這個(gè) T 類型。

function printer<T>(arr:T[]):void {
  for(var item of arr) {
    console.log(item)
  }
}
// 指定具體類型調(diào)用
printer<string>(['a','b','c','d']);
// 調(diào)用時(shí)也可以直接讓ts自己做類型推論
printer([1,2,3,4]);

也可以同時(shí)使用多個(gè)類型參數(shù)

function swap<S,P>(tuple:[S,P]):[P,S] {
  return [tuple[1], tuple[0]]
}
swap<string, number>(['a', 2])

泛型類

class arrayList<T> {
  name: T;
  list: T[] = [];
  add(val:T):void {
    this.list.push(val)
  }
}

var arr = new arrayList<number>();
arr.add(1)
arr.add(2)
console.log(arr.list)

泛型接口

interface Iadd<T> {
  (x:T,y:T):T;
}

var add:Tadd<number> = function(x:number,y:number):number {
  return x + y
}

泛型約束

在函數(shù)內(nèi)部使用泛型變量的時(shí)候半开,由于事先不知道它是哪種類型隔披,所以不能隨意的操作它的屬性或方法

獲取一個(gè)參數(shù)的長(zhǎng)度:

function getLength<T>(arg:T):T {
    console.log(arg.length) // error: Property 'length' does not exist on type 'T'
  return arg;
}

上例中,泛型 T 不一定包含屬性 length寂拆,所以編譯的時(shí)候報(bào)錯(cuò)了奢米,這時(shí)候就可以使用泛型約束,使用 extends 約束泛型 <T> 必須符合 Ilength 的形狀纠永,也就是必須包含 length 屬性:

interface Ilength {
  length: number
}

function getLength<T extends Ilength>(arg:T):T {
    console.log(arg.length)
  return arg;
}

getLength('abcd') // 4

getLength(7) // error: Argument of type '7' is not assignable to parameter of type 'Ilength'.

多個(gè)參數(shù)間也可以互相約束:

function copyFields<T extends U, U>(target: T, source: U): T {
    for (let id in source) {
        target[id] = (<T>source)[id]; 
    }
    return target;
}

let x = { a: 1, b: 2, c: 3, d: 4 };
copyFields(x, { b: 10, d: 20 })

上例中鬓长,使用了兩個(gè)類型參數(shù),其中要求 T 繼承 U尝江,這樣就保證了 U 上不會(huì)出現(xiàn) T 中不存在的字段涉波。

聲明文件 declare

當(dāng)使用第三方庫(kù)時(shí),我們需要引用它的聲明文件炭序,才能獲得對(duì)應(yīng)的代碼補(bǔ)全怠蹂、接口提示等功能。

聲明語(yǔ)句

假如我們使用第三方庫(kù) jQuery少态,來(lái)獲取一個(gè)元素

$('#foo');
jQuery('#foo');

但是在 ts 中城侧,編譯器并不知道 $jQuery 是什么東西:

jQuery('#foo');
// ERROR: Cannot find name 'jQuery'.

這時(shí),我們需要使用 declare var 來(lái)定義它的類型彼妻,declare var 并沒有真的定義一個(gè)變量嫌佑,只是定義了全局變量 jQuery 的類型豆茫,僅僅會(huì)用于編譯時(shí)的檢查,在編譯結(jié)果中會(huì)被刪除屋摇。

declare var jQuery: (selector: string) => any;
jQuery('#foo');

聲明文件

通常我們會(huì)把聲明語(yǔ)句放到一個(gè)單獨(dú)的文件(xxx.d.ts)中揩魂,這就是聲明文件,聲明文件必需以 .d.ts 為后綴炮温。

一般來(lái)說火脉,ts 會(huì)解析項(xiàng)目中所有的 *.ts 文件,當(dāng)然也包含以 .d.ts 結(jié)尾的文件柒啤。所以當(dāng)我們將 jQuery.d.ts 放到項(xiàng)目中時(shí)倦挂,其他所有 *.ts 文件就都可以獲得 jQuery 的類型定義了。

這是使用全局變量模式的聲明文件担巩,還有其他模式如 模塊導(dǎo)入 等會(huì)在后面介紹方援。

第三方聲明文件

社區(qū)已經(jīng)幫我們定義好了很多第三方庫(kù)的聲明文件,可以直接下載下來(lái)使用涛癌,更推薦使用 @types 統(tǒng)一管理第三方庫(kù)的聲明文件犯戏。@types 的使用方式很簡(jiǎn)單,直接用 npm 安裝對(duì)應(yīng)的聲明模塊即可拳话,以 jQuery 舉例:

npm install @types/jquery --save-dev

可以在這個(gè)頁(yè)面搜索你需要的聲明文件先匪。

書寫聲明文件

當(dāng)一個(gè)第三方庫(kù)沒有提供聲明文件時(shí),我們就需要自己書寫聲明文件了弃衍。

在不同的場(chǎng)景下呀非,聲明文件的內(nèi)容和使用方式會(huì)有所區(qū)別:

  • 全局變量:通過 <script> 標(biāo)簽引入第三方庫(kù),注入全局變量
  • npm 包:通過 import foo from 'foo' 導(dǎo)入笨鸡,符合 ES6 模塊規(guī)范
  • UMD 庫(kù):既可以通過 <script> 標(biāo)簽引入姜钳,又可以通過 import 導(dǎo)入
  • 模塊插件:通過 import 導(dǎo)入后,可以改變另一個(gè)模塊的結(jié)構(gòu)
  • 直接擴(kuò)展全局變量:通過 <script> 標(biāo)簽引入后形耗,改變一個(gè)全局變量的結(jié)構(gòu)哥桥。比如為 String.prototype 新增了一個(gè)方法
  • 通過導(dǎo)入擴(kuò)展全局變量:通過 import 導(dǎo)入后,可以改變一個(gè)全局變量的結(jié)構(gòu)

作者:MrWelson

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末激涤,一起剝皮案震驚了整個(gè)濱河市拟糕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌倦踢,老刑警劉巖送滞,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異辱挥,居然都是意外死亡犁嗅,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門晤碘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)褂微,“玉大人功蜓,你說我怎么就攤上這事〕杪欤” “怎么了式撼?”我有些...
    開封第一講書人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)求厕。 經(jīng)常有香客問我著隆,道長(zhǎng),這世上最難降的妖魔是什么呀癣? 我笑而不...
    開封第一講書人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任美浦,我火速辦了婚禮,結(jié)果婚禮上十艾,老公的妹妹穿的比我還像新娘抵代。我一直安慰自己腾节,他們只是感情好忘嫉,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著案腺,像睡著了一般庆冕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上劈榨,一...
    開封第一講書人閱讀 49,764評(píng)論 1 290
  • 那天访递,我揣著相機(jī)與錄音,去河邊找鬼同辣。 笑死拷姿,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的旱函。 我是一名探鬼主播响巢,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼棒妨!你這毒婦竟也來(lái)了踪古?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤券腔,失蹤者是張志新(化名)和其女友劉穎伏穆,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體纷纫,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡枕扫,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了辱魁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片烟瞧。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡偷厦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出燕刻,到底是詐尸還是另有隱情只泼,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布卵洗,位于F島的核電站请唱,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏过蹂。R本人自食惡果不足惜十绑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望酷勺。 院中可真熱鬧本橙,春花似錦、人聲如沸脆诉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)击胜。三九已至亏狰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間偶摔,已是汗流浹背暇唾。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留辰斋,地道東北人策州。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像宫仗,于是被迫代替她去往敵國(guó)和親够挂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容