Typescript 2+迷你書 :從入門到不放棄

CRPER發表於2017-03-15

前言

  • 文中會穿插部分ES6&7的程式碼(此處不在解釋什麼作用域什麼的,作用什麼的),能言簡意賅的絕不廢話哈;
  • 文中的ts或者ts2皆指typescript;

這不是一個合格的手冊,要想深入和更全應該看官方的手冊文件加以練習和嘗試


Typescript 三問三答

  1. Typescript是什麼!
    • ts不是程式語言,可以理解為一種補充(超集),讓JS具有後端的部分特點(型別推斷)
    • ts不等同於js,但是可以理解為類似CoffeeScript,可以編譯成js,跨平臺且專案是開源的
  2. Typescript能做什麼!
    • 能提升你的程式碼質量,只要你願意遵循它的套路(標準)
    • 能簡化你的程式碼複雜程度
      • ts對於ECMAScript的特性和支援一直很超前,有些特性還沒釋出,ts上就能使用了(各種語法糖,還能轉編譯到ES5乃至ES3)
      • ECMAScript的部分特性還是參考ts的,其實微軟也是TC39委員會成員之一啦
      • 有部分後端語言的特性,比如implements,extends,inteface,abstract等。。
  3. Typescript難麼
    • 不誇大的說,若是常規使用,上手還是相當快的(有後端經驗的小夥伴)
    • 濃濃的C#風格,目前最新版本是typescript 2.2,我簡稱它為ts2

注:文章的部分內容是會結合實際應用經驗來說,基於ts最新的2.2來說;


迷你文件手冊

基礎型別及常規宣告

眾所周知:JS中有這麼幾種型別的資料: Symbol,boolean,Number,Object[Array在js中也屬於物件],undefind,null,String;

那麼在TS中如何來宣告一個變數或者物件亦或者宣告返回物件的值型別呢?

// 最簡單的一個宣告, 在需要推導的後面加上冒號和型別
let a: number;  

/*但是有些人有強迫症的,比如會出現這類的寫法,但是這類寫法在最新版
*中會變成一個警告,說是該型別沒必要再寫推導型別了,建議移除;
*/
let b: number = 0; // 會出現警告,應該改成下面這個
let c: boolean = false;

let b = 0; // 推薦的寫法;
let c = false;

//--------------- 在TS中型別會比JS稍微多一些,記得ts中型別全部是小寫---------------//

let temp1: number; // 二進位制,八進位制,浮點,整數皆歸這類
let temp2: string; // 字串型別,ES6的模板字串也屬於這貨
let temp3_1: number[]; // 代表返回值均為數字的陣列
let temp3_2: Array<any>; // 陣列泛型,代表陣列內可以包含所有型別
let temp3_3: [string,number,boolean]; 元組陣列,子元素的型別強制一一對應
let temp4: any; // any代表任意型別都可以,萬金油
let temp5: void;  // 只能賦值undefined或者null
let temp6: undefined; // 只能賦值undefined,在--strictNullChecks模式下只能賦值給: void
let temp7: null; // 只能賦值null,在--strictNullChecks模式下只能賦值給: void
let temp8: object; // 類似Object.create的皆可以

//------ 特殊型別
// 對於異常這類的永遠得不到正確返回值的,使用: never
try{
 ...
}catch(e): never{
    ...
}

// 不需要推斷的--- 型別斷言,簡言之,我不需要ts推導,本來就知道這貨是什麼
// 最常見的是用於引入一些第三方的js庫的時候,比如echarts的作用域必須全域性window
const echarts = (<any>window)['echarts'];複製程式碼

介面

ts中的作用,可以簡單的理解為規範型別

export interface Personal{
    name: string; 
    age: number;
    sex: string;
    colthColor?: string[]; // 衣服顏色,[?]可選引數,返回字串陣列
    readonly idCard: number | string; 
    /* readonly是新版本加進來的,和const大同小異,
    *兩者區分readonly用於介面宣告而const能作用於變數, 
    *後面number | string其實另外一部分的內容點,叫做聯合推導,就是值可以是數字或者字串
    */
    // 介面也能描述函式引數這些,寫法都差不多
}

//------使用,比如用於介面上傳 ----- 記得匯入
// 介面內的欄位及型別會強制對應,不然會報錯,這會讓程式碼更加嚴謹;
uploadUserInfo( personalParam: Personal ): Observable<any> {
    return this.authHttp.upload( environment.baseUrl + 'xxxx/UpdateInfo', personalParam );
}

// 介面也可以用實現某些方法,和C#或Java裡介面的基本作用一樣
// 關鍵字implements
export class MitUpdateUserInfo implements  Personal{
 ...
}

// 介面與介面之間可以實現單繼承或者多繼承
// 關鍵字extends
interface extraParam{
    favority:any
}

interface Personal extends extraParam{
    ....
}複製程式碼

ts內的類其實就是es6的類寫法,只是可以完整支援而已

export class test{
    public a: number; // 在類直接宣告的作用域整個類內,預設為public,寫不寫看自己了
    constructor(){
        // 建構函式,建構函式必須可以帶上什麼範圍
        // public,private,protected
    }

    test(){
        函式。。。。
    }
}

/** 修飾符:更細緻的解釋請看官方文件手冊
* public : 預設,公共開放
* private: 自身類使用
* protected : 派生類可以使用【子類這類】
* readonly : 只讀
*/


// 同理類也是必然可以繼承的,但是不能多繼承,不能多繼承,不能多繼承
// 依舊關鍵字是extends
export class test1{
    constructor(private hh:string){
        this.hh = hh;
    }

    test(a){
        console.log(this.hh + a);
    }

}

export class test2 extend test1{
    public a = 'sfasdf';
    constructor(a:string){
        super(a);
    }

    test(){
        super.test(); // 繼承方法
    }
}


// 當然,類中不可能少了抽象類,這貨的作用就是讓一堆人繼承實現它帶的東西
export abstract class test3 {
  constructor(public name: string) {

  }

  eat(): void {
    console.log(this.name + '吃吃吃');
    // 可以在抽象內實現預設方法,然後子類複寫或者直接用
  }

  abstract move(): void;  // 等著子類來實現動的方法
}

// 也有set和get,就是getter和setter複製程式碼

函式 | 泛型 | 列舉 | 交叉及聯合型別

  • 函式
exrpot class test4{
    constructor(){}

    sun(leaf:string,drink?:boolean):void{
        ...
        // 傳入的引數leaf為字串型別,該方法沒有任何返回
        // 水為可選引數,傳入為布林型別
    }
}

// 應該有人想問this,但是this的範圍該怎麼樣就怎麼樣,在被呼叫的上下文中確定
//適量的使用箭頭函式會讓你寫起來更舒服複製程式碼
  • 泛型
    泛型可以簡單粗暴的理解為,你傳入什麼型別,就返回什麼型別的值
// TS中的泛型只能用於介面,類(例項),不能用於列舉和名稱空間
// 泛型用符號T表示,不一定要用<T>的寫法
identity<T>(arg: T): T {
    return arg;
}複製程式碼
  • 列舉
    列舉其實就是一組常量,內部的關係是互相對映的
// 這裡拿官方的文件例子就很清晰了。。
enum Enum {
    A
}
let a = Enum.A;
let nameOfA = Enum[Enum.A]; // "A"複製程式碼
  • 交叉型別

交叉型別就是複合型別,把多種型別彙總為一種型別,用 & 符號關聯 , 常見用於多個物件的組合

test(args1: A,args2: B ): A & B{
   ...
}複製程式碼
  • 聯合型別

就是型別可以或!!!

let a: number | string | null;  // 即a可以賦值為這三種型別的任意一種複製程式碼

雜七雜八的總結

至此,TS2的常規用法相信大部分小夥伴都能一知半解了。。至於更深入的,其實用的並不多,我自己也沒用到那些;
【d.ts和tsconfig.json的寫法請看官方文件,有很詳細的教程】, 安裝這些就不說了

在這裡彙總下我之前遇到的問題及解決方案;

  1. 引入第三方庫該怎麼破,比如主流的jquery啥米的,會報型別錯誤啥的。。
    傳送門:github.com/DefinitelyT…
    安裝對應的types,即可解決。。。至於上面沒有的只能自己動手豐衣足食了【看官方文件然後自己寫個】;
    然後放到對應的檔案頂部,用/// <reference types="..." />引入

  2. 如何宣告一個全域性變數?
    寫一個公用的xx.d.ts;在裡面用寫,大致如下

declare var System: any;
declare var $: any;
declare var jQuery: any;
declare var _: any;
declare var echarts: any;
declare var BMap: any;
declare var BMapLib: any;複製程式碼

額外的一堆堆的廢話,有些新人經常在群裡問ES6模組匯入有幾種方式和什麼意思,這裡我也來個解釋。。

// 從元件中匯入AppComponent這個類
import { AppComponent } from './app.component'; 

// 匯入所有內部可以匯出的
import * from './app.routes';

// 匯入某個功能並且把它別名化(alias)
import { MitNotificationsModule as notify } from './widgets/mit-notifications/mit-notifications.module';

// 匯入兩個功能(成員)
import { UserAdd , UserDelte } from "User.module"; 

// 上面兩個的結合
import { UserAdd , UserDelte as d } from "User.module"; 

// 匯入預設成員及成員
import defaultMember, { UserAdd  } from "User.module"; 

// 匯入預設成員且重新命名
import defaultMember, * as name from "module-name"; 

// 匯入整個模組,慎重使用,儘量匯入一些你需要用到的,避免這種,有可能你們有部分程式碼會影響全域性
import "global.module";


//------- 說了匯入,那麼順便扯扯匯出把
// 匯出的花樣也挺多,下至變數常量,上至物件函式
// 比較有差異的就是default 。。 其他的和上面大致對應
export default const name = 'crper'; // 預設成員,一個模組只有一個至多一個預設
export { a , b} // 匯出a,b
....複製程式碼

相關文章