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
-
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.
-
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.
-
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.