9

TypeScript allows the definition of overloaded function types, which can be implemented in the form of multiple consecutive overloaded declarations + one function implementation. for example

function func(n: number): void;
function func(prefix: string, n: number): void;
function func(first: string | number, n?: number): void {
    if (typeof first === "string") {
        console.log(`${first}-${n}`);
    } else {
        console.log(`number-${first + 10}`);
    }
}

func() function in the example has two overloads:

  • (number) => void
  • (string, number) => void

In its implementation part, the parameter and return value declarations must be compatible with all overloads, so the first parameter may be number or string or first: string | number ; and the second parameter may be number or none, which is n?: number .

The declaration of overloaded functions can be defined by the interface declaration of the function. The above overloaded function type can be defined as follows:

interface Func {
    (n: number): void;
    (prefix: string,  n: number): void;
}

Check it out:

const fn: Func = func;

The above is "Preface"!


Now, forget about func() , we have two functions defined separately func1() and func2() :

function func1(n: number): void {
    console.log(`number-${n + 10}`);
}

function func2(prefix: string, n: number): void {
    console.log(`${prefix}-${n}`);
}

And there is a render() , hope to render the output according to the function passed in:

function render(fn: Func): void {
    if (fn.length === 2) {
        fn("hello", 9527);
    } else {
        fn(9527);
    }
}

So far, everything has been fine. Next is the test render()

render(func1);
render(func2);

The problem is, whether it is func1 or func2 , it can not match the parameter type of render() ! !

There is a misunderstanding that many people understand the overloaded function type. They think that if the function f conforms to an overloaded signature of the overloaded function type Fn , then it should be used as this overloaded type.

In fact, if a function is to match the overloaded function type, then it must also be an overloaded function (or a function compatible with all overloaded types). Take the above example, if you pass in func1 , render() can indeed accurately enter the else branch when running, and successfully call fn(9527) ; passing in func2 can also accurately enter the if branch and successfully call fn("hello", 9527) . but--

These things are done by JavaScript at runtime. The TypeScript compiler, static analysis at the time of discovery render() parameters fn need to be compatible fn(string, number) calls and fn(number) call, whether func1() or func2() do not have all the conditions.

So in the above example, render() can only be the overloaded function func but not func1 or func2 .

So what if you want to achieve the original purpose?

Suppose types Func1 and Func2 are func1() and func2() types, this render() function should be so declared:

function render(fn: Func1 | Func2) { ... }

But in this way, the render() function will not work, because fn is one of the two types, but it is not sure which one is when it is called. We need to write a type assertion function to help TypeScript inference. The complete example is as follows:

type Func1 = (n: number) => void;
type Func2 = (prefix: string, n: number) => void;

function isFunc2(fn: Func1 | Func2): fn is Func2 {
    return fn.length === 2;
}

function render(fn: Func1 | Func2): void {
    if (isFunc2(fn)) {
        fn("hello", 9527);
    } else {
        fn(9527);
    }
}

render(func1);  // number-9527
render(func2);  // hello-9527

Finally, to summarize and emphasize: overloaded function type and the combination function types participating in the overloaded type are completely different two types, please pay attention to the difference, do not misuse.


边城
59.8k 声望29.6k 粉丝

一路从后端走来,终于走在了前端!