TypeScript - Conditional Types



In TypeScript, conditional types allow you to assign a type to the variables based on the conditions. This enables you to define types that dynamically change based on certain conditions. This feature is very useful for large-scale applications, where you need dynamic typing according to the different situations.

Basic Conditional Types

We will use the ternary (?:) operator to use the conditional types. It will evaluate the condition, and based on the true or false result, it will select the new type.

Syntax

You can follow the syntax below to use the conditional types.

type A = Type extends anotherType ? TrueType : FalseType;
  • In the above syntax, the 'Type extends anotherType' condition will be evaluated first.

  • If the condition is true, 'type A' will contain the 'TrueType'. Otherwise, it will contain the 'FalseType'.

  • Here, 'extends' keywords check whether 'Type' is the same as 'anotherType' or at least contains all properties of the 'anotherType' type.

Example

In the code below, we have defined the 'car' type which contains the name, model, and year properties. We have also defined the 'Name' type which contains only 'name' properties.

The 'carNameType' type variable stores either a string or any value based on the evaluation result of the 'Car extends Name' condition. Here, the condition will be evaluated as true as the 'Car' type contains all properties of the 'Name' type.

After that, we have created the 'carName' variable of 'carNameType' and printed it in the console.

type Car = {
    name: string,
    model: string,
    year: number,
}
type Name = { name: string }
// If Car extends Name, then carNameType is string, otherwise it is any
type carNameType = Car extends Name ? string : any; // string
// Define a variable carName of type carNameType
const carName: carNameType = 'Ford';
console.log(carName); // Ford

On compiling, it will generate the following JavaScript code.

// Define a variable carName of type carNameType
const carName = 'Ford';
console.log(carName); // Ford 

Output

Ford

Generic Conditional Types

Now, let's learn the generic conditional types. In TypeScript, generic types are similar to the parameters in the function. It allows developers to define a conditional type such that it can be used at multiple places in the code. It provides flexibility to use the different types in the conditional statements.

Syntax

You can follow the syntax below to use the generic conditional types.

type ConditionalType<T> = T extends Type1 ? TrueType : FalseType;
  • In the above syntax, 'conditionalType<T>' has a type 'T' parameter, and 'conditionalType' is the name of the type.

  • The condition 'T extends Type1' checks whether type 'T' extends 'Type1'.

  • If the condition evaluates true, 'TrueType' will be assigned to the 'ConditionalType'. Otherwise, 'FalseType' will be assigned.

Example

In the code below, 'IsNumArray<T>' is a generic type that takes type 'T' as a parameter. It checks whether the type of the 'T' is an array of numbers (number[]). If yes, it returns 'number'. Otherwise, it returns 'string'.

After that, we defined the 'num' and 'str' variables and used the 'IsNumArray' type with them. For the 'num' variable, we have used the 'number[]' parameter, and for the 'str' variable, we have used the 'string[]' parameter.

// Generic conditional types
type IsNumArray<T> = T extends number[] ? number : string;
const num: IsNumArray<number[]> = 5; // number
const str: IsNumArray<string[]> = '5'; // string
console.log(num);
console.log(str);

On compiling, it will generate the following JavaScript code.

const num = 5; // number
const str = '5'; // string
console.log(num);
console.log(str);

Output

5
5

Conditional Type Constraints

Conditional type constraints are also called type assertions or conditional type predicates. It is used to add constraints on generic types. The generic types are reusable but if you want to reuse them for particular data types like array, number, etc. then you should add constraints with the generic type.

Syntax

You can follow the syntax below to use the conditional type constraints.

type ConditionalType<T extends T1 | T2> = T extends Type1 ? TrueType : FalseType;

In the above syntax, we have added the constraints for the type parameter 'T'. It accepts the values of type 'T1' and 'T2'.

Example

In the code below, 'CondionalType' is a generic type. It takes a number or string type as a value of the type parameter 'T'. The conditional type returns the number if the type of 'T' is a number. Otherwise, it returns a string.

Next, we reused the ConditionalType type by passing the number and string as a type parameter.

You can notice that when we try to use the 'boolean' as a type parameter, it throws an error. So, it allows to reuse of this generic conditional type with limited types.

// Defining the conditional type with constraints
type ConditionalType<T extends number | string> = T extends number ? number : string;
let x: ConditionalType<number> = 10;
let y: ConditionalType<string> = 'Hello';
// let z: ConditionalType<boolean> = true; // Error: Type 'boolean' does not satisfy the constraint 'number | string'.
console.log("The value of x is: ", x);
console.log("The value of y is: ", y);

On compiling, it will generate the following JavaScript code.

let x = 10;
let y = 'Hello';
// let z: ConditionalType<boolean> = true; // Error: Type 'boolean' does not satisfy the constraint 'number | string'.
console.log("The value of x is: ", x);
console.log("The value of y is: ", y);

Output

The value of x is:  10
The value of y is:  Hello

Inferring Within Conditional Types

In TypeScript, inferring within the conditional types means to infer the types with the conditional type definition. It allows you to create a more flexible type of transformation.

For example, if you have an array of elements and match it with the type 'T' in the conditional types. After that, you want to return the type of the array element if the condition becomes true, in such case inferring conditional types is useful.

Syntax

You can use the 'infer' keyword to infer within the conditional types.

type Flatten<Type> = Type extends Array<infer Item> ? Item : Type;

In the above syntax, the 'infer' keyword is used to extract the type of the array element.

Example

In the code below, we have created the 'Flatten' conditional type. We used the infer keyword with the type 'U'. It extracts the type of 'U' from the array of elements having type 'U'.

For 'Flatten<boolean>', it will return a boolean. So, if we try to assign a string value to the 'bool1' variable, it throws an error.

// Inferring within the conditional types
type Flatten<T> = T extends (infer U)[] ? U : T;
let bool: Flatten<boolean[]> = true;
// let bool1: Flatten<boolean> = "s"; // Error as string can't be assigned to boolean
console.log(bool);

On compiling, it will generate the following JavaScript code.

let bool = true;
// let bool1: Flatten<boolean> = "s"; // Error as string can't be assigned to boolean
console.log(bool);

Output

true

This way, you can use the conditional types in TypeScript. You can use the generic types, and infer them to make type transformation more flexible.

Advertisements