課程大綱
- 建置 TypeScript 開發環境。
- 掌握 TypeScript 的基礎類型、聯集類型和交集類型。
- 詳細說明型別斷言的作用和用法。
- 掌握 TypeScript 中函式、類別的型別宣告方式。
- 掌握型別別名、介面的作用和定義。
- 掌握泛型(Generics)的應用場景,熟練應用泛型。
- 靈活運用條件類型、映射類型與內建類型。
- 建立和使用自訂類型。
- 理解命名空間(Namespace)、模組(Module)的概念及使用場景。
- 詳細說明 TS 中的型別保護(Type Guard)、裝飾器(Decorator)。
- 巧妙運用型別推導和簡化程式碼。
- 深入理解 TypeScript 型別層級系統。
- 詳細說明函式的變型(Variance)與逆變(Contravariance)。
- 深入研究 infer 的用法與技巧。
- 詳細探究符號類型。
- 靈活編寫與運用型別宣告檔案,擴展 TypeScript 的型別系統。
- 掌握 TS 中型別檔案查找規則。
- 深刻使用裝飾器,運用反射元資料(Reflection Metadata)擴展裝飾器的功能,實現控制反轉(Inversion of Control, IoC)、依賴注入(Dependency Injection, DI)。
- 深度解析 TSConfig 設定檔。
- TS 型別體操
ts基礎
目前大部分企業的中大型前端專案都採用了 Typescript,那麼為什麼我們需要它?
JavaScript 的核心特點就是靈活,但隨著專案規模的增大,靈活反而增加開發者的心智負擔。例如在程式碼中一個變數可以被賦予字串、布林值、數字,甚至是函式,這樣就充滿了不確定性。而且這些不確定性可能需要在程式碼執行的時候才能被發現,所以我們需要型別的約束。
當然不可否認的是有了型別的加持多少會影響開發效率,但是可以讓大型專案更加健壯。
- Typescript 更像後端 JAVA,讓 JS 可以開發大型企業應用;
- TS 提供的型別系統可以幫助我們在撰寫程式碼時提供豐富的語法提示;
- 在編寫程式碼時會對程式碼進行型別檢查,從而避免很多線上錯誤;
越來越多的專案開始擁抱 TS 了,典型的 Vue3、Pinia、第三方工具庫、後端 NodeJS 等。我們也經常為以上程式設計擁有更好的支援去編寫 .d.ts 檔案。
1. ts是一門語言
- ts 是 js 的一個超集,擴展了語法,添加了靜態型別支援以及其他一些新特性。
環境配置
全域安裝
npm install typescript -g
# 我們可以用 tsc 來把 ts 轉成 js
# 我們要把什麼轉換成什麼,所以我們要先生成一個設定
# 在目前專案下建立一個設定檔 tsc --init
# --watch
tsc --init
# 外掛程式 code runner 這個外掛程式需要額外安裝一個 ts-node 的套件
npm init -y
npm install typescript rollup -D
npm install rollup-plugin-typescript -D
npm i @rollup/plugin-node-resolve rollup-plugin-serve -D
# rollup.config.js
# error lens 外掛程式
學習ts就是學習型別,ts的型別分類(DOM,Promise,原始方法)基礎類型,
ts 中:後面的都是型別 = 後面的都是值
ts 一切從安全的角度出發,看能不能賦值,就看安全不安全
ts 還有自動的型別推導,不用見到變數就寫型別,而是推斷的不正確,我們才需要自己編寫。
基礎類型
let abc: string = 'Hello World';
console.log(abc);
let name = 'John Doe'; // 目前模組中,作用域隔離
let age = 20;
let handsome: boolean = true;
// 原始類似識別碼都是小寫的,而類別名稱都是大寫的,它描述的實例(都是物件)
// 型別註解:告訴 TS 變數的型別
// 型別推斷:TS 會自動嘗試分析變數的型別
// 型別註解優先級高於型別推斷
// 型別註解:變數名: 型別 = 值
// 型別推斷:變數名 = 值
let s1: string = 'Hello World';
let s2: String = new String('Hello World');
let s3: String = 'Hello World'; // 會自動轉換為物件
// 陣列宣告
let arr1: number[] = [1, 2, 3];
let arr2: Array<number> = [1, 2, 3];
// 元組(Tuple):固定長度的陣列
let tuple: [string, number] = ['Hello', 123];
// 元組的越界問題
// tuple[2] = 'World'; // 會報錯
let tuple2: readonly [string, number, boolean] = ['Hello', 123, true];
// tuple2[0] = 'World'; // 會報錯
export { name };
列舉(自帶型別的物件)
let abc: string = 'Hello World';
console.log(abc);
let name = 'John Doe'; // 目前模組中,作用域隔離
let age = 20;
let handsome: boolean = true;
// 原始類似識別碼都是小寫的,而類別名稱都是大寫的,它描述的實例(都是物件)
// 型別註解:告訴 TS 變數的型別
// 型別推斷:TS 會自動嘗試分析變數的型別
// 型別註解優先級高於型別推斷
// 型別註解:變數名: 型別 = 值
// 型別推斷:變數名 = 值
let s1: string = 'Hello World';
let s2: String = new String('Hello World');
let s3: String = 'Hello World'; // 會自動轉換為物件
// 陣列宣告
let arr1: number[] = [1, 2, 3];
let arr2: Array<number> = [1, 2, 3];
// 元組:固定長度的陣列
let tuple: [string, number] = ['Hello', 123];
// 元組的越界問題
// tuple[2] = 'World'; // 會報錯
let tuple2: readonly [string, number, boolean] = ['Hello', 123, true];
// tuple2[0] = 'World'; // 會報錯
// 列舉(Enum) 自帶型別的物件,自動增長
enum USER_ROLE {
USER,
ADMIN = 6,
MANAGER,
OTHER = 'other', // 異構列舉
}
/*
USER = 0,
ADMIN = 6,
MANAGER = 7,
*/
console.log(USER_ROLE.USER); // 0
// 如果不需要物件可以直接採用常數列舉
const enum USER_ROLE2 {
USER,
ADMIN,
MANAGER,
}
console.log(USER_ROLE2.USER); // 0
export { name };
null 和 undefined
// 嚴格模式下,不允許使用 any 型別
// null 和 undefined 是任何型別的子型別
let x: number | null | undefined = 1;
// let y: number = undefined; // 會報錯
// let z: number = null; // 會報錯
// strict:false 可以使用 any 型別
never類型
永遠不
// never 型別 它是任何型別的子型別,也可以賦值給任何型別
// never 型別的變數只能被賦值為 never 型別
function fn1(): never {
throw new Error('報錯了');
}
// 型別保護(Type Guard),保障程式的不缺失
// never 是永遠到達不了的終點
function fn2(): never {
while (true) {}
}
function validate(x: never) {
console.log(x);
}
// 針對不同的型別做不同的處理
function fn3(x: number | string | boolean) {
// 類似有收斂的作用
if (typeof x === 'number') {
console.log(x.toFixed(2));
return;
}
if (typeof x === 'string') {
console.log(x.trim());
return;
}
if (typeof x === 'boolean') {
console.log(x.valueOf());
return;
}
//守衛語句
validate(x); // 如果型別不匹配,會報錯 這個邏輯應該是永遠不會執行的,所以上面還是有沒覆蓋到的邏輯(要添加 boolean 型別的判斷)
}
object的類型
// object 型別 object {} Object
let obj: object = { name: 'John Doe' };
// obj.name = 'Jane Doe'; // 會報錯
// 小寫的 object 是物件型別,大寫的 Object 是物件的建構函式
! 號非空斷言
聯集類型
// 一般我們會基於額外的型別來擴展定義型別,有點類似列舉
type Direction = "up" | "down" | "right" | "left"
let direction :Direction = "left"
// type 和 interface 的區別
type women =
| {
wealthy: true;
waste: string;
}
| {
wealthy: false;
norality: string;
};
// 是富人一定不是節儉的人,是節儉的人一定不是富人
可以利用聯集類型來做到屬性之間的互斥
斷言
斷言有可能出問題,出問題後果自負
type women =
| {
wealthy: true;
waste: string;
}
| {
wealthy: false;
norality: string;
};
// 是富人一定不是節儉的人,是節儉的人一定不是富人(類比約定,不嚴謹)
// 斷言 把某個型別斷言為已經存在的一種型別
let ele = document.getElementById('app');
ele!.innerHTML = 'Hello World'; // 繞過 TS 的型別檢查 ele
主題測試文章,只做測試使用。發佈者:Walker,轉轉請注明出處:https://walker-learn.xyz/archives/4409