TypeScript 基礎篇

前言

我的學習TS的總結, 主要參考官方文檔,對於其中不太容易理解的點進行剖析;寫下來,主要是本身爲了鞏固知識點,並給你們分享下,若有不對之處,請指出~javascript

類型系統

基本類型

  • 數字(number):包括浮點數,以及 NaN、±Infinity。
  • 字符串(string):字符串型。
  • 布爾(boolean):布爾型,即 { true, false }。
  • null:即 { null }。
  • undefined:即 { undefined }。 默認狀況下null和undefined是全部類型的子類型,能夠賦值給任何類型;當你指定了--strictNullChecks標記,null和undefined只能賦值給void和它們各自
  • symbol:符號類型。
  • never:表示永遠沒法到達的終點,是任何類型的子類型,也能夠賦值給任何類型,但除自身以外任何類型都不能賦值給它。一箇中途拋出錯誤,或者存在死循環的函數永遠不會有返回值,其返回類型是 never
  • void:表示沒有任何類型,沒有返回值;它等於 { null, undefined }。
  • 元組(tuple):特殊的數組,指定數組每項具體的類型,一一映射。eg: let tuple1: [number, string] = [12, 'aa']
  • 枚舉(enum):一組固定值的特定映射。eg: enum sex { MALE, FEMALE} 等同於 enum sex {MALE=0, FEMALE=1} sex.MALE // 取值
  • 任意類型(any)
  • object:表示非原始類型,也就是除number,string,boolean,symbol,null或undefined以外的類型。用來描述一種非基礎類型,因此通常用在類型校驗上,好比做爲參數類型。若是參數類型是 object,那麼容許任何對象數據傳入

另外,全部原始類型的字面量自己也可做爲類型使用,其外延只包括自身java

高級類型

聯合類型

表示一個值能夠是幾種類型之一。 豎線(|)分隔;若一個值是聯合類型,只能訪問此聯合類型的全部類型裏共有的成員es6

交叉類型

將多個類型合併爲一個類型。 豎線(&)分隔;把現有的多種類型疊加到一塊兒成爲一種類型,它包含了所需的全部類型的特性,多用於mixinstypescript

映射類型

從舊類型建立新類型的一種方式編程

type Readonly<T> = {
    readonly [P in keyof T]: T[P];
}
type Partial<T> = {
    [P in keyof T]?: T[P];
}

interface Person {
    name: string;
    age: number;
}

// 調用
type PersonPartial = Partial<Person>;
type ReadonlyPerson = Readonly<Person>;
// 結果
interface PersonPartial {
    name?: string;
    age?: number;
}
interface PersonReadonly {
    readonly name: string;
    readonly age: number;
}

類型保護

一、用戶自定義類型保護:定義函數,返回類型謂詞(語法:parameterName is Type)
二、typeof類型保護:只有兩種形式能被識別: typeof v === "typename"和 typeof v !== "typename", "typename"必須是 "number", "string", "boolean"或 "symbol" 原始類型,若爲除前四個的其餘字符串,將不被識別爲類型保護
三、instanceof 類型保護:過構造函數來細化類型的一種方式。語法: 實例 instanceof 構造函數數組

function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
 return names.map(n => o[n]);
}

interface Person {
   name: string;
   age: number;
}
let person: Person = {
   name: 'Jarid',
   age: 35
};
let strings: string[] = pluck(person, ['name']); // ok, ['Jarid]

其中
keyof T: 索引類型查詢操做符 (此實例中,爲person的屬性name、age,值等於'name'|'age')
K extends keyof T:泛型約束
T[K]: 索引訪問操做符(此實例中,爲person['name'])ide

interface 與 type 聲明類型的區別

定義類型有兩種方式: 接口(interface) 和類型別名(type alias), 它們的主要區別以下:函數

一、interface 只能定義對象類型或者函數, type 還能夠定義組合類型,交叉類型(&,相似並集),聯合類型(|,相似交集),原始類型
二、interface 方式能夠實現接口的 extends 和 implements , 而 type alias 則不行。
三、interface 能夠實現接口的合併,但 type alias 則不行。post

兼容性

兼容能夠簡單理解能否賦值,A = B (A 兼容/包含 B)學習

子類型兼容性

一、Any最「寬」。兼容其它全部類型(換言之,其它類型都是Any的子類型)
二、Never最「窄」。不兼容任何其它類型
三、Void兼容Undefined和Null
四、其它類型都兼容Never和Void

例如:

type point1D = { x: number } // 父 A
type point2D = { x: number, y: number } // 子 B
A = B // ok

當且僅當類型 B 是 A 的子集時,A 兼容 B,B 能夠被當成 A 處理
A = B

函數兼容性斷定

參數:要求對應參數的類型兼容,數量容許多餘 (多的包含少的,即參數容許多,不準少)
 返回值:類型兼容 (範圍更廣, 賦值變量是被賦值的變量的子類型)

類型斷言

能夠用來手動指定一個值的類型
語法:<類型>值值 as 類型 (在 tsx 語法(React 的 jsx 語法的 ts 版)中必須用後一種。)

使用內置對象做爲類型

ECMAScript 內置對象: Error、RegExp、Date、Boolean 等
DOM/BOM 內置對象: Document、HTMLElement、Event、NodeList、MouseEvent 等

參考:
http://kbscript.com/2017/01/2...
https://juejin.im/post/5c2723...

泛型

泛型(Generics)是指在定義函數、接口或類的時候,不預先指定具體的類型,而在使用的時候再指定類型的一種特性。
類型變量(類型參數) T

泛性約束 extends

interface Lengthwise {
    length: number;
}

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

泛型接口

使用含有泛型的接口來定義函數形狀

interface ConfigFn{
    <T>(value:T):T;
}

var getData:ConfigFn=function<T>(value:T):T{
    return value;
}

getData<string>('張三');
getData<string>(1243);  //錯誤
// 把類型參數提到接口名上
// 寫法一:
interface ConfigFn<T>{
    (value:T):T;
}

var getData:ConfigFn<string>=function<T>(value:T):T{
    return value;
}

getData('20');  /*正確*/


// 寫法二:
interface ConfigFn<T>{
    (value:T):T;
}

function getData<T>(value:T):T{
    return value;
}

var myGetData:ConfigFn<string>=getData;     
myGetData('20');  /*正確*/
myGetData(20)  //錯誤

泛型類

使用泛型來定義類

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

抽象類

抽象類: abstract 修飾,裏面能夠沒有抽象方法。但有抽象方法(abstract method)的類必須聲明爲抽象類(abstract class)

一、抽象類是不容許被實例化的
二、抽象類中的抽象方法必須被子類實現,不能本身實現
三、可使用修飾符

注意:若是子類繼承的是一個抽象類,子類必須實現父類裏的抽象方法,否則的話不能實例化,會報錯。

類與接口的關係

類繼承類(class extends class)
類實現接口(class implements interface)
接口繼承接口(interface extends interface)
接口繼承類(interface extends class)

混合類型

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;

參考: https://ts.xcatliu.com/advanc...

接口

接口的做用:在面向對象的編程中,接口是一種規範的定義,它定義了行爲和動做的規範;在程序設計裏面,接口起到一種限制和規範的做用。接口定義了某一批類所須要遵照的規範,接口不關心這些類的內部狀態數據,也不關心這些類裏方法的實現細節,它只規定這批類裏必須提供某些方法,提供這些方法的類就能夠知足實際須要。 typescrip中的接口相似於java,同時還增長了更靈活的接口類型,包括屬性、函數、可索引和類等。

面嚮對象語言:接口是對行爲和動做的抽象,而具體如何行動須要由類(classes)去實現(implement)
ts: 對類的部分行爲進行抽象,對對象的形狀進行描述; 把不一樣類中的共性抽取做爲接口,再由類分別去實現(implement)

簡而言之,定義規範標準,用於複用(與普通類型約束相比)。

interface Person {
    readonly id: number; // 只讀屬性
    name: string; // 肯定屬性
    age?: number; // 可選屬性
    [propName: string]: string; // 任意屬性
    [index: number]: string; // 可索引屬性
    (name: string, age: number): void; // 函數
    currentTime: Date; // 類類型的屬性 (類實現接口)
    setTime(d: Date); // 類類型的方法
}

注意:
一、使用 [propName: string] 定義了任意屬性取 string 類型的值。
須要注意的是,一旦定義了任意屬性,那麼肯定屬性和可選屬性的類型都必須是它的類型的子集
二、 只讀的約束存在於第一次給對象賦值的時候,而不是第一次給只讀屬性賦值的時候。給對象變量賦值後,只讀屬性不能再改變。
三、當一個類實現了一個接口時,只對其實例部分進行類型檢查。 constructor存在於類的靜態部分,因此不在檢查的範圍內

接口繼承接口

interface Shape5 {
      color: string;
  }

  interface Square5 extends Shape5 {
      sideLength: number;
  }

  let square = <Square5>{}; // 定義對象,使用斷言
  console.log(square) // {}
  // let square:Square5 = {color: '', sideLength: 0}
  square.color = "blue";
  square.sideLength = 10;
  console.log(square)

另:一個接口能夠繼承多個接口

接口繼承類
接口繼承類時,會繼承類的全部成員但不包含實現

class Person {
      type: string // ❗️這裏是類的描述
  }

  interface Child extends Person { // ❗️Child 接口繼承自 Person 類,所以規範了 type 屬性
      log(): void
      // 這裏其實有一個 type: string
  }

  // ⚠️ 上面的 Child 接口繼承了 Person 對 type 的描述,還定義了 Child 接口自己 log 的描述

  // 🥇 第一種寫法
  class Girl implements Child {
      type: 'child' // 接口繼承自 Person 的
      log() {} // 接口自己規範的
  }

  // 🥈 第二種寫法
  class Boy extends Person implements Child { // 首先 extends 了 Person 類,而後還需知足 Child 接口的描述
      type: 'child'
      log() {}
  }

當接口繼承了一個擁有私有(private)或受保護(protected)的成員的類時,這個接口類型只能被這個類或其子類所實現(implement)。

class Person {
  private type: string // ❗️這裏是類的描述
}

interface Child extends Person { // ❗️Child 接口繼承自 Person 類,所以規範了 type 屬性
  log(): void
  // 這裏其實有一個 type: string
}

// ⚠️ 上面的 Child 接口繼承了 Person 對 type 的描述,還定義了 Child 接口自己 log 的描述


// 寫法 (only)
class Boy extends Person implements Child { // 首先 extends 了 Person 類,而後還需知足 Child 接口的描述
  type: 'child'
  log() {}
}

函數

定義函數方式:

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

一、函數聲明

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 = function (x: number, y: number): number {
      return x + y;
  };

可選參數:(?:) 必須接在必需參數後面
參數默認值: 至關於可選參數,任意位置
剩餘參數:(...rest)必須是在最後,與 es6 相同

重載

java中方法的重載:重載指的是兩個或者兩個以上同名函數,但它們的參數不同,這時會出現函數重載的狀況。
typescript中的重載:經過爲同一個函數提供多個函數類型定義,一個函數體實現多種功能的目的。
ts爲了兼容es5 以及 es6 重載的寫法和java中有區別。

重載:同名函數,參數不同。容許一個函數接受不一樣數量或類型的參數時,做出不一樣的處理

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('');
      }
  }

多態

父類定義一個方法不去實現,讓繼承它的子類去實現, 每個子類有不一樣的表現(用於繼承)
注意:使用多態基礎是類的繼承或者接口實現。

抽象類

typescript中的抽象類:
一、它是提供其餘類繼承的基類,不能直接被實例化。
二、用abstract關鍵字定義抽象類和抽象方法,抽象類中的抽象方法不包含具體實現而且必須在派生類中實現。
三、abstract抽象方法只能放在抽象類裏面
用途:抽象類和抽象方法用來定義標準 。 (抽象類:Animal 這個類要求它的子類必須包含eat方法,不然報錯;多態:子類繼承父類,子類能夠不實現該方法,但使用多態就是子類爲了實現方法)

模塊系統

從ECMAScript 2015開始,JavaScript引入了模塊的概念。TypeScript沿用此概念,在TS 中,模塊分爲內部模塊和外部模塊,自typescript 1.5 後,內部模塊稱做「命名空間」,外部模塊稱做「模塊」

命名空間(namespace)(又名內部模塊)

同Java的包、.Net的命名空間同樣,TypeScript的命名空間將代碼包裹起來,經過export關鍵字對外暴露須要在外部訪問的對象。
在代碼量較大的狀況下,爲了不各類變量命名相沖突,可將類似功能的函數、類、接口等放置到命名空間內

主要用於組織代碼,解決命名衝突,會在全局生成一個對象,定義在namespace內部的類都要經過這個對象的屬性訪問

模塊(module)(又名外部模塊)

主要是解決加載依賴關係的,側重代碼的複用。跟文件綁定在一塊兒,一個文件就是一個module,模塊寫法和ES6同樣

三斜線指令

一、書寫一個全局變量的聲明文件(書寫一個全局變量的聲明文件時,若是須要引用另外一個庫的類型)
二、依賴一個全局變量的聲明文件

命名空間和模塊的區別:

  • 命名空間:內部模塊,主要用於組織代碼,避免命名衝突。
  • 模 塊:ts的外部模塊的簡稱,側重代碼的複用,一個模塊裏可能會有多個命名空間。
相關文章
相關標籤/搜索