Skip to main content

mapped-types

Mapped Types in TypeScript:

Certainly! Here are some concise TypeScript code examples illustrating each key point about mapped types:

Introduction to Mapped Types:

Mapped types enable you to create new types by transforming all properties of an existing type into new types, offering a flexible way to represent state variations without duplicating types.

  • Example:
// Original interface
interface Person {
name: string;
age: number;
}

// Mapped type that makes all properties optional
type PartialPerson = {
[Property in keyof Person]?: Person[Property];
};

Basics of Creating Mapped Types:

Mapped types use a syntax akin to a for-loop to iterate over properties of an existing type and can modify their names and types in the new mapped type.

  • Example:
// Mapped type that prefixes all property names with 'new_'
type PrefixedProperties<T> = {
[Property in keyof T as `new_${string & Property}`]: T[Property];
};

Using keyof with Mapped Types:

The keyof operator is used in mapped types to generate a union of property names from an existing type, which is then iterated over in the mapped type.

  • Example:
// Mapped type that creates a union of property names
type PropertyNames = keyof Person; // "name" | "age"

Modifying Property Types:

Mapped types can be used to change the property types of an existing type, such as converting all properties of an interface from one type to another.

  • Example:
// Mapped type that changes all property types to strings
type StringifiedProperties<T> = {
[Property in keyof T]: string;
};

Adding Modifiers to Properties:

Mapped types can also add or remove modifiers, allowing you to make all properties of a type readonly or optional.

  • Example:
// Mapped type that makes all properties readonly
type ReadonlyProperties<T> = {
readonly [Property in keyof T]: T[Property];
};

Using in with Mapped Types:

The in keyword is used within mapped types to iterate over the keys of a union created by keyof.

  • Example:
// Mapped type that transforms all properties to nullable
type NullableProperties<T> = {
[Property in keyof T]: T[Property] | null;
};

These examples showcase the versatility of mapped types in TypeScript, allowing developers to create new types based on existing ones with precision and control.

  • Utility Types as Mapped Types:

TypeScript's utility types like Partial<T> and Readonly<T> are practical examples of mapped types that make all properties of T optional or readonly.

interface Task {
id: number;
description: string;
}

type OptionalTask = Partial<Task>;
type ImmutableTask = Readonly<Task>;
  • Conditional Types with Mapped Types:

Combine mapped types with conditional types to create types that change based on certain conditions.

type ConditionalProperties<T> = {
[P in keyof T]: T[P] extends Function ? P : never;
};
  • Template Literal Types with Mapped Types:

Template literal types within mapped types allow you to manipulate property names.

type MappedWithTemplateLiterals<T> = {
[P in keyof T as `get${Capitalize<string & P>}`]: () => T[P]
};
  • Using Mapped Types for Defaults:

Mapped types can set default types for properties, establishing default states for types.

type Defaults<T> = {
[P in keyof T]?: T[P] extends Required<T>[P] ? T[P] : never;
};
  • Filtering Properties with Mapped Types:

Mapped types can filter which properties to include or exclude, analogous to filtering array elements.

type PickTask = Pick<Task, 'description'>; // Only the description property
type OmitTask = Omit<Task, 'id'>; // All except the id property
  • Mapped Types with Index Signatures:

Apply transformations to index signatures in types using mapped types.

type IndexSignatureMapped<T> = {
[P in keyof T]: T[P] extends infer R ? R : never;
};
  • Combining Mapped Types:

Compose multiple mapped types to create intricate type transformations.

type CombinedMapped<T> = Readonly<Partial<T>>;
  • Advanced Techniques with Mapped Types:

Mapped types can be combined with generics or type inferences for advanced and versatile type manipulations.

type AdvancedMapped<T> = {
[P in keyof T]: T[P] extends infer U ? { prop: U } : never;
};
  • Performance Considerations:

Use mapped types considerately to balance their compile-time impact and complexity in the codebase.

// Example omitted for brevity - consider performance impacts when designing types.