TypeScript

TypeScript 進階開發技巧

深入TypeScript進階特性,包含泛型、裝飾器、高階類型、模組系統和實用工具類型等核心概念。

林工程師
22分鐘閱讀
#TypeScript#泛型#裝飾器#高階類型

# TypeScript 進階開發技巧

TypeScript作為JavaScript的超集,提供了強大的型別系統和進階功能。本文將深入探討TypeScript的進階特性。

## 1. 泛型(Generics)

泛型是TypeScript中最強大的特性之一,允許你創建可重用的組件。

### 基本泛型

typescript
// 基本泛型函數
function identity(arg: T): T {
return arg;
}

// 使用
const output1 = identity("hello");
const output2 = identity("hello"); // 型別推斷

// 泛型介面
interface GenericIdentityFn {
(arg: T): T;
}

const myIdentity: GenericIdentityFn = identity;


### 泛型約束

typescript
// 使用extends約束泛型
interface Lengthwise {
length: number;
}

function loggingIdentity(arg: T): T {
console.log(arg.length);
return arg;
}

// 正確使用
loggingIdentity("hello"); // 字串有length屬性
loggingIdentity([1, 2, 3]); // 陣列有length屬性

// 錯誤使用
// loggingIdentity(123); // 數字沒有length屬性

// 多個約束
function combine(obj1: T, obj2: U): T & U {
return { ...obj1, ...obj2 };
}

const result = combine({ name: "John" }, { age: 30 });
// result: { name: string; age: number }


### 泛型類別

typescript
class GenericNumber {
zeroValue: T;
add: (x: T, y: T) => T;
}

const myGenericNumber = new GenericNumber();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = (x, y) => x + y;

// 泛型堆疊實現
class Stack {
private items: T[] = [];

push(item: T): void {
this.items.push(item);
}

pop(): T | undefined {
return this.items.pop();
}

peek(): T | undefined {
return this.items[this.items.length - 1];
}

isEmpty(): boolean {
return this.items.length === 0;
}

size(): number {
return this.items.length;
}
}

// 使用
const numberStack = new Stack();
numberStack.push(1);
numberStack.push(2);
console.log(numberStack.pop()); // 2

const stringStack = new Stack();
stringStack.push("hello");
stringStack.push("world");


## 2. 高階類型(Advanced Types)

### 聯合類型與交叉類型

typescript
// 聯合類型
type StringOrNumber = string | number;

function processValue(value: StringOrNumber): string {
if (typeof value === "string") {
return value.toUpperCase();
} else {
return value.toString();
}
}

// 交叉類型
interface Person {
name: string;
age: number;
}

interface Employee {
id: number;
department: string;
}

type EmployeePerson = Person & Employee;

const employee: EmployeePerson = {
name: "John",
age: 30,
id: 123,
department: "Engineering"
};


### 條件類型

typescript
// 基本條件類型
type NonNullable = T extends null | undefined ? never : T;

type T0 = NonNullable; // string | number
type T1 = NonNullable; // string[]

// 分發條件類型
type ToArray = T extends any ? T[] : never;

type StrNumArr = ToArray; // string[] | number[]

// 過濾聯合類型
type Filter = T extends U ? T : never;

type T2 = Filter; // string | number


### 映射類型

typescript
interface Person {
name: string;
age: number;
address: string;
}

// 將所有屬性變為可選
type Partial = {
[P in keyof T]?: T[P];
};

type PartialPerson = Partial;
// { name?: string; age?: number; address?: string; }

// 將所有屬性變為必需
type Required = {
[P in keyof T]-?: T[P];
};

// 將所有屬性變為唯讀
type Readonly = {
readonly [P in keyof T]: T[P];
};

// 選擇特定屬性
type Pick = {
[P in K]: T[P];
};

type PersonNameAge = Pick;
// { name: string; age: number; }

// 排除特定屬性
type Omit = Pick>;

type PersonWithoutAge = Omit;
// { name: string; address: string; }


## 3. 裝飾器(Decorators)

裝飾器是一種特殊的宣告,可以附加到類別、方法、屬性或參數上。

### 類別裝飾器

typescript
// 基本類別裝飾器
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}

@sealed
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}

// 工廠裝飾器
function color(value: string) {
return function (target: Function) {
// 保存對構造函數的引用
const original = target;

// 創建新的構造函數
const f: any = function (...args: any[]) {
console.log("Creating new instance with color: " + value);
return new original(...args);
};

// 複製原型
f.prototype = original.prototype;

return f;
};
}

@color("red")
class Car {
constructor(public brand: string) {}
}

const car = new Car("Toyota"); // 輸出: Creating new instance with color: red


### 方法裝飾器

typescript
// 方法裝飾器
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const method = descriptor.value;

descriptor.value = function (...args: any[]) {
console.log(`Calling ${propertyKey} with args: ${JSON.stringify(args)}`);
const result = method.apply(this, args);
console.log(`${propertyKey} returned: ${JSON.stringify(result)}`);
return result;
};

return descriptor;
}

class Calculator {
@log
add(a: number, b: number): number {
return a + b;
}

@log
multiply(a: number, b: number): number {
return a * b;
}
}

const calc = new Calculator();
calc.add(2, 3); // 輸出調用和返回日誌


### 屬性裝飾器

typescript
// 屬性裝飾器
function format(formatString: string) {
return function (target: any, propertyKey: string) {
let value = target[propertyKey];

const getter = function () {
return value;
};

const setter = function (newVal: string) {
value = newVal.replace(/\s+/g, ' ').trim();
};

Object.defineProperty(target, propertyKey, {
get: getter,
set: setter,
enumerable: true,
configurable: true
});
};
}

class User {
@format("trim")
name: string;

constructor(name: string) {
this.name = name;
}
}

const user = new User(" John Doe ");
console.log(user.name); // "John Doe"


## 4. 實用工具類型

### 內建工具類型

typescript
interface User {
id: number;
name: string;
email: string;
age?: number;
readonly createdAt: Date;
}

// Partial - 所有屬性變為可選
type PartialUser = Partial;
// { id?: number; name?: string; email?: string; age?: number; createdAt?: Date; }

// Required - 所有屬性變為必需
type RequiredUser = Required;
// { id: number; name: string; email: string; age: number; readonly createdAt: Date; }

// Readonly - 所有屬性變為唯讀
type ReadonlyUser = Readonly;
// { readonly id: number; readonly name: string; readonly email: string; readonly age?: number; readonly createdAt: Date; }

// Pick - 選擇特定屬性
type UserBasicInfo = Pick;
// { id: number; name: string; email: string; }

// Omit - 排除特定屬性
type UserWithoutId = Omit;
// { name: string; email: string; age?: number; readonly createdAt: Date; }

// Record - 創建鍵值對類型
type UserRoles = Record;
// { [key: string]: string; }

// ReturnType - 獲取函數返回類型
function getUser(): User {
return { id: 1, name: "John", email: "john@example.com", createdAt: new Date() };
}
type UserReturnType = ReturnType; // User

// Parameters - 獲取函數參數類型
function createUser(id: number, name: string, email: string): User {
return { id, name, email, createdAt: new Date() };
}
type CreateUserParams = Parameters; // [number, string, string]


### 自定義工具類型

typescript
// 深度Partial
type DeepPartial = {
[P in keyof T]?: T[P] extends object ? DeepPartial : T[P];
};

interface NestedObject {
user: {
profile: {
name: string;
age: number;
};
settings: {
theme: string;
};
};
}

type DeepPartialNested = DeepPartial;
// user?: { profile?: { name?: string; age?: number; }; settings?: { theme?: string; }; }

// 可變參數函數類型
type VariadicFunction = (...args: T) => R;

// 非空類型
type NonNullable = T extends null | undefined ? never : T;

// 字面量聯合類型
type StringLiteralUnion = T | Omit;

// 條件類型工具
type If = C extends true ? T : F;
type IsString = T extends string ? true : false;


## 5. 模組系統

### 模組導入導出

typescript
// 基本導出
export interface User {
id: number;
name: string;
email: string;
}

export class UserService {
private users: User[] = [];

addUser(user: User): void {
this.users.push(user);
}

getUser(id: number): User | undefined {
return this.users.find(u => u.id === id);
}
}

// 默認導出
export default class Database {
connect(): Promise {
return Promise.resolve();
}
}

// 重新導出
export { UserService as UserManager } from './user-service';
export * from './types';

// 動態導入
async function loadModule() {
const module = await import('./user-service');
const userService = new module.UserService();
return userService;
}


### 命名空間

typescript
// 命名空間
namespace Validation {
export interface StringValidator {
isValid(s: string): boolean;
}

export class EmailValidator implements StringValidator {
isValid(s: string): boolean {
return s.indexOf('@') !== -1;
}
}

export class ZipCodeValidator implements StringValidator {
isValid(s: string): boolean {
return s.length === 5 && !isNaN(Number(s));
}
}
}

// 使用命名空間
let emailValidator = new Validation.EmailValidator();
let zipCodeValidator = new Validation.ZipCodeValidator();

// 多檔案命名空間
// validation.ts
export namespace Validation {
export interface StringValidator {
isValid(s: string): boolean;
}
}

// email-validator.ts
import { Validation } from './validation';

export namespace Validation {
export class EmailValidator implements StringValidator {
isValid(s: string): boolean {
return s.indexOf('@') !== -1;
}
}
}


## 6. 進階模式

### 單例模式

typescript
class Singleton {
private static instance: Singleton;
private constructor() {}

static getInstance(): Singleton {
if (!Singleton.instance) {
Singleton.instance = new Singleton();
}
return Singleton.instance;
}

doSomething(): void {
console.log("Singleton is doing something");
}
}

// 使用
const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();
console.log(instance1 === instance2); // true


### 工廠模式

typescript
interface Product {
operation(): string;
}

class ConcreteProductA implements Product {
operation(): string {
return "ConcreteProductA";
}
}

class ConcreteProductB implements Product {
operation(): string {
return "ConcreteProductB";
}
}

abstract class Creator {
abstract factoryMethod(): Product;

someOperation(): string {
const product = this.factoryMethod();
return product.operation();
}
}

class ConcreteCreatorA extends Creator {
factoryMethod(): Product {
return new ConcreteProductA();
}
}

class ConcreteCreatorB extends Creator {
factoryMethod(): Product {
return new ConcreteProductB();
}
}

// 使用
const creatorA = new ConcreteCreatorA();
const creatorB = new ConcreteCreatorB();

console.log(creatorA.someOperation()); // "ConcreteProductA"
console.log(creatorB.someOperation()); // "ConcreteProductB"


### 觀察者模式

typescript
interface Observer {
update(subject: Subject): void;
}

interface Subject {
attach(observer: Observer): void;
detach(observer: Observer): void;
notify(): void;
}

class ConcreteSubject implements Subject {
private observers: Observer[] = [];
private state: string = '';

attach(observer: Observer): void {
const isExist = this.observers.includes(observer);
if (isExist) {
return console.log('Subject: Observer has been attached already.');
}

console.log('Subject: Attached an observer.');
this.observers.push(observer);
}

detach(observer: Observer): void {
const observerIndex = this.observers.indexOf(observer);
if (observerIndex === -1) {
return console.log('Subject: Nonexistent observer.');
}

this.observers.splice(observerIndex, 1);
console.log('Subject: Detached an observer.');
}

notify(): void {
console.log('Subject: Notifying observers...');
for (const observer of this.observers) {
observer.update(this);
}
}

someBusinessLogic(): void {
console.log('Subject: I'm doing something important.');
this.state = Math.random().toString(36).substr(2, 9);
console.log(`Subject: My state has just changed to: ${this.state}`);
this.notify();
}

getState(): string {
return this.state;
}
}

class ConcreteObserverA implements Observer {
update(subject: Subject): void {
if (subject instanceof ConcreteSubject && subject.getState() < "3") {
console.log('ConcreteObserverA: Reacted to the event.');
}
}
}

class ConcreteObserverB implements Observer {
update(subject: Subject): void {
if (subject instanceof ConcreteSubject && (subject.getState() === "0" || subject.getState() >= "2")) {
console.log('ConcreteObserverB: Reacted to the event.');
}
}
}

// 使用
const subject = new ConcreteSubject();
const observer1 = new ConcreteObserverA();
const observer2 = new ConcreteObserverB();

subject.attach(observer1);
subject.attach(observer2);

subject.someBusinessLogic();


## 總結

TypeScript的進階特性提供了:

1. **泛型**:類型安全的可重用組件
2. **高階類型**:複雜的類型操作和轉換
3. **裝飾器**:元程式設計和AOP
4. **工具類型**:內建和自定義的類型工具
5. **模組系統**:組織和重用程式碼
6. **設計模式**:面向對象的最佳實踐

掌握這些進階特性,將大大提升TypeScript開發的效率和程式碼品質。
TypeScript 進階開發技巧 | 香港大專CS功課代做