Function Overloading in TypeScript (I was wrong)

preview_player
Показать описание
TypeScript gives you all the power you need to create flexible functions that support multiple call signatures!

Рекомендации по теме
Комментарии
Автор

Function overloading with different return types isn't actually type safe at all. TS can't enforce the actual return type deduced from the parameters passed. It just relies on the signature you've provided, only giving you the illusion of type safety with zero errors or warnings when there's a bug in your implementation. This is quite literally the same thing as using the `as` keyword and should be discouraged imo. Here's a buggy version of your `maybe` function:

function maybe<T>(fnOrP: () => T): T | undefined
function maybe<T>(fnOrP: Promise<T>): Promise<T | undefined>
function maybe<T>(fnOrP: (() => T) | Promise<T>): T | undefined | Promise<T | undefined> {
if (typeof fnOrP === "function") {
try {
return fnOrP()
} catch {
return undefined
}
}
return undefined // TS thinks this is ok
// return fnOrP.catch(() => undefined)
}

main()
function main() {
// TS thinks `x` is a Promise<string | undefined>
const x =
// but in reality it's just undefined
console.log(x)
}

I don't think you should be treating TS as a separate language from JS. In general, don't use the fancy features JS doesn't have natively (e.g., enums, overloading, and so on) because they usually lead to buggy code.

raianmr
Автор

Function overloading is good for library code where DX is the goal. But it’s horrible to maintain for application code. In that case just use multiple functions. Then you don’t need to throw all those errors.

kbitgood
Автор

In the overload signature you can also rename the params. So it doesn’t have to be “updateOrKey” for one it would be update:Partial<Widget> and the other would be key:K

kbitgood
Автор

In the last example there are a couple things you could do to clean it up a bit.
1. If you know the consumers of the function will be using TS and you have those overloads specified, you can just check for the existence of the last argument to know which signature is being used and not need to check the types.
2. If you're not ok with that, at the very least you could (using the final code for reference) combine the `if`s on lines 32 and 33:

if (isString(updateOrKey)) {
if (!isWidget(widgetOrValue) && isWidget(widget)) {
return { ... }
}
throw "wrong args";
}

DontFollowZim
Автор

I love to use function overload especially to handle promises vs non promise code it’s probably an edge case but I love to put the logic of handling async code inside the functions and methods I defined instead of in my code where I am using them 😅

Luxcium
Автор

Love your videos ❤ They‘re exactly in the right spot between niche knowledge and things that you can apply to your daily workflows. 🎉

Schippo
Автор

Option type in Scala is an iterable that can be None - it makes sense because as a developer your know that argument is Optional rather than any variable just being possibly undefined and having to handle it again as possibly undefined.

TheYinyangman
Автор

This was rather enlightening- had no idea this was a valid typescript pattern :-)

alanbloom
Автор

My neck gets sore just watching this guy

TheYinyangman
Автор

Happy New Year, awesome as always, I see many more subscribers in your future :D

radulaski
Автор

You can even have different argument names to make it clearer.

function updateWidget(update: Partial<Widget>, widget: Widget): Widget;
function updateWidget<K extends keyof Widget>(key: K, value: Widget[K], widget: Widget): Widget;

vukkulvar
Автор

I don't use overloads too much either, but there was this one instance in my Next.js codebase where I neeed to set cookies via server-side or client-side.


/**
* Set a cookie with options.context:
* - Set Document cookie, or
* - Set Response Headers cookie
*/
function set(
cookieName: string,
value: CookieValue,
options?: CookieOptions
): void;
function set(
this: NextApiResponse,
cookieName: string,
cookieValue: CookieValue,
options: CookieOptions = {}
) {
const { expires: days, context = 'document', domain: host = '' } = options;
const domain = !host ? '' : `Domain=.${host};`;
const expires = !days ? '' :
const value =
const path = `Path=/;`;
const cookie = `${value} ${expires} ${domain}${path}`;

if (context === 'document') {
document.cookie = cookie;
}

if (context === 'headers' && 'setHeader' in this) {
this.setHeader('Set-Cookie', cookie);
}
}

mluevanos
Автор

Function overload with intersections is where's at.

hugodsa
Автор

i think it is worth to mention that this is not possible with arrow const functions :( only the old way functions declarations

kamilzielinski
Автор

Great video! I didn't know about this!

noahwinslow
Автор

Doesn't this also solve the messy code inside your updateWidget function? Because there are only two possibilities instead of eight, you also need only one binary check instead of three. Just test if the third argument is a widget and if it is, you return { ...widget, [updateOrKey]: widgetOrValue}, else you return {...widgetOrValue, ...updateOrKey}. You don't need any of the "wrong args" cases anymore because they're all already caught by the type checking.

kappasphere
Автор

I believe it is possible to make TS smart enough to not require all those checks inside the updateWidget function. What you can do is define the two possible argument signatures as two named tuple types (say Sig1Params, Sig2Params).
Then you can use: updateWidget(...args: Sig1Params | Sig2Params) {...}

Now you only need one check for TS to narrow down the types of the args.

Sorry i can't produce a playground link as I'm writing this from my phone. But let me know if you need more clarification, i can get back later and share a playground link.

iamrohandatta
Автор

Amazing content! Thank you, I'm subscribing right now

tomshieff
Автор

I hate function overloading in ts. It is neat when using it but ugly implementing it. Better implementations of this paradigm can be found in other languages like C#

DaveTheDeveloper
Автор

The more I watch your videos the less I like typescript. This doesn't mean your videos are bad. It's the opposite. Your videos show a broad look at typescript. This broad look helps me to understand that things are done in a stupid way. It's the same for python. Things are done in a different often worse way then most languages. For example overloading. Would it have been so difficult to simply supply the standard model of overloading with seperate bodies? No they do it differently with this weird shit that doesn't solve the problem overloading was designed to solve. To have different implementations for the same function name. You have to then check the types of the overload with if cases in the body. This is like you would sieve sand and then throw the things collected back on the pile and pick them back out by hand.

redcrafterlppa