Day 49: Conditional Types
Tags: javascript, typescript, 100daysofcode
Published: 2023-09-15 13:30:00

## What are Conditional Types?

Conditional types are a TypeScript feature introduced in version 2.8 that enable you to define types based on conditions. They allow you to express complex type relationships and create reusable type logic. Conditional types are defined using the `extends` keyword and the `?` operator, making them highly versatile.

### Basic Syntax

Let's start with the basic syntax of a conditional type:

```typescript
type MyConditionalType<T> = T extends U ? X : Y;
```

- `T` is the type parameter we want to test.
- `U` is the type we're checking `T` against.
- `X` is the type that `MyConditionalType` evaluates to if the condition is `true`.
- `Y` is the type that `MyConditionalType` evaluates to if the condition is `false`.

### Understanding the Condition

The condition in a conditional type can be any valid type operation or expression. Commonly used operations include `keyof`, `extends`, and `infer`. Here's a breakdown of some common condition types:

- `keyof`: Checks if a type has a specific key.
- `extends`: Tests if a type extends another type.
- `infer`: Extracts a type from another type.


## Basic Examples

### Conditional Type with `extends`

```typescript
type IsString<T> = T extends string ? true : false;

const a: IsString<"Hello"> = true; // true
const b: IsString<42> = false;     // false
```

In this example, `IsString<T>` checks if `T` extends (is assignable to) the `string` type. If so, it returns `true`, otherwise `false`.

### `keyof` and Index Signatures

```typescript
type KeysOfType<T, U> = {
  [K in keyof T]: T[K] extends U ? K : never;
};

interface Person {
  name: string;
  age: number;
  email: string;
}

type StringKeys = KeysOfType<Person, string>; // "name" | "email"
```

Here, `KeysOfType<T, U>` generates a new type that contains keys from `T` whose corresponding values extend `U`. In our `Person` interface, it extracts keys with values of type `string`.

## Advanced Examples

### Mapping Union Types

```typescript
type FilterByType<T, U> = T extends U ? T : never;

type OnlyStrings = FilterByType<string | number | boolean, string>; // "string"
```

This example creates a type that filters out all non-`U` types from a union type `T`.

### Inferring Function Parameters

```typescript
type FirstParam<T> = T extends (param1: infer P, ...args: any[]) => any ? P : never;

function greet(name: string, age: number) {
  return `Hello, ${name}! You are ${age} years old.`;
}

type NameParam = FirstParam<typeof greet>; // string
```

Here, `FirstParam<T>` extracts the type of the first parameter of a function. In this case, it infers `string` from the `greet` function.

## Built-in Conditional Types

TypeScript provides several built-in conditional types that simplify common type operations.

### Exclude

`Exclude<T, U>` removes all types from `T` that are assignable to `U`.

```typescript
type Excluded = Exclude<"a" | "b" | "c", "a" | "b">; // "c"
```

### Extract

`Extract<T, U>` extracts all types from `T` that are assignable to `U`.

```typescript
type Extracted = Extract<"a" | "b" | "c", "a" | "b">; // "a" | "b"
```

### NonNullable

`NonNullable<T>` removes `null` and `undefined` from `T`.

```typescript
type NotNullable = NonNullable<string | null | undefined>; // string
```