Join egghead, unlock knowledge.

Want more egghead?

This lesson is for members. Join us? Get access to all 3,000+ tutorials + a community with expert developers around the world.

Unlock This Lesson
Become a member
to unlock all features

Level Up!

Access all courses & lessons on egghead today and lock-in your price for life.


    Dynamically Allocate Function Types with Conditional Types in TypeScript

    Rares MateiRares Matei

    Conditional types take generics one step further and allow you to test for a specific condition, based on which the final type will be determined. We will look at how they work on a basic level and then leverage their power to allocate function types dynamically based on the type of their parameters, all without any overloads.



    Become a Member to view code

    You must be a Member to view code

    Access all courses and lessons, track your progress, gain confidence and expertise.

    Become a Member
    and unlock code for this lesson


    Instructor: 00:00 Generics have always been a good solution to making reusable types. You can pass in one type and return a completely new type from it. It's like a function, really. TypeScript's new conditional types take this to the next level.

    00:12 Instead of simply copying the value of whatever's passed in here to some location in the generated type, we can tell the compiler to intelligently analyze a ternary expression.

    00:23 If my generic parameter extends a string or is a string, then my container property will be of type string container. Otherwise, it's going be a number container. I've created in this, that's of an item down here. I'm passing in string as the generic parameter.

    00:37 Let's try this. If I try to access the container on it, we can see we get format, split, all of which are string container property. Now, let's try to pass in a number here, see what happens. As expected, we get number container properties now.

    00:51 I can also build an array filter type. All we have to say is that, if the generic parameter extends an array, then we just return that type. Otherwise, we shall return the never type, so the compiler can ignore it. Let's try this out.

    01:06 I'll create a new type called strings or numbers. I'll use the array filter type that I created. I'll pass in a type that can be either string or a number, or an array of strings, or an array of numbers.

    01:19 Now, if I hover over my new type, I can see that it's either the array of strings or the array of numbers. Anything that wasn't of type array has been filtered out. All of this works because of two mechanisms.

    01:32 Conditional types distribute over each element in the set of possible types that we pass into it. In my case, it's going to apply this ternary operation to each of these four types individually and replace each one of them with whatever this expression returns.

    01:50 In my case, it's going to return never for the first type. It's going to return never for the second type. It's going to return array of string and it's going to return array of numbers for the last type.

    02:01 Second, because by definition the never type can never happen, if TypeScript sees it in a union of types like we have here, it's just going to be ignored. What we end up in the end is a type that's either an array of string or array of numbers.

    02:18 Finally, conditional types can become a replacement for function overloads. I have here a type item service, which has a function getItem. This function is a bit versatile, because if the ID is a string, then it's going to return a book.

    02:33 That's because books are indexed by strings. If it's a number, it's going to return a TV. That's because TVs are indexed by numbers. Now, I can try to overload this function with a few definitions, and it works.

    02:46 Let's see if we can make use of conditional types and keep just the single definition. I'll just add in here. If key is a string, then return a book, otherwise, return a TV.

    03:00 I'll pretend this item service variable here exists, and it's instantiated. I'll attempt to get the book from it by passing in a string. Then, I'll do the same thing for the TV by passing in the number.

    03:12 If I hover over the book, I can see TypeScript correctly infer that it's a book. If I hover over the TV variable, TypeScript correctly inferred that it's a type TV. One problem with this approach is that I can pass in a Boolean in here. It's still going to work and TypeScript will think that this is still a TV.

    03:30 To stop that from happening, I can just lock in the possible types of the ID that can be sent in here to just strings and numbers. The moment I do that, TypeScript will start complaining here that I'm passing in something that's not really allowed.