Implement a ReturnTypeOf<T> Conditional Type in TypeScript

Marius Schulz
InstructorMarius Schulz

Share this video with your friends

Send Tweet
Published a year ago
Updated a year ago

In this lesson, we're going to write another conditional type. This time, we'll implement a ReturnTypeOf<T> helper type which lets us extract the return type of a function type. This type is identical to the ReturnType<T> helper type that's defined in the core type declaration files that ship with the TypeScript compiler.

Instructor: [0:00] Conditional types are often used to implement various helper types. For example, here we have a type called, ReturnTypeof, that accepts a generic type parameter T. If that type T is a function type, it extracts the return type. I've implemented this type using the infer keyword.

[0:19] Here, we have our type relationship test. We're checking whether the generic type T is assignable to this function type. This function type says that we have a function that takes an arbitrary number of arguments of arbitrary types and that returns a type which we can refer to as U.

[0:37] If that type relationship test is true and our type T is assignable to this function type, which has the inferred return type U, we produce the return type U. Otherwise, we produce the type any. Note that we can only refer to the inferred type U in the true branch of the conditional type. If I try to refer to U here, TypeScript gives me a TypeError. It's saying that it cannot find the name U.

[1:03] This makes sense because, if the type T is not assignable to this function type, there's no way for TypeScript to infer this type U. Let's have a look at a few examples. In example A, we're determining the return type of this function type which does not accept any parameters and which returns a string. Unsurprisingly, we get the return type string.

[1:26] In example B, we're determining the return type of the Math.random() method, and that is indeed a number. In example C, we're doing the same thing with the Array.isArray() method, which returns a Boolean, so the inferred return type here is Boolean.

[1:42] Our ReturnTypeof helper type is currently too permissive. We currently can pass a type to it that is not a function type, for example, the type string. This is because we haven't added any constraints to our generic type parameter T up here.

[1:58] What we can do is, we can add a type constraint. We can say that our type parameter T must itself be a function type, so it must be assignable to a function type with an arbitrary parameter list and an arbitrary return value. Now, we get a TypeError in line eight because the type string does not satisfy this function constraint.

[2:21] You'll find that a lot of developers are using single-letter names for the generic type parameters, but we don't have to do that. We can pick descriptive names here. For example, we could rename the type T to TFunction. Similarly, we can rename the type U to something like TReturnType. Maybe this type is more readable this way.

[2:44] Finally, I want to point out that the ReturnType helper type is built into the core type declaration files shipping with the TypeScript compiler. We don't have to define it ourselves. We can simply use the built-in ReturnType helper type. If we jump to the definition, you can see that it's defined exactly the same way as we've defined it.

[3:05] While you're in here, I would encourage you to have a look at some of the other built-in conditional types. You can learn a lot about TypeScript's type system by having a look at the built-in type declarations. It can be overwhelming at times, but you'll learn so much about TypeScript syntax and the capabilities of the type system.