MST

星途 面试题库

面试题:TypeScript自定义类型保护在泛型和条件类型结合场景下的深度应用

定义一个泛型函数`filter<T, U extends T>(arr: T[], typeGuard: (arg: T) => arg is U): U[]`,要求利用自定义类型保护实现对数组元素的过滤。同时,编写一个场景,在该场景中使用这个函数,并且结合条件类型来处理不同类型元素的复杂逻辑,详细说明每一步实现的思路以及TypeScript类型系统在其中的作用机制。
23.9万 热度难度
前端开发TypeScript

知识考点

AI 面试

面试题答案

一键面试

1. 定义泛型函数 filter

function filter<T, U extends T>(arr: T[], typeGuard: (arg: T) => arg is U): U[] {
    return arr.filter(typeGuard);
}

思路

  • 该函数接收两个参数,一个是数组 arr,其类型为 T 数组,另一个是类型保护函数 typeGuard,它用于判断 T 类型的元素是否为 U 类型(UT 的子类型)。
  • 函数内部使用数组的 filter 方法,传入 typeGuard 函数,这样就可以过滤出满足 typeGuard 条件的元素,返回类型为 U 数组。

2. 编写使用场景及结合条件类型处理复杂逻辑

type Animal = { name: string };
type Dog = Animal & { bark: () => void };
type Cat = Animal & { meow: () => void };

function isDog(animal: Animal): animal is Dog {
    return (animal as Dog).bark!== undefined;
}

function isCat(animal: Animal): animal is Cat {
    return (animal as Cat).meow!== undefined;
}

function handleAnimal(animal: Animal) {
    if (isDog(animal)) {
        animal.bark();
    } else if (isCat(animal)) {
        animal.meow();
    } else {
        console.log(`${animal.name} is an unknown animal`);
    }
}

const animals: Animal[] = [
    { name: 'Buddy', bark: () => console.log('Woof') },
    { name: 'Whiskers', meow: () => console.log('Meow') },
    { name: 'Toby' }
];

const dogs = filter(animals, isDog);
const cats = filter(animals, isCat);

dogs.forEach(handleAnimal);
cats.forEach(handleAnimal);

思路

  • 首先定义了 Animal 类型,它有一个 name 属性。然后定义了 DogCat 类型,它们继承自 Animal 并分别有自己的方法 barkmeow
  • 接着定义了 isDogisCat 两个类型保护函数,用于判断 Animal 类型的元素是否为 DogCat 类型。
  • handleAnimal 函数展示了如何根据不同类型执行不同逻辑,利用 isDogisCat 类型保护函数进行判断。
  • 创建了一个 Animal 数组 animals,其中包含 DogCat 和普通 Animal 类型的元素。
  • 使用 filter 函数分别过滤出 DogCat 类型的元素,存储在 dogscats 数组中。
  • 最后对 dogscats 数组分别调用 handleAnimal 函数,根据元素类型执行不同操作。

TypeScript类型系统的作用机制

  • 泛型:在 filter 函数定义中,TU 泛型允许我们在函数定义时不指定具体类型,而是在使用时确定。这样提高了函数的复用性,使其可以处理各种类型的数组。
  • 类型保护isDogisCat 函数通过返回类型谓词(animal is Doganimal is Cat)告诉TypeScript编译器,在函数返回 true 时,传入的 animal 参数实际上是 DogCat 类型,从而在后续代码中可以安全地访问 DogCat 特有的属性和方法。
  • 条件类型:虽然在这个例子中没有显式使用条件类型的语法(如 T extends U? X : Y),但类型保护机制本质上是一种隐式的条件类型判断。在 handleAnimal 函数中,通过 isDogisCat 进行条件判断,TypeScript编译器会根据判断结果,智能地推断出 animal 的具体类型,从而确保类型安全。