跳到主要内容

TypeScript 高级特性

TypeScript 提供了许多高级类型和特性,掌握这些可以帮助我们写出更加健壮和类型安全的代码。

高级类型

1. 交叉类型(Intersection Types)

使用 & 运算符将多个类型合并为一个类型:

interface BusinessPartner {
name: string;
credit: number;
}

interface Identity {
id: number;
email: string;
}

type Employee = BusinessPartner & Identity;

// 使用示例
let emp: Employee = {
name: "John",
credit: 100,
id: 1234,
email: "john@example.com"
};

2. 联合类型(Union Types)

使用 | 运算符表示一个值可以是几种类型之一:

type StringOrNumber = string | number;
type Status = "success" | "error" | "pending";

function printId(id: number | string) {
if (typeof id === "string") {
console.log(id.toUpperCase());
} else {
console.log(id);
}
}

3. 类型守卫(Type Guards)

帮助TypeScript推断类型的表达式:

// typeof 类型守卫
function padLeft(value: string, padding: string | number) {
if (typeof padding === "number") {
return Array(padding + 1).join(" ") + value;
}
if (typeof padding === "string") {
return padding + value;
}
throw new Error("Expected string or number");
}

// instanceof 类型守卫
class Bird {
fly() {
console.log("flying...");
}
}

class Fish {
swim() {
console.log("swimming...");
}
}

function move(pet: Bird | Fish) {
if (pet instanceof Bird) {
pet.fly();
} else {
pet.swim();
}
}

4. 映射类型(Mapped Types)

从现有类型创建新类型:

// Readonly
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};

// Partial
type Partial<T> = {
[P in keyof T]?: T[P];
};

// Pick
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};

// Record
type Record<K extends keyof any, T> = {
[P in K]: T;
};

5. 条件类型(Conditional Types)

根据条件选择类型:

type NonNullable<T> = T extends null | undefined ? never : T;

// 示例
type EmailAddress = string | null | undefined;
type NonNullableEmail = NonNullable<EmailAddress>; // string

// 条件类型与泛型结合
type ArrayType<T> = T extends any[] ? T[number] : T;
type StringArray = ArrayType<string[]>; // string
type NumberValue = ArrayType<number>; // number

高级特性

1. 装饰器(Decorators)

用于修改类的行为:

function logged(constructor: Function) {
console.log(`Creating new instance of ${constructor.name}`);
}

@logged
class Person {
constructor(public name: string) {}
}

// 方法装饰器
function measure(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
const start = performance.now();
const result = originalMethod.apply(this, args);
const finish = performance.now();
console.log(`${propertyKey} took ${finish - start}ms`);
return result;
};
}

2. 高级类型推断

// ReturnType
function f() {
return { x: 10, y: 3 };
}
type P = ReturnType<typeof f>; // { x: number, y: number }

// Parameters
type T0 = Parameters<() => string>; // []
type T1 = Parameters<(s: string) => void>; // [string]

// InstanceType
class C {
x = 0;
y = 0;
}
type T2 = InstanceType<typeof C>; // C

3. 模块增强(Module Augmentation)

扩展现有模块的类型定义:

// 原始模块
declare module "myModule" {
export interface BaseOptions {
timeout: number;
}
}

// 增强模块
declare module "myModule" {
interface BaseOptions {
retries?: number;
}
}

最佳实践

1. 类型安全

// 使用严格的类型检查
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true
}
}

// 避免使用 any
function processData<T>(data: T): T {
// 处理数据
return data;
}

2. 类型断言最佳实践

// 使用 as const 断言
const config = {
endpoint: "api.example.com",
port: 443
} as const;

// 双重断言(谨慎使用)
let someValue: unknown = "this is a string";
let strLength: number = (someValue as string).length;

3. 泛型约束最佳实践

interface HasLength {
length: number;
}

// 泛型约束确保参数有length属性
function logLength<T extends HasLength>(arg: T): number {
return arg.length;
}

// 可以传入字符串或数组
logLength("Hello"); // OK
logLength([1, 2, 3]); // OK
logLength(123); // Error: number doesn't have length

高级工具类型

// Omit - 从类型中排除某些属性
type User = {
id: number;
name: string;
email: string;
password: string;
};

type PublicUser = Omit<User, "password">;

// Extract - 提取符合条件的类型
type T0 = Extract<"a" | "b" | "c", "a" | "f">; // "a"

// Exclude - 排除符合条件的类型
type T1 = Exclude<"a" | "b" | "c", "a" | "b">; // "c"

// Required - 使所有属性必需
type Props = {
a?: number;
b?: string;
};

type RequiredProps = Required<Props>;

1. 装饰器(Decorators)

装饰器是一种特殊类型的声明,它能够被附加到类声明、方法、访问符、属性或参数上。装饰器使用 @expression 形式,expression 求值后必须为一个函数。

1.1 类装饰器

// 类装饰器
function classDecorator<T extends { new (...args: any[]): {} }>(constructor: T) {
return class extends constructor {
newProperty = "new property";
hello = "override";
}
}

@classDecorator
class Greeter {
property = "property";
hello: string;
constructor(m: string) {
this.hello = m;
}
}

console.log(new Greeter("world"));

1.2 方法装饰器

// 方法装饰器
function methodDecorator(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
// 保存原始方法
const originalMethod = descriptor.value;

// 修改方法行为
descriptor.value = function(...args: any[]) {
console.log(`Calling ${propertyKey} with args:`, args);
const result = originalMethod.apply(this, args);
console.log(`Method ${propertyKey} returned:`, result);
return result;
};
}

class Example {
@methodDecorator
greet(name: string) {
return `Hello, ${name}!`;
}
}

1.3 属性装饰器

// 属性装饰器
function propertyDecorator(target: any, propertyKey: string) {
// 属性getter
let value = target[propertyKey];

// 属性getter/setter
Object.defineProperty(target, propertyKey, {
get: () => value,
set: (newValue: any) => {
console.log(`Setting ${propertyKey} to:`, newValue);
value = newValue;
},
});
}

class User {
@propertyDecorator
name: string = "default";
}

1.4 参数装饰器

// 参数装饰器
function parameterDecorator(target: any, propertyKey: string, parameterIndex: number) {
console.log(`Parameter decorator: ${propertyKey}, index: ${parameterIndex}`);
}

class Service {
login(@parameterDecorator username: string, @parameterDecorator password: string) {
// 方法实现
}
}

1.5 装饰器工厂

// 装饰器工厂
function log(prefix: string) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;

descriptor.value = function(...args: any[]) {
console.log(`${prefix} ${propertyKey}`);
return originalMethod.apply(this, args);
};
};
}

class Calculator {
@log("Calling method:")
add(x: number, y: number) {
return x + y;
}
}

2. 模块和命名空间

2.1 模块

TypeScript 中的模块可以帮助我们更好地组织代码,实现代码复用和封装。

// math.ts
export function add(x: number, y: number): number {
return x + y;
}

export function subtract(x: number, y: number): number {
return x - y;
}

// 默认导出
export default class Calculator {
add(x: number, y: number): number {
return x + y;
}
}

// main.ts
import Calculator, { add, subtract } from './math';

const calc = new Calculator();
console.log(calc.add(1, 2)); // 3
console.log(add(3, 4)); // 7
console.log(subtract(10, 5)); // 5

2.2 命名空间

命名空间可以用来避免命名冲突,将相关的代码组织在一起。

// 定义命名空间
namespace Validation {
export interface StringValidator {
isValid(s: string): boolean;
}

export class LettersOnlyValidator implements StringValidator {
isValid(s: string): boolean {
return /^[A-Za-z]+$/.test(s);
}
}

export class ZipCodeValidator implements StringValidator {
isValid(s: string): boolean {
return /^\d{5}(-\d{4})?$/.test(s);
}
}
}

// 使用命名空间
let validators: { [s: string]: Validation.StringValidator } = {};
validators["Letters"] = new Validation.LettersOnlyValidator();
validators["ZIP"] = new Validation.ZipCodeValidator();

3. 高级类型技巧

3.1 类型保护

// 类型谓词
function isString(value: any): value is string {
return typeof value === "string";
}

// instanceof 类型保护
class Bird {
fly() {}
layEggs() {}
}

class Fish {
swim() {}
layEggs() {}
}

function getSmallPet(): Fish | Bird {
return Math.random() > 0.5 ? new Bird() : new Fish();
}

let pet = getSmallPet();
if (pet instanceof Bird) {
pet.fly();
}

3.2 可辨识联合类型

interface Square {
kind: "square";
size: number;
}

interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}

interface Circle {
kind: "circle";
radius: number;
}

type Shape = Square | Rectangle | Circle;

function area(s: Shape): number {
switch (s.kind) {
case "square": return s.size * s.size;
case "rectangle": return s.width * s.height;
case "circle": return Math.PI * s.radius ** 2;
}
}

3.3 映射类型进阶

// 条件类型中的映射
type FunctionPropertyNames<T> = {
[K in keyof T]: T[K] extends Function ? K : never
}[keyof T];

interface User {
name: string;
age: number;
updateProfile: () => void;
}

type FunctionProps = FunctionPropertyNames<User>; // "updateProfile"

// 映射类型修饰符
type Mutable<T> = {
-readonly [P in keyof T]: T[P]
};

type Required<T> = {
[P in keyof T]-?: T[P]
};

4. 声明合并

TypeScript中独特的概念,它可以将多个同名声明合并为一个定义。

// 接口合并
interface Box {
height: number;
width: number;
}

interface Box {
scale: number;
}

let box: Box = {height: 5, width: 6, scale: 10};

// 命名空间和类合并
class Album {
label: Album.AlbumLabel;
}

namespace Album {
export interface AlbumLabel {
name: string;
}
}

5. 实用工具类型

// 内置工具类型的高级用法
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P]
};

type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]
};

// 条件类型工具
type Unpacked<T> = T extends (infer U)[]
? U
: T extends (...args: any[]) => infer U
? U
: T extends Promise<infer U>
? U
: T;

type T0 = Unpacked<string>; // string
type T1 = Unpacked<string[]>; // string
type T2 = Unpacked<() => string>; // string
type T3 = Unpacked<Promise<string>>; // string

6. 最佳实践

6.1 类型安全

// 严格的空值检查
function processValue(value: string | null | undefined) {
if (value != null) { // 同时检查null和undefined
console.log(value.toUpperCase());
}
}

// 使用类型断言时的安全检查
function assertIsString(value: any): asserts value is string {
if (typeof value !== "string") {
throw new Error("Value must be a string");
}
}

6.2 性能优化

// 避免过度使用泛型
// 不好的做法
function identity<T>(value: T): T {
return value;
}

// 更好的做法
function identity(value: any): any {
return value;
}

// 合理使用类型推断
// 不必要的类型注解
const numbers: number[] = [1, 2, 3];

// 让TypeScript自动推断
const numbers = [1, 2, 3];

6.3 代码组织

// 使用barrel文件组织导出
// models/index.ts
export * from './user.model';
export * from './product.model';
export * from './order.model';

// 使用命名空间组织相关功能
namespace App {
export namespace Utils {
export function format(value: string): string {
return value.trim();
}
}

export namespace Validation {
export function validate(value: string): boolean {
return value.length > 0;
}
}
}