Table of contents
1.
Introduction
2.
What are conditional types?
3.
Why do we need conditional types?
4.
Error handling and conditional types
5.
What are distributive conditional types?
6.
FAQs
7.
Key Takeaways 
Last Updated: Mar 27, 2024

Conditional Types

Author Toohina Barua
0 upvote
Career growth poll
Do you think IIT Guwahati certified course can help you in your career?

Introduction

Typescript is the superset of javascript. But what is so special about typescript? Was javascript not enough? Well, the answer to all these questions is in the name typescript! As the name suggests, typescript can deal with the data type. When using typescript, we would not lose information about the data type. Typescript has extra features, and one of them is conditional types. So, let’s dive into this article on conditional types in typescript.

Source

What are conditional types?

In javascript, we make decisions based on the input. Sometimes, our program has to make decisions based on the data type of the input. Conditional types define the relationship between the inputs and outputs. 

Conditional types are similar to the ternary operator in javascript. Typescript will determine which type can be assigned to the variable based on the condition. The majority of the time, conditional types are used alongside generics.

Source

Let us look at a small example. Write the following code in your typescript file.

Since type1 is a number, type2 will become null.

If we change type1 to string, type2 will be assigned string as shown below.

Why do we need conditional types?

Look at the code below:

interface ForId {
    id: number
}
interface ForName {
    name: string
}
function create(id: number): ForId
function create(name: string): ForName
function create(nameOrId: string | number): ForId | ForName
function create(nameOrId: string | number): ForId | ForName {
    throw "unimplemented";
}

These create() functions overloads describe a single JavaScript function that decides based on the types of inputs it receives. 

When a library has to make the same decision repeatedly across its API, it becomes inconvenient.

We'll need to make three overloads: for each situation where we know the type (string and number) and the most general case (taking a string | number). The number of overloads grows exponentially for each new type that create() can handle.

Because of the above reasons, using conditional types is very useful as they make writing the above logic easier.

type nameOrId<T extends number | string> = T extends number? ForId:ForName
function create<T extends number | string>(nameOrId: T): nameOrId<T> {
    throw "unimplemented"
}

We use that conditional type to reduce our overloads to a single, non-overloaded function.

Error handling and conditional types

Let us see how we can use conditional types in error handling. We will deal with application and server errors in the following code.

abstract class ErrorApplication {
    abstract status: number;
    abstract message: string;
}
class ErrorServer extends ErrorApplication {
    status = 500;
    constructor(public message: string) {
        super();
    }
}
type ErrorType<T extends {error: ErrorApplication | Error}> = T['error'] extends ErrorApplication ? ErrorApplication : Error;

type typeOfError1=ErrorType<{error:Error}>

type typeOfError2=ErrorType<{error:ErrorApplication}>

For typeOfError1 the ErrorType assigned is Error(the second option in conditional type statement) as it is not application error. 

Since the ErrorType is an application error, the first option in the conditional type statement is assigned to typeOfError2. 

What are distributive conditional types?

Distributive conditional types are conditional types in which the checked type is a naked type parameter. While given a union type, conditional types become distributive when acting on a generic type. For instance:

type ToDisArray<Type> = Type extends any ? Type[] : never
type ArrStrOrNum = ToDisArray<string | number>

When we use ToDisArray with a union type, the conditional type is applied to each union member. ArrStrOrNum distributes on string | number. And maps each member type of the union to ToArraystring> | ToArraynumber>. As a result, we have string[] | number[]. As given below:

FAQs

  1. What is an application of conditional types?
    Conditional types are great for 'plumbing' or 'framework' code for dealing with API boundaries and other things in server-side scripting.
     
  2. Does typescript give an error when trying to change a variable to uppercase without mentioning the data type?
    Yes, when we use the toUpperCase() function on a variable without mentioning the datatype, we get an error before compilation. Javascript does not throw such an error. Javascript only gives an error on runtime when converting a number type variable to uppercase.  
     
  3. How can you avoid distributivity behavior in conditional types? 
    To avoid distributivity behavior, you can surround each side of the extends keyword with square brackets.

Key Takeaways 

From this article, we learned about conditional types in typescript and why we need them. We saw some practical coding examples of conditional types. We observed the working of conditional types using an error handling example. We also saw distributive conditional types.   

But this is not enough; you need something extra to excel in Vue.js truly. If you want to learn more about Vue.js, you can read our articles on Vue.js or take our highly curated Web Development course.

Live masterclass