小講Typescript

前言

  Typescript做爲ECMA的實現,javascript的超集,目前已經普遍在項目中使用。typescript是什麼?有什麼具體功能?這些已經被你們寫得差很少了。在這裏,我再也不贅述ts的做用,而是直接用起來,從一個初學者角度告訴你們一些使用typescript的心得。下文將會完整地講解一下typescript各個方面的具體用法,力求在我總結之餘讓你們有所收穫。javascript

正文

  typescript是type+script(js)。它的本質是經過類型定義來限制js靈活多變的語法。ts提供了一些基本類型,但同時容許開發者自定義類型,在一些複雜狀況下還可使用一些高級類型。在使用以前先總結幾點能夠幫助理解的要點:html

  • ts能夠由tsc(Type Script Complier)編譯器編譯成js,項目的ts代碼不管在開發時仍是打包後都會經歷編譯環節,最終執行的只會是js代碼。
  • ts是js的超集,支持全部的JavaScript語法,支持es六、es7等新語法,不須要使用babel進行轉換,在tsconfig.json配置文件中進行配置以後,tsc便能將其編譯成指定版本的js代碼。
  • ts能夠經過tsconfig.json配置一些編譯規則,違反這些規則是致使編譯錯誤,可是即使編譯錯誤不會打斷編譯,tsc仍是會繼續完成編譯。

開始使用

  1. 全局安裝typescriptjava

    yarn global add typescript
    // 或使用 npm
    npm i typescript -g
    複製代碼
  2. 使用tsc進行編譯node

    // 使用默認的配置編譯helloword.ts文件
    tsc helloword.ts 
    複製代碼
  3. 使用tsconfig.json配置文件es6

    // 在根目錄下創建tsconfig.json配置文件
    {
        // 編譯選項
        "compilerOptions": {
            // 編譯目標,如'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'
            "target": "es5",
            // 編譯代碼使用的模塊 'commonjs', 'amd', 'system', 'umd' or 'es2015'
            "module": "commonjs",
            // 輸出文件夾,默認會對應ts文件下生成同名js文件
            "outDir": "./dist",
            // 類型聲明文件的目錄,工做目錄下@types文件會自動加入
            "typeRoots": [],
        },
        // 進行編譯的文件
        // 包含在files中的文件是必定會進行編譯(即便使用exclude去除)
        "files": [
            "hello.ts",
            "word.ts"
        ],
        // 要編譯的目錄或文件
        "include": [
            "./folder"
        ],
        // 不要包含的目錄或文件
        "exclude": [
            "./folder/**/*.spec.ts",
            "./folder/someSubFolder"
        ]
    }
    複製代碼

    完整的配置選項能夠參考 typescript官網typescript

    在創建好配置文件以後,直接執行tsc,編譯好的js文件便會存在於js目錄,當module爲commonjs時,能夠直接使用node進行執行測試。固然還可使用自動編譯,在tsconfig.json頂層中加入 "compileOnSave": true,告訴IDE保存後進行編譯,vscode按照typescript插件,以後執行 tsc -w,這樣子以後就能進行自動編譯。npm

基本類型

  ts提供了Boolean、Number、String、Array、Tuple、Enum、Any、Void、Null、Undefined、Never、Object 共12種基本類型。json

  1. Boolean、Number、String數組

    // 大部分類型的使用很是簡單
    let bool: boolean = true;
    let num: number = 1213;
    let str: string = "Hello World";
    複製代碼
  2. Array和Tuple(元組)bash

    // 數組類型
    let arr: number[] = [1213,2324]
    // 或者 
    const arr2: Array<string> = ['1213', '234234']
    
    // 元組爲有限個有序的數組,元組須要對數組的每一個元素聲明類型
    let tup: [string, number] = ["121", 2321];
    tup[0] = "wadsas";
    tup[0] = 1212 // 報錯
    複製代碼
  3. Enum類型

      枚舉類型是一種很特殊的類型,其它的類型聲明在編譯成js代碼以後便不會存在,但枚舉類型是會編輯成js代碼的。

    enum Tristate {
      False,
      True,
      Unknown
    }
    // 編譯成js以後
    var Tristate;
    (function(Tristate) {
      Tristate[(Tristate['False'] = 0)] = 'False';
      Tristate[(Tristate['True'] = 1)] = 'True';
      Tristate[(Tristate['Unknown'] = 2)] = 'Unknown';
    })(Tristate || (Tristate = {}));
    // 也就是如下對象
    {0: "False", 1: "True", 2: "Unknown", False: 0, True: 1, Unknown: 2}
    
    let tri: Tristate = Tristate.False
    複製代碼

    枚舉選項的值默認是從0開始,上面的Tristate中False爲0, True爲1,Unknown爲2,咱們能夠改變枚舉的值:

    enum HighColor {
      Green,
      Black = 5,
      Blue
    }
    // Green爲0,Black爲5,Blue爲6
    // 字符串枚舉
    enum Type {
      ON_LINE = "ON_LINE",
      DOWN = "DOWN",
      OTHER = "OTHER"
    }
    複製代碼

    還可使用 enum + namespace 的聲明的方式向枚舉類型添加靜態方法

    enum PersonType {
      young,
      middle,
      old
    }
    namespace PersonType {
      export function isOld(person: PersonType) {
        return person === PersonType.old;
      }
    }
    const Marry: PersonType = PersonType.old;
    console.log(PersonType.isOld(Marry));
    複製代碼
  4. Any和Object

    Any是任意類型,當一個變量被聲明爲了any類型,它能夠是任何值,就如js同樣能夠隨意改變其值而不會有提示,當從js項目遷移到ts,any能夠提供極大的便利,但一個項目內應該儘可能少地使用any類型,由於這意味着你放棄類型檢查。

    let a: any = 1231;
    a = "12313";
    a = true
    複製代碼

    Object是對象類型,它僅表示變量是對象類型,不會對對象的屬性作出限制,一個比較適合的場景是Object.create(o: object)

    declare function create(o: object | null): void;
    create({ prop: 0 }); // OK
    create(null); // OK
    create(42); // Error
    
    let obj: object = {a: 12}
    obj = {b: 12, c: 88}
    obj.d = 1212 // 報錯
    
    複製代碼
  5. Void、Never、Null和Undefined

    Void通常用以表示函數或方法沒有返回值,Never表明永遠不會發生的類型。

    function log(name: string):void {
        console.log(name)
    }
    
    // 拋出了錯誤,程序永遠沒有返回值
    function error(message: string): never {
        throw new Error(message);
    }
    // 函數內有一個死循環,永遠不會執行到循環外的代碼
    function infiniteLoop(): never {
        while (true) {
        }
    }
    複製代碼

    Null和Undefined類型爲其它全部類型的子類型,這意味着該類型能夠賦值爲其它任何類型,但這必須在strictNullChecks編譯選項爲false的狀況下。

    let n: null = null
    let u: undefined = undefined;
    let str: string = '1212'
    str = n
    複製代碼

自定義類型

  1. 內聯方式

    let person: {name: string, age: number} = {name: '小龍女', age: 12}
    複製代碼

    內聯方式聲明自定義類型較爲直接,但其聲明的類型沒法複用。

  2. 聲明函數

    函數能夠直接使用function聲明類型,如函數變量則能夠直接經過內聯方式聲明。

    // 使用function進行聲明
    function log(str: string): void {
      console.log(str);
    }
    
    // 對函數變量進行聲明
    interface Log {
      (str: string): boolean;
    }
    type Log2 = {
      (str: string): boolean;
    }
    const myLog: Log = str => {
      console.log(str);
      return true;
    };
    // 直接使用箭頭進行函數聲明
    const toDo: (str: string) => void = str => {
      console.log(str);
    };
    複製代碼
  3. 經過class聲明類型

    es6新增的class能夠直接聲明類型,並且對於靜態屬性、靜態函數的聲明很是方便:

    class Cat {
      name: string;
      static Type: string;
    }
    
    // Cat.Type 會是string類型
    
    const cat: Cat = new Cat();
    複製代碼
  4. interface和type

      interface和type大同小異,均可用於自定義類型,建議直接使用interface聲明類型,在interface沒法實現時使用type。

    interface Point {
      x: number;
      y: number;
      // 加?號表示可選
      x2?: number;
      // 聲明方法
      computed?():void
    }
    // 同名接口合併,下面接口能夠與上面的Point接口合併
    interface Point {
      x2: number;
    }
    // 接口繼承
    interface Point_3D extends Point {
      z: number;
    }
    // 接口能夠被class實現
    class ChartPoint implements Point {
      x: number;
      y: number;
      constructor(x, y) {
        this.x = x;
        this.y = y;
      }
    }
    // 使用type聲明一個類型
    type Person = {
        name: string;
        age: number;
        sex: string;
        showInfo(isLog: boolean):string;
    }
    // 爲Point接口取個別名
    type Point2 = Point
    複製代碼

    有一些狀況下只能使用type進行自定義類型聲明:

    interface Person {
      name: string;
      age: number;
      sex: string;
      id: string;
    }
    // SubPerson類型的全部屬性都存在於Person類型中
    type SubPerson = {
      [key in keyof Person]: Person[key];
    };
    
    function createSubPerson( person: Person, keys: Array<keyof Person> ): SubPerson {
      return keys.reduce(
        (obj, key) => ({ ...obj, [key]: person[key] }),
        {} as SubPerson
      );
    }
    
    複製代碼

字面量類型

  我認爲學習ts比較重要的是區分類型與變量,類型和變量是能夠重名的,有些操做是針對類型,如keyof;有些則是針對變量, 如instanceof;有些則是二者皆可, 如typeof。事實上分清類型與變量一點也不容易。在ts中,值也是一種類型——字面量類型(literal type)

// 全部的值均可以做爲類型
// 字面量類型
let foo: "Hello"; // foo的值只能爲Hello
foo = "Hello";
let num: 12;
num = 12;
let bool: true = true;
let obj2: { a: 121 } = { a: 121 };
複製代碼

變量只能爲一個固定的值,這樣看來字面量類型彷佛沒有什麼價值?結合聯合類型會讓其大有所爲:

type Strs = "a" | "b" | "c" | "d";
type Nums = 1 | 2 | 3 | 4 | 5;
type Bools = true | false;
let str:Strs = 'a' // str只能爲a、b、c、d中的一個

// 對象和聯合類型實現枚舉類型的功能
// 枚舉類型是一種奇特的類型,既能夠充當類型,又能夠當成對象進而訪問它的屬性

// strEnum將數組轉爲對象
function strEnum(o: Array<T>): { [K in T]: K } {
  return o.reduce((res, key) => {
    res[key] = key;
    return res;
  }, Object.create({}));
}
const OTHER = strEnum(["North", "South", "East", "West"]); //此處OTHER是對象
type OTHER = keyof typeof OTHER; // 此處OTHER爲類型

// 如今OTHER具有了枚舉類型的所有特性
let other2: OTHER;
other2 = OTHER.East;
複製代碼
相關文章
相關標籤/搜索