2023-07-1811分钟小遇

TypeScript高级类型系统 - 提升代码质量的秘密武器

TypeScriptJavaScript类型系统

本文探讨TypeScript的高级类型功能,包括条件类型、映射类型、类型守卫等,帮助你编写更安全、更可维护的代码。

TypeScript 高级类型系统简介

TypeScript 作为 JavaScript 的超集,提供了丰富的类型系统,极大地增强了代码的可靠性和可维护性。本文将深入探讨 TypeScript 的高级类型特性,帮助你充分利用这些"秘密武器"来提升代码质量。

条件类型 (Conditional Types)

条件类型允许我们根据类型关系创建动态类型,就像是类型系统中的 if 语句:

type IsString<T> = T extends string ? true : false;

// 使用示例
type Result1 = IsString<string>;  // true
type Result2 = IsString<number>;  // false

条件类型的强大之处在于它可以与泛型结合使用,实现类型转换和过滤:

// 从类型T中排除可分配给类型U的类型
type Exclude<T, U> = T extends U ? never : T;

// 使用示例
type T0 = Exclude<'a' | 'b' | 'c', 'a'>;  // 'b' | 'c'
type T1 = Exclude<string | number | (() => void), Function>;  // string | number

映射类型 (Mapped Types)

映射类型允许我们基于旧类型创建新类型,通过遍历现有类型的属性来转换它们:

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

// 使用示例
interface Person {
    name: string;
    age: number;
}

const readonlyPerson: Readonly<Person> = {
    name: "张三",
    age: 30
};

// 以下操作将导致类型错误
// readonlyPerson.name = "李四";

TypeScript 内置了几个常用的映射类型:Partial<T>, Required<T>, Readonly<T>, Record<K,T> 等。

类型守卫 (Type Guards)

类型守卫让我们可以在运行时检查类型,并在特定的代码块中缩小类型范围:

// 使用 typeof 类型守卫
function padLeft(value: string, padding: string | number) {
    if (typeof padding === "number") {
        // 此处 padding 的类型被缩小为 number
        return Array(padding + 1).join(" ") + value;
    }
    if (typeof padding === "string") {
        // 此处 padding 的类型被缩小为 string
        return padding + value;
    }
    throw new Error("Expected string or number, got '" + padding + "'.");
}

// 使用 instanceof 类型守卫
class Bird {
    fly() {
        console.log("鸟儿飞行");
    }
    layEggs() {
        console.log("鸟儿下蛋");
    }
}

class Fish {
    swim() {
        console.log("鱼儿游泳");
    }
    layEggs() {
        console.log("鱼儿产卵");
    }
}

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

const pet = getRandomPet();

if (pet instanceof Bird) {
    // 此处 pet 的类型被缩小为 Bird
    pet.fly();
}
if (pet instanceof Fish) {
    // 此处 pet 的类型被缩小为 Fish
    pet.swim();
}

自定义类型守卫

除了使用 typeof 和 instanceof,我们还可以创建自定义的类型守卫函数:

interface Bird {
    fly(): void;
    layEggs(): void;
}

interface Fish {
    swim(): void;
    layEggs(): void;
}

// 自定义类型守卫
function isFish(pet: Fish | Bird): pet is Fish {
    return (pet as Fish).swim !== undefined;
}

// 使用自定义类型守卫
function move(pet: Fish | Bird) {
    if (isFish(pet)) {
        // TypeScript 知道这里 pet 是 Fish 类型
        pet.swim();
    } else {
        // TypeScript 知道这里 pet 是 Bird 类型
        pet.fly();
    }
}

索引类型 (Index Types)

索引类型允许我们动态查询和访问对象的属性:

function pluck<T, K extends keyof T>(o: T, propertyNames: K[]): T[K][] {
    return propertyNames.map(n => o[n]);
}

interface Car {
    manufacturer: string;
    model: string;
    year: number;
}

const taxi: Car = {
    manufacturer: "丰田",
    model: "卡罗拉",
    year: 2020
};

// 结果类型是 (string | number)[]
const nameAndYear = pluck(taxi, ['manufacturer', 'year']);

实用工具类型

TypeScript 提供了许多内置的工具类型,它们利用以上高级类型功能实现:

// Partial<T> - 将T中的所有属性变为可选
interface User {
    id: number;
    name: string;
    email: string;
}

// 所有字段都变为可选
function updateUser(user: User, fieldsToUpdate: Partial<User>) {
    return { ...user, ...fieldsToUpdate };
}

// Pick<T, K> - 从T中选择特定属性K
type UserBasicInfo = Pick<User, 'id' | 'name'>;

// Omit<T, K> - 从T中排除特定属性K
type UserWithoutEmail = Omit<User, 'email'>;

// ReturnType<T> - 获取函数返回值的类型
function createUser() {
    return { id: 1, name: "小遇", email: "xiaoyu@example.com" };
}

type CreatedUser = ReturnType<typeof createUser>;  // User 类型

总结

TypeScript 的高级类型系统是提升代码质量的强大工具,通过掌握条件类型、映射类型、类型守卫和索引类型,我们可以:

  1. 创建更精确的类型定义
  2. 提高代码的可读性和可维护性
  3. 在编译时捕获潜在错误
  4. 增强IDE的代码补全和提示功能

在实际项目中灵活运用这些类型特性,将帮助我们构建更加健壮和可靠的应用程序。

我是一名大二学生,欢迎和我交流这些前端技术心得,一起学习进步!

小遇

小遇

前端开发工程师,热爱分享与学习。专注于React、Next.js等前端技术栈。

你可能也感兴趣