Vue 3中使用的函数重载有啥用?

你知道上图中为什么定义了那么多个 ref 函数,它们的作用是什么?如果不清楚的话,阅读完本文的内容,也许你就懂了。.

Vue 3中使用的函数重载有啥用?

这是一个简单的 greet 函数,它接收一个字符串类型的参数,其返回值的类型也是字符串类型。

function greet(person: string): string {
  return `Hello, ${person}!`;
}

当使用字符串阿宝哥作为参数调用 greet 函数时,将会返回 "Hello, 阿宝哥!"。那么现在问题来了,如果我们希望 greet 函数能同时支持输入用户列表并返回相应的问候列表那该咋办? 

给你 3 秒钟的时间思考一下,你想到答案了么?其中一种方案是使用联合类型。

function greet(person: string | string[]): string | string[] {  
  if (typeof person === "string") {
    return `Hello, ${person}!`;
  } else if (Array.isArray(person)) {
    return person.map((name) => `Hello, ${name}!`);
  }
  throw new Error("Unable to greet");
}

而另一种解决方案是使用函数重载,使用函数重载技术,我们需要定义重载签名和实现签名。 

其中重载签名定义了函数中每个参数的类型和函数的返回值类型,但不包含函数体。一个函数可以有多个重载签名。 

而实现签名的参数类型和返回值类型都需要使用更通用的类型,且还会包含实现该签名的函数体。一个函数只能含有一个实现签名。 

结合了重载签名和实现签名之后,我们就实现了前面所说的功能:

// 重载签名
function greet(person: string): string;
function greet(persons: string[]): string[];
 
// 实现签名
function greet(person: unknown): unknown {
  if (typeof person === 'string') {
    return `Hello, ${person}!`;
  } else if (Array.isArray(person)) {
    return person.map(name => `Hello, ${name}!`);
  }
  throw new Error('Unable to greet');
}

在实际使用的过程中,只有重载签名才是可以调用的,调用函数之后的返回值就能够被推导出正确的类型。

需要注意的是,当 TypeScript 编译器处理函数重载时,它会查找重载列表,尝试使用第一个重载定义。如果匹配的话就立即返回。当使用实现签名对应类型的参数调用实现签名函数时将会出现错误。

除了重载普通函数之外,我们也可以对类中的方法进行重载。方法重载是指在同一个类中方法同名,参数不同(参数类型不同、参数个数不同或参数个数相同时参数的先后顺序不同),调用时根据实参的形式,选择与它匹配的方法执行操作的一种技术。

下面我们来看一个方法重载的例子:

class Calculator {
  add(a: number, b: number): number;
  add(a: string, b: string): string;
  add(a: string, b: number): string;
  add(a: number, b: string): string;
  add(a: string | number, b: string | number) {
  if (typeof a === 'string' || typeof b === 'string') {
    return a.toString() + b.toString();
  }
    return a + b;
  }
}

const calculator = new Calculator();
const result = calculator.add('Semlinker', ' Kakuqo');

阅读完本文之后,你应该就知道  Vue 3 响应式模块中的 ref 函数背后使用了什么技术。如果你还想进一步了解 TS 函数重载的相关知识,可以继续阅读  是时候表演真正的技术了 - TS 分身之术这篇文章。