bjfletcher
bjfletcher

Reputation: 11518

How to define the function type with an existing type in a more readable way?

With a very simple and readable function like:

function signIn(...) {...}

To define its type with an existing definition Action, it becomes something that's not very readable:

const signIn: Action = function (...) {...}

That is a lot of changes, and not very readable, just to specify that the function is of the Action type.

I wonder if there's any way to not use the const and to keep signIn next to function? I realise I can copy the existing type definition Action and apply it directly to the function, but it's not good for maintainability – what if the original Action definition changes?

Upvotes: 0

Views: 85

Answers (1)

jcalz
jcalz

Reputation: 329228

So you have a function type like this:

type MyFunc = (x: string, y: number) => boolean;

but which can't be used as an annotation for a general function declaration:

// where to annotate?    
function myFunc(x: string, y: number) {
    return x.length > y; 
}

It would be nice if you could annotate the function above somehow, like the following hypothetical syntax (which currently doesn't work):

// do not do this, it doesn't work:
function myFunc: MyFunc (x, y) {
    return x.length > y;
}

Sadly this is not currently supported in TypeScript. There is, however, an open suggestion for this feature: microsoft/TypeScript#22063. It doesn't look like there's been any movement on that issue, but if you head over there and give it a 👍 you might very slightly increase the chance of it being implemented. Or, if your use case is particularly compelling and not already mentioned, you might make a comment there describing it. Realistically this might never make it into the language.


So what are the workarounds? The obvious one and the best one is to change your declared function into a declared const of a function type, as you've already done:

const myFunc: MyFunc = function (x, y) {
  return x.length > y;  
}

You could opt to use the built-in utility types Parameters<T> and ReturnType<T> to convert the annotation of the function itself into annotation of the arguments and return type, but it's so hideous that I can't imagine anyone would prefer this to the previous version:

function myFunc(...[x, y]: Parameters<MyFunc>): ReturnType<MyFunc> {
    return x.length > y;
}

Another possibility is to try using the type system to verify that your declared function is assignable to the MyFunc type in a separate line. If you have a helper type called Extends:

type Extends<T, U extends T> = void;

then you can do this:

type MyFuncWorks = Extends<MyFunc, typeof myFunc>; // okay
function myFunc(x: string, y: number) {
    return x.length > y;
}

and you'd see an error if you changed myFunc to something incompatible:

type MyFuncBroken = Extends<MyFunc, typeof myFunc>; // error!
// number is not boolean ---------> ~~~~~~~~~~~~~
function myFunc(x: string, y: number) {
    return x.length + y; // + instead of <, oops
}

The least ugly workaround is still the function-typed const, so if I were you, I'd go with that.


Okay, hope that helps; good luck!

Playground link to code

Upvotes: 3

Related Questions