TypeScript 是一個具有型別的 JavaScript 超集合 (Superset)
- 所有 JS 語法,都是合法的 TypeScript 語法。
- 記得以
;
結尾。
TypeScript 編譯器
環境建立
- 安裝指令工具
npm install -g typescript
- 初始化
tsconfig.json
設定檔 tsc --init
tsconfig.json
的檔案,這就是 TypeScript 編譯器的設定檔。
{ |
- 編譯方式
- 編譯所有檔案(讀取 tsconfig.json 設定檔)
TypeScript 編譯器就會自動掃描當前目錄以及子目錄所有.ts
結尾的檔案並且產出 JS 檔案。tsc
- 編譯單一檔案(需指定 TypeScript 編譯器選項)
tsc [options] [file...]
- 顯示完整的 TypeScript 編譯器選項
tsc --all
- 顯示 tsc 編譯器摘要說明
tsc -h
- 編譯所有檔案(讀取 tsconfig.json 設定檔)
設定技巧
tsconfig.json
設定技巧- Glob support in tsconfig.json
*
代表0到多個字元比對(不含目錄分隔字元)。?
代表1個字元比對(不含目錄分隔字元)。**/
代表比對任意子目錄。
- 設定 Base URL 預設 import 的基底路徑
- 設定 Path mapping
- 必須搭配 baseUrl 設定一起使用。
{
"compilerOptions": {
"baseUrl": "src/", //base url
"paths": {
"env/*": ["目錄/*"], //path mapping
"env": ["目錄/檔名"] //path mapping 單檔案
}
}
}- Glob support in tsconfig.json
- 預設載入內建的模組定義檔(–lib)
{
"lib": [
"es2017",
"dom"
]
}
![](/blog/2020/03/25/typescript/lib.png)
型別應用
自動型別推導 (Type Inference)
TypeScript 設定變數可不需特別設定型別,可從
=
的右側的值推導型別。
let fullName: string = 'John'; // : string 非必要 |
靜態型別標示
物件 | JavaScript | TypeScript | ||
---|---|---|---|---|
數值 |
|
|
||
字串 |
|
|
||
布林 |
|
|
||
陣列 |
|
|
||
任意物件 |
|
|
||
列舉 | 以 JS 物件模擬列舉型別 |
|
||
void |
|
|
數值
與 JavaScript 相同,TypeScript 裡的所有數字都是浮點數。 這些浮點數的類型是
number
。 除了支持十進制和十六進制字面量,TypeScript 還支持 ECMAScript 2015 中引入的二進制和八進制字面量。
let isNumber : number = 1; // 數字 |
字串
使用
string
表示文本數據類型。使用雙引號("
)或單引號('
)表示字符串。
還可以使用 ECMAScript 2015 新增的字串模板,它可以定義多行文本和內嵌表達式。這種字符串是被反引號包圍(`
),並且以${ expr }
這種形式嵌入表達式
let isString : string = 'string'; |
布林
最基本的數據類型就是簡單的
true
/false
值
let isTrue : boolean = true; |
陣列
TypeScript 像 JavaScript 一樣可以操作陣列元素。 有兩種方式可以定義陣列。
- 在元素類型後面接上
[]
,表示由此類型元素組成的一個陣列。let name: type = [];
- 使用泛型陣列,
Array<元素類型>
。let arr : number[] = [1, 2, 3];
let arr2 : Array<number> = [1, 2, 3];
let arr3 : string = ['Java', 'ASP', 'PHP', 'Dart']
let arr4 : any = [1, '2', {}, function(){}];
多維陣列
- 多維陣列定義
let name: type[][] = [[], [], []...]
let arr : number[][] = [
[1, 2, 3, 4],
[100, 200, 300],
[0.5, 0.7, 0.9]
]; - 多維陣列使用
console.log(arr[0]) // [1, 2, 3, 4]
console.log(arr[0][1]) // 2
console.log(arr[2][2]) // 0.9
列舉型別 (Enum)
- 可讓程式碼更好維護且增加程式可讀性。
- 可用來管理多個同系列的常數,做為狀態的判斷所使用。
enum name { name1, name2, name3 } |
TypeScript | JavaScript | ||
---|---|---|---|
|
|
- 在 Enum 中的每個常數,都可以經由 = 指定 Value 。
enum httpStatusCodes {
error = 400,
success = 200,
} - 也可以為定義字串
enum httpStatusCodes {
error = 'A',
success = 'B',
}
應用
enum week { |
聯合型別
可包含多種型別的變數,不建議使用。
-
定義
let data : type1 | type2 | ... ;
-
使用
let myData : boolean | string ;
myData = 'Hello world';
console.log(myData); // Hello world
myData = false;
console.log(myData); // false
myData = 100; //編譯錯誤
Function
-
定義
function f(para1: type, ...) : return_type {
// do soming
}return_type
可以為void
代表沒有回傳值。 -
使用
function add(x:number, y:number ): number {
return x + y;
}
const multiply = function (x:number, y:number ): number {
return x * y;
}
console.log(add(10,50)) // 60
console.log(multiply(2,50)) // 100
// 沒有回傳值
function test() : void {
console.log('message');
}
test();
箭頭函式
- 定義
(para1: type, ...) : return_type => {
// do soming
} - 使用
const multiply = (x:number, y:number ): number => {
return x * y;
};
console.log(multiply(2,50)); // 100
可省略參數與默認參數
TypeScript裡的每個函數參數都是必須的。 這不是指不能傳遞
null
或undefined
作為參數,而是指編譯器會檢查用戶是否為每個參數都傳入了值。
-
可省略參數
在參數旁使用?
可以實現可省略參數的功能。const sayHello = function(name?: string):void {
if (name === undefined) {
console.log('Hello World!');
} else {
console.log(`Hello ${name}!`)
}
}
sayHello(); // Hello World!
sayHello('Spongebob'); // Hello Spongebob! -
默認參數
另外也可以為參數提供一個默認值,當用戶沒有傳遞這個參數或傳遞的值是undefined
時。const sayHello = (name: string = 'World'):void => {
console.log(`Hello ${name}!`)
}
sayHello(); // Hello World!
sayHello('Spongebob'); // Hello Spongebob!
其餘參數
當想同時操作多個參數,或者不知道會有多少參數傳遞進來,可使用 ES6 新增的其餘參數來操作所有傳入的參數。
const add = function (...values: number[] ):number { |
class(類別)
- 定義
class className {
property_name1: type;
property_name2: type;
// 構造函數
constructor(para1: type, para2: type, ...) {
// 構造函數內容
}
// 方法
method1 (para1: type, para2: type, ...): returb_type {
// 函式內容
}
// method2, method3, ...
} - 使用
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age
}
sayHello (): void {
console.log(`Hello. I'm ${this.name} and I'm ${this.age} years old`);
}
}
let patrick = new Person (patrick, 28);
console.log(patrick.name); // patrick
patrick.sayHello(); // Hello. I'm patrick and I'm 28 years old
訪問限制
public
: 修飾的屬性或方法是公有的,可以在任何地方被訪問到,預設所有的屬性和方法都是 public 的。protected
: 修飾的屬性或方法是受保護的,它和 private 類似,區別是它在子類別中也是允許被訪問的。private
: 修飾的屬性或方法是私有的,不能在宣告它的類別的外部訪問。
public
class database {
dbname: string;
constructor (dbname: string) {
this.dbname = dbname;
}
public showDB() {
console.log(`數據庫:${this.dbname}`)
}
protected connect() {
console.log(`${this.dbname} 連接中...`)
}
private disconnect() {
console.log(`${this.dbname} 關閉`)
}
}
let firebase = new database("firebase");
firebase.showDB() // 數據庫:firebase
firebase.connect()
// Property 'connect' is protected and only accessible within class 'database' and its subclasses.
firebase.disconnect()
// Property 'disconnect' is private and only accessible within class 'database'.protected
// 繼承
class mongoDB extends database {
public doIt() {
super.connect();
super.disconnect();
// Property 'disconnect' is private and only accessible within class 'database'.
}
}
let mongo = new mongoDB('mongoDB');
mongo.showDB(); // 數據庫:mongoDB
mongo.connect();
// Property 'connect' is protected and only accessible within class 'database' and its subclasses.
monge.doIt() // mongoDB 連接中...private
可透過get
&set
來存取private
的屬性。class BMIcalc {
private weight: number;
height: number;
constructor(weight: number, height: number) {
this.weight = weight;
this.height = height;
}
BMI( ): number {
return Math.round(this.weight / (Math.pow((this.height / 100), 2))) ;
}
set changeW(val: number) {
this.weight = val;
}
get getWeight():number {
return this.weight;
}
}
let otaku = new BMIcalc(70, 172);
console.log(otaku.BMI());
console.log(otaku.height);
console.log(`Your secret weight is ${otaku.getWeight} kg`);
otaku.changeW = 172;
console.log(otaku.BMI());
靜態成員(static)
使用
static
修飾符修飾的方法稱為靜態方法,static
不需要實例化,而是直接透過類別來呼叫。
class Animal { |
命名空間(namespace)
對(函數/類/接口等…)進行分組管理就可以用命名空間。
- 使用
namespace
關鍵字宣告。 - 在命名空間中默認成員都是私有
private
的。 - 在命名空間中使用
export
關鍵字宣告公有資源。 - 命名空間支持跨文件分隔。
- 在Node.js/Require.js中使用
require
關鍵字導入模塊。 - 可以使用
import
關鍵字聲明命名空間的別名。
- 定義
namespace 命名空間的名稱(可以用句號連接多個名稱) {
export class class_name {}
export function func_name {}
export namespace ns_name {}
} - 使用
繼承
使用
extends
關鍵字實現繼承,子類別中使用super
關鍵字來呼叫父類別的建構函式和方法。
-
定義
class child_class extends parenClass {
super()
} -
使用
class database {
protected name:string;
constructor(name: string) {
this.name = name;
};
showInfo() {
console.log(`目前使用的資料庫為 ${this.name}`)
}
}
class MongoDB extends database {};
let mydb = new MongoDB('mongoDB');
mydb.showInfo() // 目前使用的資料庫為 mongoDB
interface(介面)
介面指的是的一個「統一的」、「標準的」規格,對於產品,或是物品進行使用上的規範等。
- 定義
interface name {
// 定義介面內容
} - 使用
-
基本使用
interface Ilabel {
label: string; // 必要屬性,必須傳入!
age?: number; // 可選屬性
[propName: string]: any; //允許任意屬性傳入
}- 在無其他可選及任意屬性下,賦值的時候,變數的形狀必須和介面的形狀保持一致。
- 使用
?
可選屬性,可選屬性的含義是該屬性可以不存在。 [propName: string]: any
允許有任意的屬性。- 一旦定義了任意屬性,那麼確定屬性和可選屬性的型別都必須是它的型別的子集。
-
類別的使用
interface IDatabase {
connect(): void;
close(): void;
exxsql(sql: string): number
}
class mySQL implements IDatabase {
connect() {
console.log('[mySQL]資料庫連接')
}
close() {
console.log('[mySQL]資料庫關閉')
}
exxsql(sql: string) {
console.log('[mySQL]sql 執行成功');
return 0;
}
}
let myDB: IDatabase = new mySQL();
myDB.connect(); // [mySQL]資料庫連接
myDB.exxsql("select * from table"); // [mySQL]sql 執行成功
myDB.close(); // [mySQL]資料庫關閉
class PostgreSQL implements IDatabase {
connect() {
console.log('[PostgreSQL]資料庫連接')
}
close() {
console.log('[PostgreSQL]資料庫關閉')
}
exxsql(sql: string) {
console.log('[PostgreSQL]sql 執行成功');
return 0;
}
}
myDB = new PostgreSQL();
myDB.connect(); // [PostgreSQL]資料庫連接
myDB.exxsql("select * from table"); // [PostgreSQL]sql 執行成功
myDB.close(); // [PostgreSQL]資料庫關閉 -
介面參數
interface IDatabase {
connect(): void;
close(): void;
exxsql(sql: string): number
}
class mySQL implements IDatabase {
connect() {
console.log('[mySQL]資料庫連接')
}
close() {
console.log('[mySQL]資料庫關閉')
}
exxsql(sql: string) {
console.log('[mySQL]sql 執行成功');
return 0;
}
}
class PostgreSQL implements IDatabase {
connect() {
console.log('[PostgreSQL]資料庫連接')
}
close() {
console.log('[PostgreSQL]資料庫關閉')
}
exxsql(sql: string) {
console.log('[PostgreSQL]sql 執行成功');
return 0;
}
}
function doSomething(db: IDatabase) {
db.connect();
db.exxsql('update ...');
db.close();
}
let mysql: IDatabase = new mySQL();
let pgsql: IDatabase = new PostgreSQL();
doSomething(mysql);
doSomething(pgsql);
// [mySQL]資料庫連接
// [mySQL]sql 執行成功
// [mySQL]資料庫關閉
// [PostgreSQL]資料庫連接
// [PostgreSQL]sql 執行成功
// [PostgreSQL]資料庫關閉
-
型別轉換 (Type assertions)
assertions : 斷言
有時宣告了 any,但有時需要做明確的轉型,此時可使用 type assertion 明確轉型,也就是告訴編譯器:「我說這是 string ,就是 string 型別」。
有兩種形式
-
<>
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length; -
as
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;let a = document.getElementById('link'); // HTMLElement 型別
a.href = 'http://www.google.com'; // 找不到 href 屬性
let a = <HTMLAnchorElement>document.getElementById('link');
a.href = 'http://www.google.com'; // OK
嚴格空值檢查模式
開啟方式:
{ |
預設型別檢查模式 (regular type checking mode)
let a: T; // 可以指派 undefined 或 null,因為 undefined 和 null 是所有型別的子型別。 |
- (2)
let a = null; // a 變數型別為 any
let b = undefined; // b 變數型別為 any
嚴格空值檢查模式 (strict null checking mode) (–strictNullChecks)
let a: T; // 不允許為 undefined 或 null (嚴格檢查)。 |
- 可選參數預設就擁有 undefined 型別,啟用嚴格空值檢查模式也一樣(例外)。
type T1 = (X?: number) => string;
- (2)
let a = null; // a 變數型別為 null
let b = undefined; // b 變數型別為 undefined - (3) 非空值斷言運算子 ( ! ) (Non-null assertion operator)
interface Entity {
name: string;
}
function valiableEntity(e?: Entity) {
// 丟出例外 如果 e 是 null or invalid entity
}
function processEntity(e?: Entity) {
valiableEntity(e);
let s = e!.name
// Assert that e is non-null and access name
標記的聯合類型(Tagged union types)
- 傳統的聯合類型
let a : number | string | undefinded;
- 字串實字聯合類型 (Sstring Literal Types)
type test = "Sun" | "Moon" | "Day";
function( X: test ) {
// do something
}可與
switch
搭配使用。 - 數值實字聯合類型 (Numeric Literal Types)
function rollDie() : 1 | 2 | 3 | 4 | 5 | 6 {};
- 列舉成員聯合類型 (Enum Member Types)
let kind: Shapekind.Square | Shapekind.Circle;
- 標記的聯合類型
never 原始型別
- never 原始型別 是 TypeScript 2.0 推出的
- never 用來宣告某個函式或變數永遠不可能有值
let a: never = function test() {while (true) { } };
- void 型別的特性
- 宣告 void 型別的變數只能是 undefind or null (代表沒有值)。
- undefined 和 null 是所有型別的子型別(subtype)。
- never 型別的特性
- never是所有型別的子型別(subtype),不須特別宣告。
- 沒有任何型別是 never 的子型別(除了本身 never 以外)。
- 若函式表達式或箭頭函式要能自動推導出 never 型別,有以下條件。
- 函式中沒有任何 return 敘述或回傳的只會有 never 型別。
- 函式一定不能跑到函式最後一行(end point of the function is not reachable)
this 的嚴格用法
- 預設函式內使用的 this 預設型別為 any
- TypeScript 2.0 新增
-noImplicitThis
- 在函式中預設無法使用 any 型別的 this 變數。
- 如果要使用 this 必須在函式的第一個參數加入型別宣告。(編譯後會自動移除)
function c (this: any, b: number) {
return this.title;
}
-noImplicitAny
![](/blog/2020/03/25/typescript/noImplicitAny.png)
-alwayStrict
- 所有 TypeScript 程式碼都會以 strict mode 進行解析。
- 所有輸出的 JavaScript 程式碼都會加上 “use strict” 在每個 Scope。
- 所有相關模組都會進入嚴格編譯模式。
- 建議使用在 non-module 的程式碼。
第三方庫應用
以 node-request
為例。
-
安裝
node-request
npm init
npm install --save request -
TypeScript 使用方式
- 尋找 tsd 文件 - TypeSearch
- 導入 tsd
npm install --save @types/request
- 編寫代碼
- 運行代碼
JavaScript | TypeScript | ||
---|---|---|---|
|
|