TypeScript不僅支持JavaScript所包含的數據類型,還額外擴展了許多實用的數據類型,例如枚舉、空值、任意值等。
## 一、JavaScript的數據類型
  JavaScript的數據類型包括6種基本類型:undefined、null、布爾值、數字、字符串以及ES6新增的Symbol,還有1種復雜類型:object。由于TypeScript提供了可選的靜態類型聲明(即在變量后跟一個冒號和類型聲明),因此同樣的變量聲明,在TypeScript中將更能傳播代碼的意圖,并且在編譯時還能驗證代碼的正確性。在下面的代碼中,聲明了6種類型(不包括Symbol),并且引入了ES6新增的二進制、八進制和模板字面量。
~~~js
let isDone: boolean = false;
let decimal: number = 10; //十進制
let hex: number = 0xa; //十六進制
let binary: number = 0b1010; //二進制
let octal: number = 0o12; //八進制
let name: string = "strick";
let template: string = `my name is ${name}`; //模板字面量
let u: undefined = undefined;
let n: null = null;
let obj: object = {};
~~~
## 二、TypeScript擴展的類型
**1)任意值**
  當變量聲明為任意值(any類型)時,它在編譯階段會跨過類型檢查,并且能被賦為任意類型的值,還允許訪問任意屬性、調用任意方法。在下面的示例中,雖然能編譯通過,但是在使用時卻會拋出錯誤,因為字符串類型的變量沒有toFixed()方法。
~~~js
let data: any = 10.5;
data = "strick";
data.toFixed(); //錯誤
~~~
  注意,沒有顯式聲明類型的變量,默認都是any類型的。
**2)空值**
  在JavaScript中,沒有空值(void類型)的概念。TypeScript中的void不表示任意類型,與any類型相悖。當一個函數沒有返回值時,通常會將其返回值的類型聲明成void,如下所示。
~~~js
function send(): void { }
~~~
  如果聲明一個void類型的變量,那么它的值只能是undefined或null,如下所示。
~~~js
let u: void = undefined;
let n: void = null;
~~~
**3)Never**
  never類型表示那些永不存在的值的類型,例如包含死循環不會有返回值的函數或拋出錯誤的函數,如下所示。
~~~js
function loop(): never {
while (true) {}
}
function error(message: string): never {
throw new Error(message);
}
~~~
  注意,當一個函數沒有返回值時,它返回一個void類型;而當函數被意外中斷時,它返回一個never類型。
  由于never類型是所有類型的子類型,因此可被賦給任意類型。但是除了其自身之外,其它類型(包括any)都不能賦給它,如下所示。
~~~js
let none: never;
let digit: number = none; //正確
let figure: never = 10; //錯誤
~~~
**4)數組**
  在TypeScript中,有兩種常見的數組聲明方式。第一種通過類型和方括號組合來表示數組,第二種是使用數組泛型,如下所示。
~~~js
let arr1: number[] = [1, 2, 3];
let arr2: Array<number> = [1, 2, 3];
~~~
  在指定了元素類型之后,就不能添加其它類型的元素,例如為數組的push()方法傳入一個字符串數字,如下所示,在編譯時會報錯。
~~~js
arr1.push("4");
~~~
  如果數組需要包含各種類型的元素,那么可以將其聲明成any類型,如下所示。
~~~js
let arr3: any[] = [1, "2", true];
~~~
**5)元組**
  在TypeScript中,元組(Tuple)會合并不同類型的值,例如定義一對string和number兩種類型的元組,如下所示。
~~~js
let list: [string, number];
~~~
  當為元組賦值時,需要指定相應類型的元素,即先傳字符串,后傳數字,下面代碼的第二次賦值沒有按照這個順序,因此在編譯時將報錯。
~~~js
list = ["strick", 10]; //正確
list = [10, "strick"]; //錯誤
~~~
  也可以通過索引為元組添加元素,但也要遵守類型限制,如下所示。
~~~js
list[0] = "strick";
list[1] = 10;
~~~
  當添加越界的元素時,只要該元素是元組的聯合類型(既可以是字符串,也可以是數字),就能編譯成功,如下所示。
~~~js
list.push(10); //正確
list.push(true); //錯誤
~~~
  在編譯第二條語句時,會報“Argument of type 'true' is not assignable to parameter of type 'string | number'.”的錯誤。
## 三、枚舉
  枚舉是一個被命名的常量集合,該類型也是對JavaScript標準數據類型的一個補充。和C#、Java等其它語言一樣,使用枚舉可以更清晰的表達代碼意圖。TypeScript支持數字的和字符串兩種類型的枚舉。
**1)數字枚舉**
  默認情況下,定義的都是數字枚舉,如下所示,其中枚舉成員的值從0開始自增長,例如Up的值為0,Down的值為1,其余依次遞增。
~~~js
enum Direction { Up, Down, Left, Right }
~~~
  通過枚舉名可以正向映射得到枚舉值,而通過枚舉值也可以反向映射得到枚舉名,如下所示。
~~~js
Direction.Up; //0
Direction[0]; //"Up"
~~~
  由于TypeScript中的枚舉會被編譯成下面這樣,因此才能通過兩種映射方式分別得到枚舉名和枚舉值。
~~~js
var Direction;
(function (Direction) {
Direction[Direction["Up"] = 0] = "Up";
Direction[Direction["Down"] = 1] = "Down";
Direction[Direction["Left"] = 2] = "Left";
Direction[Direction["Right"] = 3] = "Right";
})(Direction || (Direction = {}));
~~~
  數字枚舉也支持手動賦值,例如將上例中的Direction從1開始遞增,如下所示,Down的值為2。注意,定義的數字還可以是小數、負數等各種與數字兼容的值。
~~~js
enum Direction { Up = 1, Down, Left, Right }
~~~
  或者也可以為每個枚舉成員都賦值,如下所示。
~~~js
enum Direction { Up = 1, Down = 3, Left = 5, Right = 7 }
~~~
**2)字符串枚舉**
  在字符串枚舉中,每個成員的值都得是字符串類型的,如下所示。
~~~js
enum Color { Red = "RED", Green = "GREEN", Blue = "BLUE" }
~~~
  字符串枚舉提供了有意義、可調試的字符串,常用于簡單的值比較,如下所示。
~~~js
if (colorName === Color.Red) {
console.log("success");
}
~~~
**3)異構枚舉**
  枚舉還可以混合字符串和數字兩種成員,如下所示。
~~~js
enum Mix { Up = 1, Red = "RED" }
~~~
**4)枚舉成員**
  每個枚舉成員都會包含一個值,而根據值的來源可將成員分成常量成員(Constant Member)和計算成員(Computed Member)。
  之前示例中的枚舉成員都是常量,除此之外,當枚舉成員通過常量枚舉表達式初始化時,也會成為常量成員。常量枚舉表達式是TypeScript表達式的子集,可在編譯階段求值。當一個表達式滿足下面一個條件時(引用自官方文檔),它就是一個常量枚舉表達式:
  (1)枚舉表達式字面量,例如字符串字面量或數字字面量。
  (2)一個對之前定義的常量成員的引用,可以在不同的枚舉類型中。
  (3)帶括號的常量枚舉表達式。
  (4)將+、-、~三個一元運算符中的一個應用到常量枚舉表達式中。
  (5)常量枚舉表達式作為二元運算符+、-、\*、/、%、>、>>>、&、|或^的操作對象。
~~~js
enum Con {
Red = 1,
Green = Direction.Down,
Blue = -(1 << 1),
Yellow = Red & Blue
}
~~~
  不滿足上述條件的枚舉成員會被當作計算成員來使用,并且要注意,計算成員之后,都需要手動賦值,否則會在編譯階段報錯。下面這個枚舉就會編譯失敗。
~~~js
enum Computed {
Red = "red".length,
Green,
Blue
}
~~~
**5)常量枚舉**
  在聲明枚舉時添加const關鍵字就能生成常量枚舉,如下所示。
~~~js
const enum Colors { Red, Green, Blue }
~~~
  常量枚舉只能使用常量枚舉表達式,不能包含計算成員,并且會在編譯階段被刪除,其成員在被引用到時才會被內聯進來,例如將上例Colors中的成員組成一個數組,其編譯結果如下所示。
~~~js
let colors = [Colors.Red, Colors.Green, Colors.Blue];
//編譯結果
var colors = [0 /* Red */, 1 /* Green */, 2 /* Blue */];
~~~
**6)外部枚舉**
  在聲明枚舉時添加declare關鍵字就能生成外部枚舉,即全局枚舉,如下所示。
~~~js
declare enum Externals { Red, Green, Blue }
~~~
  外部枚舉用于描述已經存在的枚舉類型,并且在編譯結果中會移除declare聲明的枚舉,例如將Externals的成員組成一個數組。
~~~js
let externals = [Externals.Red, Externals.Green, Externals.Blue];
~~~
  在運行時調用externals時,如果沒有定義Externals對象,那么就會報錯。
  declare還可以與其它關鍵字(例如var、function、class等)配合,聲明全局變量、全局函數、全局類等。
## 四、類型斷言
  類型斷言可指定一個值的類型,類似于類型轉換,但它只在編譯階段起作用,并且不影響運行時的結果。類型斷言包含兩種語法形式,第一種是尖括號語法,第二種是as語法,如下所示。當在TypeScript中使用JSX時,只支持as語法。
~~~js
let age: number = 28;
let digit = <number>age; //語法一
let figure = age as number; //語法二
~~~
## 五、聯合類型
  聯合類型(Union Type)可讓一個變量擁有多種類型,在語法上,通過豎線(|)來分隔每個類型,例如下面的data變量,既可以是字符串,也可以是數字。
~~~js
let data: number | string;
data = 10;
data = "strick";
~~~
  注意,當訪問聯合類型的成員時,只能訪問它們共有的成員。以上面示例的data變量為例,它能成功調用toString()方法,但不能訪問length屬性(如下所示),因為length屬性只存在于string中,而toString()方法是兩者共有的。
~~~js
data.toString(); //正確
data.length; //錯誤
~~~
## 六、函數
  TypeScript中的函數不僅包含ES6的默認參數、剩余參數等功能,還新增了許多額外的功能,例如類型聲明、重載等。
**1)函數創建**
  在創建函數時可以為其參數和返回值添加類型,從而起到約束的作用。在下面的示例中,通過兩種方式創建函數,第一種是函數聲明,第二種是函數表達式。
~~~js
function add(x: number, y: number): number { //第一種
return x + y;
}
let minus = function(x: number, y: number): number { //第二種
return x - y;
};
~~~
  由于TypeScript能根據return語句推斷出返回值的類型,因此可以省略該類型的聲明。注意,如果在調用函數時傳遞多余的參數,那么在編譯時就會報錯。
~~~js
function add(x: number, y: number) { //正確
return x + y;
}
add(1, 2, 3);
~~~
  TypeScript還可以為函數表達式右側的變量添加類型,如下所示,其中“=>”符號不表示箭頭函數,而是用來定義函數的返回值類型,并且返回值類型必須指定。
~~~js
let minus: (x: number, y: number) => number =
function(x: number, y: number): number { return x - y; }; //正確
let minus: (x: number, y: number) =
function(x: number, y: number): number { return x - y; }; //錯誤
~~~
  注意,兩處的參數名稱可以不一致,只要類型匹配即可,如下所示。
~~~js
let minus: (left: number, right: number) => number =
function(x: number, y: number): number { return x - y; }; //正確
~~~
**2)可選參數**
  在JavaScript中,函數的參數都是可選的,而在TypeScript中的參數默認都是必傳的。如果要讓參數可選,那么需要在其后面跟一個問號(?),如下所示。
~~~js
function sum(x: number, y?: number): number {
return x + y;
}
~~~
  注意,可選參數得位于必選參數之后,下面的寫法是錯誤的。
~~~js
function sum(x?: number, y: number): number {
return x + y;
}
~~~
**3)重載**
  JavaScript里的函數可根據不同數量和類型的參數返回不同類型的值,這樣雖然很便捷,但是無法精確的傳達出函數的輸入和輸出之間的對應關系。TypeScript提供了重載功能,可有效改善JavaScript函數定義不明確的問題。以重載定義多個caculate()函數為例,如下所示。
~~~js
function caculate(x: number, y: number): number;
function caculate(x: string, y: string): string;
function caculate(x, y): any {
return x + y;
}
~~~
  編譯器會從重載列表中選出最先匹配的函數定義,并進行正確的類型檢查。當多個函數定義之間是包含關系時,優先把最精確的定義放在最前面。
  在調用改變后的caculate()函數時(如下所示),傳遞給它的實參,其類型和組合只要能與重載列表中的一個相同,就能編譯成功,否則就會報錯。
~~~js
caculate(1, 2); //正確
caculate("1", "2"); //正確
caculate(false, true); //錯誤
caculate("1", 2); //錯誤
~~~
**4)this參數**
  TypeScript能在定義函數時,顯式地聲明一個this參數,指定this的類型(即限制其指向),從而避免錯誤的使用this。例如為this定義為void類型,那么在函數中一旦使用this,就無法編譯成功,如下所示。
~~~js
function func(this: void) {
this.name = "strick"; //錯誤
}
~~~
  注意,this是個假參數,位于參數列表的最前面,只用來做靜態檢查,不會出現在編譯后的代碼中。
*****
> 原文出處:
[博客園-TypeScript躬行記](https://www.cnblogs.com/strick/category/1561745.html)
[知乎專欄-TypeScript躬行記](https://zhuanlan.zhihu.com/pwts2019)
已建立一個微信前端交流群,如要進群,請先加微信號freedom20180706或掃描下面的二維碼,請求中需注明“看云加群”,在通過請求后就會把你拉進來。還搜集整理了一套[面試資料](https://github.com/pwstrick/daily),歡迎瀏覽。

推薦一款前端監控腳本:[shin-monitor](https://github.com/pwstrick/shin-monitor),不僅能監控前端的錯誤、通信、打印等行為,還能計算各類性能參數,包括 FMP、LCP、FP 等。
- ES6
- 1、let和const
- 2、擴展運算符和剩余參數
- 3、解構
- 4、模板字面量
- 5、對象字面量的擴展
- 6、Symbol
- 7、代碼模塊化
- 8、數字
- 9、字符串
- 10、正則表達式
- 11、對象
- 12、數組
- 13、類型化數組
- 14、函數
- 15、箭頭函數和尾調用優化
- 16、Set
- 17、Map
- 18、迭代器
- 19、生成器
- 20、類
- 21、類的繼承
- 22、Promise
- 23、Promise的靜態方法和應用
- 24、代理和反射
- HTML
- 1、SVG
- 2、WebRTC基礎實踐
- 3、WebRTC視頻通話
- 4、Web音視頻基礎
- CSS進階
- 1、CSS基礎拾遺
- 2、偽類和偽元素
- 3、CSS屬性拾遺
- 4、浮動形狀
- 5、漸變
- 6、濾鏡
- 7、合成
- 8、裁剪和遮罩
- 9、網格布局
- 10、CSS方法論
- 11、管理后臺響應式改造
- React
- 1、函數式編程
- 2、JSX
- 3、組件
- 4、生命周期
- 5、React和DOM
- 6、事件
- 7、表單
- 8、樣式
- 9、組件通信
- 10、高階組件
- 11、Redux基礎
- 12、Redux中間件
- 13、React Router
- 14、測試框架
- 15、React Hooks
- 16、React源碼分析
- 利器
- 1、npm
- 2、Babel
- 3、webpack基礎
- 4、webpack進階
- 5、Git
- 6、Fiddler
- 7、自制腳手架
- 8、VSCode插件研發
- 9、WebView中的頁面調試方法
- Vue.js
- 1、數據綁定
- 2、指令
- 3、樣式和表單
- 4、組件
- 5、組件通信
- 6、內容分發
- 7、渲染函數和JSX
- 8、Vue Router
- 9、Vuex
- TypeScript
- 1、數據類型
- 2、接口
- 3、類
- 4、泛型
- 5、類型兼容性
- 6、高級類型
- 7、命名空間
- 8、裝飾器
- Node.js
- 1、Buffer、流和EventEmitter
- 2、文件系統和網絡
- 3、命令行工具
- 4、自建前端監控系統
- 5、定時任務的調試
- 6、自制短鏈系統
- 7、定時任務的進化史
- 8、通用接口
- 9、微前端實踐
- 10、接口日志查詢
- 11、E2E測試
- 12、BFF
- 13、MySQL歸檔
- 14、壓力測試
- 15、活動規則引擎
- 16、活動配置化
- 17、UmiJS版本升級
- 18、半吊子的可視化搭建系統
- 19、KOA源碼分析(上)
- 20、KOA源碼分析(下)
- 21、花10分鐘入門Node.js
- 22、Node環境升級日志
- 23、Worker threads
- 24、低代碼
- 25、Web自動化測試
- 26、接口攔截和頁面回放實驗
- 27、接口管理
- 28、Cypress自動化測試實踐
- 29、基于Electron的開播助手
- Node.js精進
- 1、模塊化
- 2、異步編程
- 3、流
- 4、事件觸發器
- 5、HTTP
- 6、文件
- 7、日志
- 8、錯誤處理
- 9、性能監控(上)
- 10、性能監控(下)
- 11、Socket.IO
- 12、ElasticSearch
- 監控系統
- 1、SDK
- 2、存儲和分析
- 3、性能監控
- 4、內存泄漏
- 5、小程序
- 6、較長的白屏時間
- 7、頁面奔潰
- 8、shin-monitor源碼分析
- 前端性能精進
- 1、優化方法論之測量
- 2、優化方法論之分析
- 3、瀏覽器之圖像
- 4、瀏覽器之呈現
- 5、瀏覽器之JavaScript
- 6、網絡
- 7、構建
- 前端體驗優化
- 1、概述
- 2、基建
- 3、后端
- 4、數據
- 5、后臺
- Web優化
- 1、CSS優化
- 2、JavaScript優化
- 3、圖像和網絡
- 4、用戶體驗和工具
- 5、網站優化
- 6、優化閉環實踐
- 數據結構與算法
- 1、鏈表
- 2、棧、隊列、散列表和位運算
- 3、二叉樹
- 4、二分查找
- 5、回溯算法
- 6、貪心算法
- 7、分治算法
- 8、動態規劃
- 程序員之路
- 大學
- 2011年
- 2012年
- 2013年
- 2014年
- 項目反思
- 前端基礎學習分享
- 2015年
- 再一次項目反思
- 然并卵
- PC網站CSS分享
- 2016年
- 制造自己的榫卯
- PrimusUI
- 2017年
- 工匠精神
- 2018年
- 2019年
- 前端學習之路分享
- 2020年
- 2021年
- 2022年
- 2023年
- 2024年
- 日志
- 2020