1. 程式人生 > >《TypeScript入門教程》筆記

《TypeScript入門教程》筆記

基礎

原始資料型別

布林值

  let isDone: boolean = false;

數值

  let decLiteral: number = 6;

字串

  let myName: string = 'tom';

null/undefined

1.示例

  let myName: null = null;
  let myName: undefined = undefined;

2.它們是所有型別的子型別

  let val: 其它型別 = undefined;

非原始資料型別

空值

1.關鍵字為void,只能被賦值undefined和null

  let unusable: void = undefined;

任意值

1.可以賦值為任意型別的值(沒有型別限制)

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

2.對任意值進行任何操作,返回值型別都是任意值

3.變數宣告時,如果未指定型別

  a.未賦值,則會預設為any型別
    let something;
    something = 'seven';
    something = 7;
  b.賦值了,則會被型別推斷為所賦值的型別
    let myFavoriteNumber = 'seven';
    myFavoriteNumber = 7;  // 會報錯

聯合型別

1.表示取值可以為多種型別中的一種

2.示例

  let myFavoriteNumber: string | number
  myFavoriteNumber = 'seven'
  myFavoriteNumber = 7

3.當不確定聯合型別變數的型別時,只能訪問此聯合型別的所有型別裡共有的屬性或方法

  function getString(something: string | number): string {
    return something.toString()
  }

物件型別·介面

1.物件的型別由介面定義

2.介面是對行為的抽象,而具體實現則由類去實現

3.示例程式碼

  interface Person {
    name: string
    age: number
  }
  
  let tom: Person = {
    name: 'Tom',
    age: 25
  }

4.物件定義時,變數的屬性數量/形狀必須和介面屬性數量/形狀保持一致

5.可選屬性

    interface Person {
        name: string
        age?: number
    }

    let tom: Person = {
        name: 'Tom'
    }

5.任意屬性(一旦定義了任意屬性,那麼確定屬性和可選屬性的型別都必須是它的子型別)

    interface Person {
        name: string
        age: number
        [propName: string]: any
    }

    let tom: Person = {
        name: 'Tom',
        age: 25 // 會報錯
    }

6.只讀屬性

    interface Person {
        readonly id: number
        age: number
    }

    let tom: Person = {
        id: 10,
        age: 25
    }

    tom.id = 89757 // 報錯

陣列型別

1.型別+方括號表示法

    let arr: number[] = [1, 1, 2, 3, 5];
    let arr: number[] = [1, 1, 2, 3, '5']; // 會報錯
    arr.push('5'); // 會報錯

2.陣列泛型

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

3.介面表示陣列

    interface NumberArray {
        [index: number]: number
    }
    let fibonacci: NumberArray = [1, 1, 2, 3, 5]

4.any陣列

  • 用any表示陣列中允許出現任意型別
  • 示例
    let list: any[] = ['xjh', 25, { key: 'xx' }]

5.類陣列

  • a.常見的類陣列都有自己的介面定義,如 IArguments, NodeList, HTMLCollection
  • b.示例
    function sum() {
        let args: IArguments = arguments
    }

函式型別

1.函式宣告

  • 示例
    function sum(x: number, y: number): number {
                return x + y;
    }
  • 輸入多餘/少於要求的引數,是不被允許的
    sum(1, 2, 3) // 報錯
    sum(1) // 報錯

2.函式表示式

  • 示例
    let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
        return x + y
    }
  • =>用來表示函式的定義,左邊是輸入型別,需要用括號括起來,右邊是輸出型別

3.用介面定義函式的形狀

    interface SearchFunc {
        (source: string, subString: string): boolean
    }

    let mySearch: SearchFunc = function(source: string, subString: string) {
        return source.search(subString) !== -1
    }

4.可選引數

  • 可選引數,必須接在必需引數後面
  • 可選引數,後面不允許再出現必須引數

5.引數預設值

  • 同ES6
  • 會將添加了預設值的引數,識別為可選引數
  • 不受"可選引數必須接在必需引數後面"的限制

6.剩餘引數

  • 同ES6

7.過載

  • 允許一個函式接受不同數量或型別的引數時,作出不同的處理
  • 示例
    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.手動指定一個值的型別

2.方法

  • <型別>值
  • 值 as 型別

3.斷言成一個聯合型別中不存在的型別是不允許的

    function toBoolean(something: string | number): boolean {
        return <boolean>something
    }

進階

類型別名

定義

  • 用來給一個型別起個新名字

示例程式碼

    type Name = string;
    type NameResolver = () => string;
    type NameOrResolver = Name | NameResolver;

字串字面量型別

定義

  • 用來約束取值只能是某幾個字串中的一個

示例程式碼

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

注意

  • 類型別名與字串字面量型別都是使用type進行定義

元祖Tuple

定義

  • 數組合並了相同型別的物件,而元組合並了不同型別的物件

示例

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

注意

1.當賦值或訪問一個已知索引的元素時,可以只賦值其中一項

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

2.直接對元祖型別賦值時,需提供所有型別項

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

3.當新增越界元素時,型別會被限制為定義項的型別

    let tom: [string, number];
    tom = ['Tom', 25];
    tom.push('male');
    tom.push(true); // 報錯

列舉

定義

  • 用於取值被限定在一定範圍內的場景

簡單示例

    enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};
    console.log(Days["Sun"] === 0); // true
    console.log(Days[0] === "Sun"); // 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["Sat"] === 6); // true

說明

  • 未手動賦值的列舉項會接著上一個列舉項遞增
  • 未手動賦值的列舉項與手動賦值的重複了,ts不會察覺,後者只會覆蓋前者

列舉項型別

1.常數項和計算所得項

2.計算所得項·示例

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

3.如果緊接在計算所得項後面的是未手動賦值的項,那麼它就會因為無法獲得初始值而報錯

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

常數列舉

1.示例程式碼

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

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

    var directions = [0, 1, 2, 3]; //編譯結果

2.常數列舉與普通列舉的區別是,它會在編譯階段被刪除,並且不能包含計算成員

外部列舉

1.示例程式碼

    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]; //編譯結果

2.declare 定義的型別只會用於編譯時的檢查,編譯結果中會被刪除

3.外部列舉與宣告語句一樣,常出現在宣告檔案中

概言

  • TS除了實現了所有ES6中的類的功能以外,還添加了一些新的用法

修飾符

    public          可被公開訪問
    private         僅供自身訪問,子類也不可以訪問它的屬性/方法
    protected       受保護的訪問,子類可以訪問它的屬性/方法

抽象類

  • 用abstract關鍵字定義(抽象類/抽象方法)

  • 不允許被例項化

  • 抽象方法必須被子類實現

類與介面

類實現介面

  • 把各層級的類之間共有的特性提取出來進行實現的部分叫介面

  • 介面通過interface定義,通過implements實現

  • 一個類只能繼承一個類,但是可以實現多個介面

介面繼承介面

  • 通過extends關鍵字來繼承

介面繼承類

  • 通過extends關鍵字來繼承

泛型

定義

  • 在定義函式、介面或類的時候,不預先指定具體的型別,而在使用的時候再指定型別的一種特性

  • 示例程式碼
    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 swap<T, U>(tuple: [T, U]): [U, T] {
        return [tuple[1], tuple[0]];
    }
    swap([7, 'seven']);     // ['seven', 7]

泛型約束

1.由於事先不知道它是哪種型別,所以不能隨意的操作它的屬性或方法(會報錯)

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

2.對泛型進行約束

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

3.泛型之間也可以相互約束

泛型介面

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

泛型類

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

泛型引數的預設型別

1.當使用泛型時沒有在程式碼中直接指定型別引數,從實際值引數中也無法推測出時,這個預設型別就會起作用

2.示例程式碼

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

宣告檔案

宣告檔案

概要

1.使用第三方庫時,需要引用它的宣告檔案,才能獲得對應的程式碼補全、介面提示等功能

2.宣告語句中只能定義型別,切勿在宣告語句中定義具體的實現

3.示例程式碼

    declare var jQuery: (selector: string) => any

4.注意

  • 帶有宣告語句的檔案即為宣告檔案
  • 宣告檔案必需以 .d.ts為字尾
  • 如果ts無法解析宣告檔案,檢查tsconfig.json中的files、include和exclude配置,確保其包含了對應庫的xx.d.ts檔案

5.第三方宣告檔案

  • 通常使用@types統一管理第三方庫的宣告檔案
  • 示例
    npm install @types/jquery --save-dev

宣告合併

1.如果定義了兩個相同名字的函式、介面或類,那麼它們會合併成一個型別

2.合併的屬性型別必須是唯一的

3.函式合併

  • 參考函式過載示例

4.介面合併

    interface Alarm {
        price: number;
    }

    interface Alarm {
        weight: number; 
    }

    // 等同於
    interface Alarm {
        price: number;
        weight: number;
    }

5.類合併

  • 與介面的合併規則一致

書寫宣告檔案

1.宣告合併

  • 一個東西既可以是函式,也可以是物件(擁有子屬性)
  • 示例
    declare function jQuery(selector: string): any
    declare namespace jQuery {
        function ajax(url: string, settings?: any): void
    }

2.export

  • 宣告檔案中禁止定義具體的實現
  • interface前是不需要declare的

3.export default

  • 只有 function、class 和interface可以直接預設匯出,其他變數需先宣告,再預設匯出
  • 一般會將匯出語句放在整個宣告檔案的最前面

4.commonjs規範下的export

  • commonjs匯出
    module.exports = foo;   // 整體匯出
    exports.bar = bar;      // 單個匯出
  • 在匯入方式上,有兩種跟es6語法相同
  • 官推匯入方式(commonjs下)
    import foo = require('foo');    // 整體匯入
    import bar = foo.bar;           // 單個匯入
  • 官推匯出方式(commonjs下)(假如要為它寫型別宣告檔案的話)
    export = foo
    declare function foo(): string
  • 兩種官推方式都是ts為了相容AMD和commonjs規範而創立的

5.UMD庫的匯出

    export as namespace '匯出名'

6.在npm包/UMD庫中擴充套件全域性變數

    declare global {
        interface String {
            prependHello(): string
        }
    }

7.模組外掛(declare module)

    // index.d.ts宣告
    import * as moment from 'moment';

    declare module 'moment' {
        export function foo(): moment.CalendarKey
    }

    // index.ts使用
    import * as moment from 'moment';
    import 'moment-plugin';

    moment.foo();

8.宣告檔案中的依賴

  • 示例參考<模組外掛index.d.ts宣告>
  • 三斜槓指令
    • 早期版本中為了描述模組之間的依賴關係而創造的語法
    • 使用場景:書寫一個全域性變數,並且需要依賴一個全域性變數宣告檔案時
    • 示例
      /// <reference types="jquery" />
    
      declare function foo(options: JQuery.AjaxSettings): string
    • 三斜線指令必須放在檔案的最頂端,它前面只允許出現單行或多行註釋
  • 拆分宣告檔案
    • 示例
      /// <reference types="sizzle" />
      /// <reference path="JQueryStatic.d.ts" />
      /// <reference path="JQuery.d.ts" />
      /// <reference path="misc.d.ts" />
      /// <reference path="legacy.d.ts" />
    
      export = jQuery;
    • types用於宣告對另一個庫的依賴,path用於宣告對另一個檔案的依賴

9.自動生成宣告檔案

  • 命令列
    • 在命令列中新增--declaration(簡寫-d)
  • 配置檔案
    • 在tsconfig.json中新增declaration選項
    • 示例程式碼
      {
          "compilerOptions": {
              "module": "commonjs",
              "outDir": "lib",
              "declaration": true
          }
      }

釋出宣告檔案

1.將宣告檔案和原始碼放在一起(ts宣告查詢也遵循a=>b=>c)

  • package.json中配置types或typings欄位,指定宣告檔案
  • 在專案根目錄下,放置一個index.d.ts宣告檔案
  • 針對package.json的入口檔案位置,放置一個宣告檔案

2.將宣告檔案釋出到@types下

  • 與普通的npm模組不同,@types統一由DefinitelyTyped管理

內建物件的宣告檔案

  • TypeScript核心庫定義中,包含了所有遊覽器環境用到的型別(ECMAScript,DOM和BOM標準)

  • TypeScript核心庫定義中,不包含Nodejs部分(需要npm install @types/node --save-dev)