How to repair a 'TS2769: No overload matches this call'
My colleague eventually taught me a workaround:
- Exclude the corresponding action, e.g.
takeEvery
from the originalimport
statement. - Import all action with
as Eff
whereEff
is an alias. - Define a new constant with the same name as the original action.
import { put, fork, select, call } from 'redux-saga/effects' // <-- modified
import * as Eff from 'redux-saga/effects' // <-- new
// ...
const takeEvery: any = Eff.takeEvery; // <-- new
const takeLatest: any = Eff.takeLatest; // <-- new
As far as I understand him, the idea is to explicity allow any
type.
Edit: I am aware that Facebook (as developer of React) does explicitly state that one should not use inheritance, see https://reactjs.org/docs/composition-vs-inheritance.html
Overload signatures, union types and "No overload matches this call" error
Passing Unions to An Overload
Typescript is not able to "split up" the union before checking it against your overload signatures. It is checking your variable of type Promise<string> | string
against each overload individually. The union is not assignable to either of its members so there is no overload signature that accepts Promise<string> | string
.
This is a known behavior and some of the GitHub issues about this date back years.
- Support overload resolution with type union arguments #14107
- Union types not working with old-style overloads #1805
The argument against changing the typescript behavior seems to be that the number of possible combinations can explode quickly when you have a function with multiple arguments that each accept multiple types.
Since typescript doesn't support this on its own, you have to manually add an overload that accepts the union and returns the union, like you have already done. Note that the implementation signature (the last line of the overload) does not count as one of the overloads. So just having the union in the implementation signature is not enough.
function trim(text: Promise<string>): Promise<string>;
function trim(text: string): string;
function trim(text: Promise<string> | string): Promise<string> | string;
function trim(text: Promise<string> | string): Promise<string> | string {
if (typeof text === "string") {
return text.trim();
} else {
return text.then(result => result.trim());
}
}
Generics
We don't have this problem when using generics instead of overloads because extends
includes the union. T extends A | B
means that T
can be A
, B
, or the union A | B
(or any more refined version of these, which I'll get into in a bit).
function trim<T extends Promise<string> | string>(text: T): T {
However generics raise their own issues when it comes to the implementation of the function because refining the type of the variable text
doesn't refine the type of the generic T
. That behavior makes sense in general, given that the type T
could be a union.
It's frustrating here especially because we are returning the same type as the input. So if we know that text
is a string
then we know that T
must include string
so it should be fine to return a string
, right? Nope.
It feels like we only have three possibilities (string
, Promise<string>
, Promise<string> | string
). But extends
opens infinite possible types for T
that are refinements of those. If T
is the literal string " A "
then the string
"A"
that we return from text.trim()
really isn't assignable to T
. So we've solved one issue but created another. We have to make as
assertions to avoid errors like:
Type 'string' is not assignable to type 'T'. 'string' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'string | Promise'.(2322)
And there are some weird edge cases where those assertions might be incorrect.
function trim<T extends Promise<string> | string>(text: T): T {
if (text instanceof Promise) {
return text.then((result) => result.trim()) as T;
}
return (text as string).trim() as T;
}
const a = trim(' ' as Promise<string> | string); // type: string | Promise<string>
const b = trim(' '); // type: ' ' -- actually wrong!
const c = trim(' ' as string); // type: string
const d = trim(new Promise<string>(() => ' ')); // type: Promise<string>
const e = trim(new Promise<' '>(() => ' ')); // type: Promise<' '> -- wrong again!
Typescript Playground Link
So I would prefer the overloaded version even though you need that extra line.
Related Topics
Enabling/Disabling Inputs Based on Checkbox Activity
Can We Call the Function Written in One JavaScript in Another Js File
Fullcalendar - Change View for Mobile Devices
Laravel Reactjs:Changes Are Not Reflecting After Change in My Js(Reactjs) File
Javascript Onclick Increment Number
How to Add Data Dynamically to Mat-Table Datasource
How to Detect Chinese Character With Punctuation in Regex
Enable Utf-8 Encoding for JavaScript
How to Override the Onbeforeunload Dialog and Replace It With My Own
Typeerror: Cannot Create Property '_Id' on String
How to Implement Ping/Pong Request for Websocket Connection Alive in JavaScript
Connection Refused! Is Selenium Server Started
Calculate the Bounding Box'S X, Y, Height and Width of a Rotated Element Via JavaScript
Using Setstate to Change Multiple Values Within an Array of Objects - Reactjs
Array Map into String With Line Break - React
The Onclick Is Not Working on First Click
Update Url With Value from Input on Click With React
How to Change Options Based on Another Select Option in a Table