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();
}