Check out a free preview of the full TypeScript 5+ Fundamentals, v4 course

The "Callables" Lesson is part of the full, TypeScript 5+ Fundamentals, v4 course featured in this preview video. Here's what you'd learn in this lesson:

Mike discusses creating callable types, explains how to define a function type using an interface or a type alias, and how to specify the parameters and return type of the function. The instructor also discusses the difference between interfaces and type aliases, and when to use each one.


Transcript from the "Callables" Lesson

>> We've dealt with function types and class types already to some extent. Let's take a step back and think about how we would type these things. Not how would we type a specific function, but how would we create a function type? How would we type a variable that is designed to hold a function?

And I call these callables cuz we can call functions. So here's an example of a type, we're using an interface for this. It's a type that has a call signature. Look at these parentheses instead of square brackets, as we saw for index signatures. What we're saying here is we take two arguments, x and y, both numbers, and we return a number, it's almost like that.

Just delete the word function and delete the name of the function, so this is a call signature. You can see here TwoNumberCalc, and we can define this with the type alias as well. The key thing to look out for, which it takes forever for muscle memory to set in here.

It's a fat arrow when you're doing a type alias, because that's kind of valid syntax on the right-hand side. And then of course, in an interface, use a colon, TypeScript will yell at you and help you get it right, but don't feel bad if you mix these up.

So here we've got add and subtract, they're both two number calculations, cuz these types are equivalent, they're the same thing. And you see here, we don't have to type x and y, Right? They are numbers. This is effectively now, we can use this as a type annotation for an arrow function.

TwoNumberCalculation, and then the right-hand side, we don't need any types. If you were going to type a callback that you pass to a function, you would use a similar technique cuz you wanna define that this argument is of type function. Here are the parameters the function takes. Here's what the function must return, and you could use this.

You could say, alright, I take as an argument a TwoNumberCalculation. Of course, you do get the type checking you'd hope to get, like, you've got to return a number here because it's part of this type, TwoNumberCalculation. Yes, sir.
>> You had sort of alluded to it back when we were talking about classes and inheritance and extends, versus implements.

That is an interface that syntactically cannot be implemented because there's no name to sign that-
>> That's-
>> Indication.
>> Wow, that is a totally valid point and good insight there. Earlier I did say interfaces describe like an interface gives you the exact same constraints that, you would need to use an interface to define a class and like there is no way to make a class callable.

Sorry, classes are callable, they just must be used with the new keyword. And we'll see that, but it would not be the signature, it would be something slightly different, trivially different. But yes, you cannot implement this interface, that's a very good insight. I'm not aware of other cases where that's true.

>> I think you may be able to technically implemented in JavaScript. I don't know if there's a way to describe the type information with TypeScript at all [LAUGH] cuz I think from a-
>> You totally can.
>> You can return some callable, invocable thing from the constructor, which is gross, but,

>> You used to be able to call constructors without the new keyword very easily, and there was no disambiguation between callable and constructable, this is purely a TypeScript concept.
>> And this might have kind of been addressed there, or you might be getting to it, but could you explain why to use an interface versus a type with callables.

>> Yeah, that's a good question, I don't think there are clear advantages to using an interface or a type alias, beyond interfaces being open. Where if you want to describe like, I don't know, a function with some things dangling off of it, I think you can still do this, yep, but now, this isn't gonna work, you'd have to do this.

Yay, and all your inference is gone when you do this. So yeah, technically, I mean, you could do that with a type alias as well. I would say it just comes down to the open interface issue, which is rarely something you think about in a situation like this.

If I were writing a library, I would always err on the side of interfaces to allow my downstream consumers little flexibility.
>> Yep, and that was another comment was types for normal projects, interfaces for libraries.
>> Sort of, yeah, in a lot of cases. Sometimes you write a library, and you want to make really sure that nobody messes with a particular type, then you do want to type alias.

I would say, it's more about you allow for modification or not, and you sort of, in libraries default to allowing modification, but there are valid exceptions. And same on the app side, I would say it just matters less on the app side, you can go and mess with these types if you need to.

So yeah, there are lots of places where an interface or a type alias will do.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now