初探TypeScript

Share This Post

TypeScript 到底是什麼?

由 Microsoft 主打,擁有型別系統(Type System)與介面(Interface)設計的語言,

TypeScript 與 JavaScript 的相容性

TypeScript 是 ECMAScript 2015 (ECMAScript 6 或 ES6) 的 strict 超集。 這表示所有的 JavaScript 程式碼也是 TypeScript 程式碼,而 TypeScript 程式可順利地取用 JavaScript。

瀏覽器只能了解 JavaScript。 為了讓您的應用程式得以運作,使用 TypeScript 撰寫時,請編譯程式碼,並將其轉換為 JavaScript。

圖片來源

TypeScript 本身的優點

  1. 加強「Type」部分的JavaScript
  2. 開發完成後還是需要編譯成JavaScript
  3. 適用於開發前後端

建立環境

新增

npm install -g typescript

TS 初始化設置文件並會產生tsconfig.json 檔案

tsc --init

預設的config檔案

{
    "compilerOptions": {
        "target": "es2016",  //指定編譯生成的JS版本
        "module": "commonjs", //指定生成哪種模組
        "strict": true,  //採用嚴格模式  
        "esModuleInterop": true,  //兼容模組導入的方式
    }
}

新增我們config檔案設定,其他詳細的config設定

"outDir": "./dist", //完成編譯後生成 js 文件的路徑                           
"rootDir": "./src",   //代表 ts 文件的入口路徑
"strictNullChecks": true // 將 null 與 undefined 視為不同的型別
"strictPropertyInitialization":false //啟用所有嚴格類型檢查模式


圖片來源
(在預設狀況下(strictNullCheck 為 false),值為 undefined 或 null會被推論為 any型別,因為這時候 undefined 和 null 可以是任何資料型別的值)


第一個 TypeScript 專案

沒有安裝過 TypeScript 的話,必須先安裝它的指令工具,記得用 -g

npm install -g typescript

初始化專案,取得TypeScript 編譯器的設定檔

tsc --init

建立一個檔案index.ts

const message='Hi';

function say(something:string):void{
    console.log(something);
}

say(message);

執行編譯
TypeScript 編譯器就會幫我們自動掃描所有 .ts 結尾的檔案並且產出 JS 檔案。
(在未提供其他參數的情況下執行,會編譯目前資料夾中所有的.ts檔案,額外延伸 tsc 命令列介面文件

tsc

好用指令推推
每當TS檔案有改變時,自動編譯JS檔案

tsc --watch

型別推論(Type Inference)

使用「類型提示」來識別變數或參數的資料類型。
→ 可透過靜態類型檢查,在開發初期就找出程式碼問題。

let a =123;
// TS 編譯器便會自動推論出資料型別為number

結論:没有明確指定型別的情況下,由 TS 自動判斷型別

型別斷言(Type Assertion)

編譯器接受開發者手動寫下型別,並且不會再送出警告錯誤。
語法:

  1. <型別>值 (angle-bracket <>)寫法 let code: any = 123; let employeeCode = <number> code;
  2. 值as 型別(as keyword)寫法
   let code: any = 123; 
   let employeeCode = code as number;

:::info
兩者寫法效果相同,但React專案使用JSX語法時只能用第二種。
:::

結論:手動指定資料型別,可以覆蓋掉 TS 自動推論的資料型別

型別註解(Type Annotation)

使用手動註解的方式,明確宣告資料型別,告訴編譯器必須符合註解的類型。
語法:在變數、參數或屬性後面加上冒號:型別

//變數的型別註解
const age: number = 32

//函式參數的型別註解
function display(name: string){
    console.log(name);
}

//函式參數/回傳值的型別註解
function display(a: number,b: number): number{
    return a + b
}

結論:明確指定資料型別。

型別註解 v.s 型別斷言

  1. 大部分情況下會使用型別註解 ; 型別斷言使用情境較少。
  2. 型別註解告訴編譯器這個資料必須永遠都是這個型別 ; 而型別斷言則主要用在覆蓋 TS 編譯器自動進行的型別推斷和型別相容性規則(Type Compatibility),告訴編譯器你知道這個值要符合斷言的型別,從而避免了上面範例中型別不兼容的錯誤產生。關於兼容性的討論在stackoverflow看到不錯的討論。
  3. 型別註解大部分使用在初始化階段,像是宣告變數、函式參數或回傳值型別等 ; 而型別斷言可能用在接收外部參數,中間過程需要明確指定資料型別的時候

資料型態

基本型別(Basic Types)

Type型別分類筆記
stringprimitive定義字串型別。
numberprimitive定義數值型別。
booleanprimitive定義布林值型別。
nullprimitive定義空值型別, 亦可賦值給所有型別(嚴謹模式則無法),something is currently unavailable(當你使用在宣告了變數,但又不想讓它是未定義的狀態)。
undefinedprimitive定義 undefined 型別,亦可賦值給所有型別(嚴謹模式則無法), something hasn’t been initialized(變數沒有被宣告,或者是已經宣告了,但是沒有賦值)
// -----------------基本類型-----------------
let name1:string = 'bob'
let name2:string = "bob"

// 支援 10 進制和 16 進制,也支援 ES6 的 2 進制和 8 進制的最新寫法
let decLiteral: number = 6;
let hexLiteral: number = 0xf00d;

// ES6 中的 2 進制表示法
let binaryLiteral: number = 0b1010;

// ES6 中的 8 進制表示法
let octalLiteral: number = 0o744;
let notANumber: number = NaN;

let infinityNumber: number = Infinity;
let boo:boolean=true;

let name: any;
name = undefined // OK 
name = null      // OK

let age: undefined; 
age = undefined  // OK
age = null       // Error 
age = 123        // Error 

let spot: null; 
spot = null      // OK
spot = undefined // Error
spot = 123       // Error

:::info
JSON value 不支援 undefined
:::

TypeScript 才有的型別 : any、unknown、void、 never、 union types(聯合型別) 、intersection types(交集型別)、 literal types(字面值型別)、 tuple(元組)、 enums(列舉)。

Type型別分類筆記
objectobject定義物件型別。
arraysobject可使用「型別 + 方括號」或陣列泛型來表示陣列。
functionobject一個函式有輸入和輸出,可以針對參數(輸入)及返回值(輸出)進行型別規範。
// 陣列
let arr:string[]=['a','b'];
let arr:Array<string>=['a','b'];
let arr2:string[][]=[['aa','bb',1]]
let arr3:string[][]=[['aa','bb'],[1]]

let combine = [1, 'hi', 'hello', 2 ,'world'];
// 針對混合型態的陣列推論結果為 (string | number)[],TS會當作聯合型別(union)來處理
// ----------------- object -----------------
type Card={
    name:string;
    desc:string;
}

interface Card2{
    name:string;
    desc:string;
}
const obj :Card={
    name:'bbb',
    desc:"---"
};
// ----------------- object -----------------
type Card={
    name:string;
    desc:string;
}

interface Card2{
    name:string;
    desc:string;
}

interface Card2{
    age?:number;
    // 代表可以是number或undefined
}
const obj :Card2={
    name:'bbb',
    desc:"---"
};
//----------------- 泛型 -----------------
function p<T>(data:T){
    console.log('data',data)
}
p<number>(9000);
p<string>('wewe');
p<boolean>(false);
//---------Function--------------
//函式宣告式
function add(x: number, y: number):number {
    return x + y;
}    

//  函式表達式(匿名函式)
let add = function(x: number, y: number) { 
    return x + y; 
};    
Type型別分類筆記
anyTS表示允許賦值為任意型別。
unknownTSunknown 和 any 一樣可以接受任何型別賦值,但 any 可以賦值給任何型別,unknown 只能賦值給 any 和自己。
voidTS表示沒有任何返回值的函式。
neverTS來表示不應該存在的狀態的型別,一般用於錯誤處理函式。
union typesTS聯合型別(union type) 使用|表示其定義的值可以為多種型別。
intersection typesTS交集型別(intersection type) 使用 & 表示其定義的值都必須符合多種型別。
literal typesTS某些特殊的”值”可以當作”型別”來使用。
tupleTStuple 就是合併了不同型別的物件。
enumTS列舉(enum)型別可以用來管理多個同系列的常數,作為狀態判斷使用。
type A =number | string;

let z :A; //變數Z屬於A類型,可接受number與string
z='wewewewew';
z=12121212;
z=true

//物陣列的元素沒有你要的型別,可以用 union 聯合型別進行型別註記
//不限定數量只要元素符合型別
let foo: (string| number)[] = ['hello', 'ya', 'yeah'];
//或
let foo: Array<string | number> = ['hello', 'ya', 'yeah'];
// 元組,元素個數必須固定,各個元素格式也必須完全吻合
let tuple:[number,String,boolean]=[1,'a',true];
let tuple2:[String,String]=['a','a'];

// -----------------Enum 枚舉-----------------
// 以座位感測來舉例
enum SeatStatus{
    ERROR= -1,
    USING= 0,
    AVAILABLE= 1,
    IDLE_TOO_LONG= 2,
}

const staus=SeatStatus.ERROR;
// -----------------交集-----------------
type Info1 ={
    name: string,
    age: number
}

type Info2 = {
    isGirl : boolean,
    nickname: string 
}

type IntersecInfo = Info1 & Info2

let person5 : IntersecInfo ={
    name : 'Una',
    age: 18,
    isGirl : true,
    nickname: 'Nana'
}

指定物件的類型

:::info
type vs interface:interface是可以擴充的但type不行
:::

物件導向

//----------------- class -----------------
// private
// public
// protected

class Live{
    public roomName:string;
    private id :string;
    protected name:string;

    constructor(roomName1:string,id1:string,name1:string){
        console.log('直播中...')
        this.roomName=roomName1;
        this.id=id1;
        this.name=name1;
    }
}

const live=new Live('1賀','011111','12121');

console.log(live);

參考資料:https://docs.microsoft.com/zh-tw/learn/modules/typescript-get-started/2-typescript-overview

https://ithelp.ithome.com.tw/articles/10217384
https://ithelp.ithome.com.tw/articles/10220016

訂閱研究文章

Get updates and learn from the best

More To Explore

Commitizen

前言: 現今軟體功能複雜與龐大,在開發過程中,不管單人開發還是多人協作,對所編寫的代碼與代碼版本管控都是必須的

NFC x Mifare

實作紀錄 readMifare readNdef 知識補充 手機NFC隱含攻擊弱點 掌握原理避免無線盜刷 MI

Scroll to Top