TypeScript function overload writing method, which layer are you on!

Posted by bilis_money on Mon, 13 Dec 2021 01:04:40 +0100

Author: Dmitri pavlutin
Translator: front end Xiaozhi
Source: dmitripavlutin

There are dreams and dry goods. Wechat search [Daqian world] pays attention to this bowl washing wisdom who is still washing dishes in the early morning.
This article GitHub https://github.com/qq449245884/xiaozhi It has been included. There are complete test sites, materials and my series of articles for the interview of front-line large factories.

Most functions accept a fixed set of parameters.

But some functions can accept a variable number of parameters, different types of parameters, and even return different types according to the way you call the function. To annotate such functions, TypeScript provides function overloading.

1. Function signature

Let's first consider a function that returns a greeting message to a specific person.

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

The above function accepts a parameter of 1 character type: person's name. Calling this function is very simple:

greet('World'); // 'Hello, World!'

What if you want to make the greet() function more flexible? For example, let it accept another list of people to greet.

Such a function will take a string or string array as a parameter and return a string or string array.

How to annotate such functions? There are two ways.

The first method is very simple, which is to directly modify the function signature by updating parameters and return types. The following is the appearance of green () after Refactoring:

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

Now we can call greet() in two ways:

greet('World');          // 'Hello, World!'
greet(['Xiao Zhi', 'Daye']); // ['Hello, Xiaozhi!', 'Hello, Daye!']

It is a common and good method to directly update the function signature to support multiple call modes.

However, in some cases, we may need to adopt another method to define all the ways in which your function can be called. This method is called function overloading.

2. Function overloading

The second method is to use the function overload function. I recommend this method when function signatures are relatively complex and involve multiple types.

Defining a function overload requires defining an overload signature and an implementation signature.

The overload signature defines the formal parameters and return types of the function without the function body. A function can have multiple overload signatures: corresponding to different ways of calling the function.

On the other hand, the implementation signature also has parameter types and return types, as well as the body of the implementation function, and there can only be one implementation signature.

// Overload signature
function greet(person: string): string;
function greet(persons: string[]): string[];
 
// Implementation signature
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');
}

The greet() function has two overload signatures and one implementation signature.

Each overload signature describes a way in which the function can be called. As far as the greet() function is concerned, we can call it in two ways: with a string parameter or with a string array parameter.

Implementation signature function greet(person: unknown): unknown {...} Contains the appropriate logic for how the function works.

Now, as above, greet() can be called with parameters of type string or string array.

greet('World');          // 'Hello, World!'
greet(['Xiao Zhi', 'Daye']); // ['Hello, Xiaozhi!', 'Hello, Daye!']

2.1 overload signature is callable

Although the implementation signature implements the function behavior, it cannot be called directly. Only overload signatures are callable.

greet('World');          // Overload signature callable
greet(['Xiao Zhi', 'Daye']); // Overload signature callable

const someValue: unknown = 'Unknown';
greet(someValue);       // Implementation signature NOT callable

// report errors
No overload matches this call.
  Overload 1 of 2, '(person: string): string', gave the following error.
    Argument of type 'unknown' is not assignable to parameter of type 'string'.
  Overload 2 of 2, '(persons: string[]): string[]', gave the following error.
    Argument of type 'unknown' is not assignable to parameter of type 'string[]'.

In the above example, even if the implementation signature accepts an unknown parameter, the greet() function cannot be called with a parameter of type unknown (greet(someValue)).

2.1 the implementation signature must be universal

// Overload signature
function greet(person: string): string;
function greet(persons: string[]): string[]; 
// This overload signature is incompatible with its implementation signature.

 
// Implementation signature
function greet(person: unknown): string {
  // ...
  throw new Error('Unable to greet');
}

Overloaded signature function greet(person: string[]): string [] is marked as incompatible with greet(person: unknown): string.

The string return type that implements the signature is not universal enough and cannot be compatible with the string [] return type that overloads the signature.

3. Method overload

Although in the previous example, function overloading is applied to an ordinary function. But we can also overload a method

In the method overload section, both overload signature and implementation signature are part of the class.

For example, we implement a Greeter class with an overloaded method, greet().

class Greeter {
  message: string;
 
  constructor(message: string) {
    this.message = message;
  }
 
  // Overload signature
  greet(person: string): string;
  greet(persons: string[]): string[];
 
  // Implementation signature
  greet(person: unknown): unknown {
    if (typeof person === 'string') {
      return `${this.message}, ${person}!`;
    } else if (Array.isArray(person)) {
      return person.map(name => `${this.message}, ${name}!`);
    }
    throw new Error('Unable to greet');
  }

The Greeter class contains the greet() overloaded method: two overloaded signatures describe how to call the method and contain the implementation signature of the correct implementation

Due to method overloading, we can call hi in two ways Greet (): use a string or an array of strings as arguments.

const hi = new Greeter('Hi');
 
hi.greet('Xiao Zhi');       // 'Hi, Xiao Zhi!'
hi.greet(['Wang Daye', 'Daye']); // ['Hi, Wang Daye! ','Hi, Daye!']

4. When to use function overloading

Function overloading, if used properly, can greatly increase the availability of functions that may be called in many ways. This is particularly useful in auto completion: we will list all possible overload records in auto completion.

However, in some cases, it is recommended not to use function overloading, but to use function signatures.

For example, do not use function overloading on optional parameters:

// Not recommended
function myFunc(): string;
function myFunc(param1: string): string;
function myFunc(param1: string, param2: string): string;
function myFunc(...args: string[]): string {
  // implementation...
}

It is sufficient to use optional parameters in the function signature:

// Recommended Practice
function myFunc(param1?: string, param2: string): string {
  // implementation...
}

5. Summary

Function overloading in TypeScript allows us to define functions that can be called in many ways.

Using function overloading requires defining overload signatures: a set of functions with parameters and return types, but no body. These signatures indicate how the function should be called.

In addition, you must write the correct implementation of the function (implementation signature): parameters and return types, as well as the function body. Note that the implementation signature is not callable.

In addition to regular functions, methods in classes can also be overloaded.

The bugs that may exist after code deployment cannot be known in real time. Afterwards, in order to solve these bugs, we spent a lot of time on log debugging. By the way, we recommend a useful BUG monitoring tool Fundebug.

Original text: https://dmitripavltin.com/typ...

communication

There are dreams and dry goods. Wechat search [Daqian world] pays attention to this bowl washing wisdom who is still washing dishes in the early morning.

This article GitHub https://github.com/qq44924588... It has been included. There are complete test sites, materials and my series of articles for the interview of front-line large factories.

Topics: Javascript JQuery TypeScript