Profile picture
💘chriseppstein @chriseppstein
, 25 tweets, 5 min read Read on Twitter
y'all. I made the coolest typescript function tonight.
So, I've been working on this Maybe implementation for typescript.

^ I already had a function `callMaybe(fn, arg1, arg2?, arg3?, arg4?): Maybe` where the arguments can be Maybe or a concrete type and if any of them are a None, then it returns none, otherwise it invokes the function with the arguments after unwrapping them.
The function can return a Maybe, or not. whatever. But the type checker will make sure all the arguments are the right type to be passed to the function. and it accounts for the correct arity and all that (up to 4 args, but I can add more if need be)
^ I was pretty pleased with myself about this one, because, tbh, it's harder than it should be to write generics for arbitrary functions.
^ But I've been really hung up on how to handle methods of objects with similarly strong type checking. I tweeted in frustration about this a bit ago

^ But tonight I came up with a new tack. I wrote: `methodMaybe(instance, methodName, arg1?, arg2?, arg3?, arg4?)`. Where the instance can be a maybe as can any of the arguments (or not) and returning the type of the method as a maybe if there's no None values.
^ This is cool, because, you don't have to write your method to accept maybes. If you find yourself with a value that's a maybe, you can call any method with it without doing all the unwrapping and checking yourself.
^ The type checker will ensure the method name exists on the instance type and it will check that the arguments passed have the same types as the arguments to the method. and the return type will be correctly inferred as a maybe of the method's return type.
^ So, here's the general strategy for doing something similar:
You create a mapped type where all the properties from the source type have the method signature of the arguments you've received. Then you use `Pluck` to narrow that type to just the one property you care about.
^ Like so:

type HasMethod<Type extends object, N extends keyof Type, PropertyType> =
Pick<{ [P in keyof Type]: PropertyType; }, N>;
Grrr. when I said `Pluck` a couple tweets ago, I meant `Pick`. sorry
^ So anyways, this gives you a type that has one property that has a method signature matching that of the arguments you're passing in but the property is named according to the method you want to call.
^ Does your object match that? Who knows?! That's why we have a type checker :)
^ Ok so now, this is where you have to turn your brain inside out a bit.

You can declare that the object passed in be of a type that extends this synthetic type -- which is defined by the object we're passing in!
So, without doing an funky types manipulation, we can now invoke a method with one argument:

type FunctionCall1<A1, R> = (arg1: A1) => R;
function callMethod<
Name extends keyof T,
T extends HasMethod<T, Name, FunctionCall1<A1, R>>,
A1, R>(obj: T, name: Name, arg1: A1): R;
^ I don't know how the hell typescript can make sense of any of that. It's so self referential. BUT IT DOES.
^ So, then I mixed in a bunch of type unions and function overloading.

There's 7 generics to get a method that accepts up to 4 arguments. But *none of them* have to be specified when you invoke the function.
^ So anyways. here's the whole goddamn library as a gist. I'll publish it properly soon.

gist.github.com/chriseppstein/…
^ You gotta be on @typescriptlang version 2.7.1 to get this to work because I'm using a constant symbol as a key for the Maybe object and that's a new feature.
^ But wait, there's more! If you read my gist now, I'll throw in my favorite utility types!!

Do you have a function that needs to accept any possible value but find that the `any` type is just a fancy way to write bugs? Me too! That's why I use `whatever`!
Or maybe your function needs to accept `something` as long as it's not `nothing`? I got you, fam. Don't settle for any old `any` that you can get in a standard library. 🙄
Alrighty, that's all the tech tweets for tonight... I hope you've enjoyed this little monologue on how to call an arbitrary method in typescript.
Update: I discovered two things:

1️⃣ Typescript doesn’t error out if you pass too many arguments to the function. Only if you pass too few or the wrong types.
2️⃣ the return type is not inferred based on the function being called, but it is validated if specified as a generic argument or assigned to a value with a declared type.
Missing some Tweet in this thread?
You can try to force a refresh.

Like this thread? Get email updates or save it to PDF!

Subscribe to 💘chriseppstein
Profile picture

Get real-time email alerts when new unrolls are available from this author!

This content may be removed anytime!

Twitter may remove this content at anytime, convert it as a PDF, save and print for later use!

Try unrolling a thread yourself!

how to unroll video

1) Follow Thread Reader App on Twitter so you can easily mention us!

2) Go to a Twitter thread (series of Tweets by the same owner) and mention us with a keyword "unroll" @threadreaderapp unroll

You can practice here first or read more on our help page!

Did Thread Reader help you today?

Support us! We are indie developers!


This site is made by just three indie developers on a laptop doing marketing, support and development! Read more about the story.

Become a Premium Member and get exclusive features!

Premium member ($3.00/month or $30.00/year)

Too expensive? Make a small donation by buying us coffee ($5) or help with server cost ($10)

Donate via Paypal Become our Patreon

Thank you for your support!