MST

星途 面试题库

面试题:复杂TypeScript函数类型注解下运行时行为的深度剖析

在TypeScript中,使用泛型定义一个高阶函数`compose`,它接受多个函数作为参数,并返回一个新的函数,新函数能够依次调用传入的函数。要求详细说明`compose`函数类型注解的设计思路,以及在运行时如何确保类型安全,尤其是在处理不同类型函数组合时,如何避免运行时类型错误,并结合类型推断机制解释其背后原理。
27.5万 热度难度
前端开发TypeScript

知识考点

AI 面试

面试题答案

一键面试
  1. compose函数的实现及类型注解设计思路
    • 首先,compose函数接受多个函数作为参数,最后返回一个新的函数。假设传入的函数依次为f1, f2, ..., fn,新函数执行时会按照fn(f(n - 1)(...f1(x)))的顺序调用。
    • 类型注解设计上,我们需要考虑这些函数的输入和输出类型关系。设compose接受的函数类型为Function1, Function2, ..., FunctionN
    • 对于compose函数的类型定义,我们使用泛型来表示不同函数的输入和输出类型。以下是具体实现:
function compose<
  F extends ((...args: any[]) => any)[],
  T extends F extends ((arg: infer A) => any)[] ? A : never
>(...funcs: F): (...args: T extends any ? [T] : []) => ReturnType<F[F['length'] - 1]> {
  return function (this: any, ...args) {
    return funcs.reduceRight((acc, func) => func.call(this, acc), args[0]);
  };
}
  • 泛型解释
    • F是一个泛型,表示传入compose的函数数组类型,约束为((...args: any[]) => any)[],即数组中的每个元素都是一个函数。
    • T是另一个泛型,它通过条件类型推断出第一个函数的输入类型。如果F是一个函数数组,并且每个函数都接受一个参数(这里使用infer A推断出参数类型A),那么T就是这个A类型;否则为never
    • 返回的函数类型为(...args: T extends any ? [T] : []) => ReturnType<F[F['length'] - 1]>,表示接受T类型的参数(如果T存在),返回值类型是最后一个函数的返回类型(通过ReturnType<F[F['length'] - 1]>获取)。
  1. 运行时确保类型安全
    • 在运行时,TypeScript通过类型检查机制确保类型安全。当我们使用compose函数组合不同函数时,TypeScript编译器会检查每个函数的输入和输出类型是否匹配。
    • 例如,如果我们有函数function add(a: number, b: number): number { return a + b; }function multiply(a: number, b: number): number { return a * b; }。当我们尝试compose(add, multiply)(2, 3)时,TypeScript会检查add函数的返回类型(number)是否与multiply函数的第一个参数类型(number)匹配。如果不匹配,编译器会报错,从而在编译阶段就避免了运行时类型错误。
  2. 结合类型推断机制解释原理
    • TypeScript的类型推断机制在compose函数中起到关键作用。在上述代码中,T泛型通过条件类型推断出第一个函数的输入类型,这样就可以自动推断出组合函数的输入类型。
    • 同时,ReturnType<F[F['length'] - 1]>通过类型推断获取最后一个函数的返回类型,作为组合函数的返回类型。这种类型推断机制使得我们在使用compose函数时,无需显式地指定每个函数的类型,编译器能够根据上下文自动推导,从而保证类型安全,减少手动类型声明的工作量,提高代码的可读性和可维护性。