# Creating Custom Utility Types in TypeScript

While TypeScript provides many built-in utility types (like `Partial`, `Pick`, `Omit`), you often need to create your own to handle specific type transformations in your application. Custom utility types are essentially **generic type aliases** that use advanced features like Mapped Types, Conditional Types, and `infer`.

## 1. The Basics: Generic Type Aliases

A utility type is just a type that takes another type as an argument.

```typescript
// A simple utility that makes a type nullable
type Nullable<T> = T | null;

const s: Nullable<string> = "hello"; // string | null
```

## 2. Using Mapped Types

Mapped types allow you to iterate over keys of an object.

### Example: `Mutable<T>`

Removes `readonly` modifier from all properties.

```typescript
type Mutable<T> = {
  -readonly [K in keyof T]: T[K];
};

interface Locked {
  readonly id: number;
}

type Unlocked = Mutable<Locked>; // { id: number }
```

## 3. Using Conditional Types

Conditional types allow you to filter or select types based on logic (`T extends U ? X : Y`).

### Example: `Diff<T, U>`

Removes types from `T` that are present in `U`.

```typescript
type Diff<T, U> = T extends U ? never : T;

type Letters = "a" | "b" | "c";
type RemoveC = Diff<Letters, "c">; // "a" | "b"
```

## 4. Using `infer`

The `infer` keyword allows you to extract a type from within another type.

### Example: `UnwrapPromise<T>`

If `T` is a Promise, extract the type inside. Otherwise, return `T`.

```typescript
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;

type A = UnwrapPromise<Promise<string>>; // string
type B = UnwrapPromise<number>;          // number
```

### Example: `ArgumentTypes<T>`

Extract arguments of a function as a tuple.

```typescript
type ArgumentTypes<F extends Function> = F extends (...args: infer A) => any ? A : never;

function greet(name: string, age: number) {}

type Params = ArgumentTypes<typeof greet>; // [string, number]
```

## 5. Combining Techniques (Key Remapping)

Real-world utility types often combine these features. For example, picking properties based on their value type.

```typescript
type PickByType<T, ValueType> = {
  [K in keyof T as T[K] extends ValueType ? K : never]: T[K];
};

interface User { id: number; name: string; isAdmin: boolean; age: number; }

// Only keep number properties: { id: number; age: number; }
type NumberProps = PickByType<User, number>; 
```

[[programming/javascript/typescript/typescript]]
[[programming/javascript/typescript/advanced-types]]
[[programming/javascript/typescript/utility-types]]