Conditional Types in Typescript

Emmanuel Onyebueke
5 min readMay 6, 2023

--

Conditional types are types that are derived based on a condition, they are derived types that depend on the truthiness of a condition

By the end of this article, you should:

  • Understand the syntax and usage of conditional types in TypeScript
  • Learn how to write more generic and reusable code using conditional types
  • Apply conditional types to real-world scenarios in your own TypeScript code
  • Gain a deeper understanding of TypeScript’s type system and how it can help you write safer, more maintainable code.

Conditional types are types that are derived based on a condition, they are derived types that depend on the truthiness of a condition. A conditional statement takes the form condition ? trueExpression: falseExpression .

To create a new conditional type in typescript, it takes the form

type NewType = Type extends OtherType ? TruthyType : FalsyType

In a conditional type, the condition plays a crucial role in determining whether the type evaluates to true or false. This is why it is important to understand how it works.

The Condition

The condition is the part that is checked, and if it meets the requirement, the statement will evaluate to true, else false . In the snippet above, Type extends OtherType is the condition, which means if OtherType is a subset or is within Type , the [extends keyword is used to constraint types](https://medium.com/@developer.olly/typescript-extends-keyword-explained-101b1636c8e4) , for example

string extends string // true
string extends number // false
any extends number // true
never extends string // false
type Car = {
name: string,
model: string,
make: string,
year: number,
engine: string,
}
type Name = {name: string}Car extends Name // true
Car extends {legs: number} // false

In the first section of the above, using the extends keyword, the code checks whether one type extends another or is in the other type or a subset of the other type. For instance, string extends string evaluates to true because string belongs to string, while string extends number evaluates to false because there is no union between these 2 types.

In the second section, there are two object types defined: Car and Name. The Car object type has several properties, including name, model, make, year, and engine. The Name object type has only one property, name.

Car extends Name evaluates to true, since Car has a name property that matches the name property in Name. On the other hand, Car extends {legs: number} evaluates to false, since Car does not have a legs property.

It is worth noting that the condition can vary depending on the type we are working with. For instance, if we are dealing with an object type like Car in the above snippet, the OtherType in the condition may be based on the properties(name ) of the object. On the other hand, if we are working with a function type, the condition may be based on the arguments and the return type of the function(more on this later). Therefore, it is important to carefully analyze the type we are working with in order to understand the condition that needs to be met.

Generic Conditional Types

Conditional types shine more with generics, generics in Typescript are similar to parameters in functions. They allow us to define a type or a set of types that can be used in multiple places in our code. This helps us write more reusable and generic code. However, oftentimes, we might want to create a new and flexible type based on a Generic, For example, let’s look at this snippet.

type isNumber<T> = T extends number ? true : false.

The isNumber type takes a generic parameter T and checks whether T extends the number type. If it does, the type evaluates to true; otherwise, it evaluates to false. For example, isNumber<32> would evaluate to true, while isNumber<"hello"> would evaluate to false.

Let’s look at a typical typescript example Flatten<T> but we call it UnArray<T>,

type UnArray<T> = T extends any[] ? T[number] : T
type Users = {
name: string,
age: number,
city: string
}[]
type User = UnArray<Users>
/** {
name: string,
age: number,
city: string
} */
type Str = UnArray<string> // string

This code defines a generic type UnArray that takes a type T as a parameter. The conditional type checks whether T extends any[] (an array of any type), and if it does, it returns the element type of the array using the T[number] syntax. If T does not extend any[], it returns T as is.

In the example code, Users is a type that defines an array of objects with name, age, and city properties. User is defined as the result of applying the UnArray type to Users, which returns the object type within the array of Users. Str is defined as the result of applying the UnArray type to string, which returns string since it is not an array.

In general, the UnArray type can be used to "flatten" nested arrays, taking an array of arrays and returning a single array of the same element type.

One more example, from the Solid-three code snippet,

type Args<T> = T extends new (...args: any) => any
? ConstructorParameters<T>
: T;

Args is a generic type that takes a type T as a parameter. The conditional type checks whether T extends a constructor function that takes any number of arguments ...args and returns any type. If the condition is true, then the type evaluates to the tuple of argument types [ConstructorParameters<T>](https://www.typescriptlang.org/docs/handbook/utility-types.html#constructorparameterstype) of this constructor function. Otherwise, it evaluates to T.

For instance, given a constructor function User that takes two arguments of type string and boolean, respectively, we can use Args<MyClass> to extract the tuple type of its arguments:

class User {
constructor(name: string, isAdult: boolean) {}
}
type UserArgs = Args<typeof User>; // [user:string, isAdult: boolean]

In this example, UserArgs is inferred as the tuple type [string, boolean], since User takes two arguments of these types in its constructor function.

In Summary

This article provides a brief guide to conditional types in TypeScript. we explained what conditional types are and how to create new conditional types in TypeScript, using the syntax Type extends OtherType ? TruthyType : FalsyType. We also show how to use conditional types with generics, to write more generic and reusable code. also explains how the condition plays a crucial role in determining whether the type evaluates to true or false and provides several examples to illustrate how to apply conditional types to real-world scenarios in TypeScript code with some concrete examples.

--

--