+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Part 68 of 354

๐Ÿ— ๏ธ Key Remapping: The Art of Property Name Transformation

Master TypeScript's key remapping in mapped types to create sophisticated property transformations and build advanced type utilities ๐ŸŽจ

๐Ÿš€Intermediate
25 min read

Prerequisites

  • Understanding of mapped types ๐ŸŽฏ
  • Knowledge of template literal types ๐Ÿ“
  • Familiarity with conditional types โšก

What you'll learn

  • Master key remapping syntax and patterns ๐Ÿ—๏ธ
  • Transform property names with precision ๐Ÿ”ง
  • Build advanced type transformation utilities ๐ŸŽจ
  • Create elegant API design patterns ๐Ÿ›

๐ŸŽฏ Introduction

Welcome to the sophisticated world of key remapping! ๐ŸŽ‰ If mapped types are like factories for transforming values, then key remapping is like having a magical name generator ๐Ÿท๏ธ that can transform property names according to any rule you can imagine!

Youโ€™re about to discover one of TypeScript 4.1โ€™s most powerful features - the ability to not just transform property values, but to completely rename properties during the transformation process. Whether youโ€™re building API adapters ๐ŸŒ, creating elegant utility types ๐Ÿ› ๏ธ, or designing sophisticated type-safe patterns ๐Ÿง™โ€โ™‚๏ธ, key remapping will give you unprecedented control over type structure.

By the end of this tutorial, youโ€™ll be renaming and reshaping type properties with the precision of a master architect! โœจ Letโ€™s unlock this transformative power! ๐Ÿš€

๐Ÿ“š Understanding Key Remapping

๐Ÿค” What is Key Remapping?

Key remapping allows you to change property names while creating mapped types. Think of it as a translator ๐Ÿ—ฃ๏ธ that can take any property name and transform it into something completely different based on your rules.

The syntax uses the as keyword:

type RemappedType<T> = {
  [K in keyof T as NewKeyName]: T[K]
}

This reads as: โ€œFor each key K in type T, create a new property named NewKeyName with the same value typeโ€

๐ŸŽจ Your First Key Remapping

Letโ€™s start with simple examples:

// ๐ŸŽฏ Original interface
interface User {
  id: number;
  name: string;
  email: string;
  age: number;
}

// ๐Ÿช„ Add prefix to all property names
type PrefixedUser<T> = {
  [K in keyof T as `user_${string & K}`]: T[K];
};

// ๐Ÿงช Testing our remapping
type DatabaseUser = PrefixedUser<User>;
// Result: {
//   user_id: number;
//   user_name: string;
//   user_email: string;
//   user_age: number;
// }

// ๐Ÿš€ Transform to getter method names
type GetterMethods<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};

// โœจ Testing getter transformation
type UserGetters = GetterMethods<User>;
// Result: {
//   getId: () => number;
//   getName: () => string;
//   getEmail: () => string;
//   getAge: () => number;
// }

๐Ÿ’ก The Magic: Key remapping lets you create completely new property names while preserving the value types!

๐Ÿ” Breaking Down the Syntax

type RemappedType<T> = {
  [K in keyof T as TransformKey<K>]: T[K]
//  ^   ^      ^   ^              ^    ^
//  |   |      |   |              |    โ””โ”€โ”€ Original value type
//  |   |      |   |              โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€ New key name
//  |   |      |   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ "as" keyword for remapping
//  |   |      โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ All keys of type T
//  |   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ "in" operator for iteration
//  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ The property key variable
}

๐Ÿ”ง Key Remapping Patterns

๐Ÿ“ Essential Transformation Recipes

Letโ€™s explore the most powerful key remapping patterns:

// ๐ŸŽจ Convert snake_case to camelCase
type SnakeToCamel<S extends string> = S extends `${infer T}_${infer U}`
  ? `${T}${Capitalize<SnakeToCamel<U>>}`
  : S;

type CamelCaseProps<T> = {
  [K in keyof T as SnakeToCamel<string & K>]: T[K];
};

// ๐Ÿงช Testing snake_case conversion
interface ApiResponse {
  user_id: number;
  full_name: string;
  email_address: string;
  created_at: Date;
  is_active: boolean;
}

type FrontendUser = CamelCaseProps<ApiResponse>;
// Result: {
//   userId: number;
//   fullName: string;
//   emailAddress: string;
//   createdAt: Date;
//   isActive: boolean;
// }

// ๐Ÿš€ Create event handler names
type EventHandlers<T> = {
  [K in keyof T as `on${Capitalize<string & K>}Change`]: (value: T[K]) => void;
};

// ๐ŸŽฎ Testing event handler generation
interface FormData {
  username: string;
  password: string;
  rememberMe: boolean;
}

type FormHandlers = EventHandlers<FormData>;
// Result: {
//   onUsernameChange: (value: string) => void;
//   onPasswordChange: (value: string) => void;
//   onRememberMeChange: (value: boolean) => void;
// }

// โœจ Filter and rename properties conditionally
type StringPropsOnly<T> = {
  [K in keyof T as T[K] extends string ? `str_${string & K}` : never]: T[K];
};

// ๐Ÿงช Testing conditional filtering
interface MixedProps {
  name: string;      // Will be included as str_name
  age: number;       // Will be excluded
  description: string; // Will be included as str_description
  active: boolean;   // Will be excluded
}

type OnlyStringProps = StringPropsOnly<MixedProps>;
// Result: {
//   str_name: string;
//   str_description: string;
// }

๐ŸŽฏ Advanced Remapping Techniques

// ๐ŸŽจ Create both getter and setter methods
type AccessorMethods<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
} & {
  [K in keyof T as `set${Capitalize<string & K>}`]: (value: T[K]) => void;
};

// ๐Ÿงช Testing accessor generation
type UserAccessors = AccessorMethods<User>;
// Result: Complete getter/setter interface!

// ๐Ÿš€ Transform property names based on value types
type TypePrefixedProps<T> = {
  [K in keyof T as T[K] extends string 
    ? `str_${string & K}`
    : T[K] extends number
      ? `num_${string & K}`
      : T[K] extends boolean
        ? `bool_${string & K}`
        : `obj_${string & K}`
  ]: T[K];
};

// ๐ŸŽฎ Testing type-based prefixing
interface ComplexObject {
  title: string;
  count: number;
  enabled: boolean;
  metadata: object;
}

type PrefixedComplex = TypePrefixedProps<ComplexObject>;
// Result: {
//   str_title: string;
//   num_count: number;
//   bool_enabled: boolean;
//   obj_metadata: object;
// }

// โœจ Create validation method names
type ValidationMethods<T> = {
  [K in keyof T as `validate${Capitalize<string & K>}`]: (value: T[K]) => boolean;
} & {
  [K in keyof T as `${string & K}Error`]: string | null;
};

type UserValidation = ValidationMethods<User>;
// Result: Both validation methods and error properties!

๐Ÿ’ก Practical Examples

๐Ÿ›’ Example 1: Database-to-Frontend Mapper

Letโ€™s build a sophisticated data transformation system:

// ๐ŸŽฏ Database schema with snake_case naming
interface DatabaseUser {
  user_id: number;
  first_name: string;
  last_name: string;
  email_address: string;
  phone_number: string | null;
  date_of_birth: string;  // ISO string
  created_at: string;     // ISO timestamp
  updated_at: string;     // ISO timestamp
  is_active: boolean;
  profile_picture_url: string | null;
}

interface DatabasePost {
  post_id: number;
  author_id: number;
  post_title: string;
  post_content: string;
  published_at: string | null;
  view_count: number;
  like_count: number;
  is_featured: boolean;
  created_at: string;
  updated_at: string;
}

// ๐Ÿš€ Advanced snake_case to camelCase conversion
type SnakeToCamelCase<S extends string> = 
  S extends `${infer T}_${infer U}` 
    ? `${T}${Capitalize<SnakeToCamelCase<U>>}`
    : S;

// ๐ŸŽจ Transform database types to frontend types
type DatabaseToFrontend<T> = {
  [K in keyof T as SnakeToCamelCase<string & K>]: 
    // Transform date strings to Date objects
    T[K] extends string 
      ? K extends `${string}_at` | `date_of_birth` | `published_at`
        ? Date | (T[K] extends null ? null : never)
        : T[K]
      : T[K];
};

// โœจ Create update payload types (exclude auto-generated fields)
type CreateUpdatePayload<T> = {
  [K in keyof T as K extends 'created_at' | 'updated_at' 
    ? never 
    : SnakeToCamelCase<string & K>
  ]: T[K] extends string 
    ? K extends `${string}_at` | `date_of_birth`
      ? Date | (T[K] extends null ? null : never)
      : T[K]
    : T[K];
};

// ๐Ÿงช Testing transformations
type FrontendUser = DatabaseToFrontend<DatabaseUser>;
// Result: {
//   userId: number;
//   firstName: string;
//   lastName: string;
//   emailAddress: string;
//   phoneNumber: string | null;
//   dateOfBirth: Date;
//   createdAt: Date;
//   updatedAt: Date;
//   isActive: boolean;
//   profilePictureUrl: string | null;
// }

type UserUpdatePayload = CreateUpdatePayload<DatabaseUser>;
// Result: Same as above but without createdAt and updatedAt

// ๐ŸŽฎ Type-safe data mapper
class DatabaseMapper {
  // ๐Ÿš€ Transform user from database to frontend format
  transformUser(dbUser: DatabaseUser): FrontendUser {
    return {
      userId: dbUser.user_id,
      firstName: dbUser.first_name,
      lastName: dbUser.last_name,
      emailAddress: dbUser.email_address,
      phoneNumber: dbUser.phone_number,
      dateOfBirth: new Date(dbUser.date_of_birth),
      createdAt: new Date(dbUser.created_at),
      updatedAt: new Date(dbUser.updated_at),
      isActive: dbUser.is_active,
      profilePictureUrl: dbUser.profile_picture_url
    } as FrontendUser;
  }

  // ๐ŸŽฏ Transform update payload to database format
  transformUpdatePayload(updateData: UserUpdatePayload): Partial<DatabaseUser> {
    const dbUpdate: Partial<DatabaseUser> = {};
    
    if ('userId' in updateData) dbUpdate.user_id = updateData.userId;
    if ('firstName' in updateData) dbUpdate.first_name = updateData.firstName;
    if ('lastName' in updateData) dbUpdate.last_name = updateData.lastName;
    if ('emailAddress' in updateData) dbUpdate.email_address = updateData.emailAddress;
    if ('phoneNumber' in updateData) dbUpdate.phone_number = updateData.phoneNumber;
    if ('dateOfBirth' in updateData && updateData.dateOfBirth) {
      dbUpdate.date_of_birth = updateData.dateOfBirth.toISOString();
    }
    if ('isActive' in updateData) dbUpdate.is_active = updateData.isActive;
    if ('profilePictureUrl' in updateData) dbUpdate.profile_picture_url = updateData.profilePictureUrl;
    
    return dbUpdate;
  }

  // โœจ Generic transformation method
  transformCollection<T, U>(
    items: T[], 
    transformer: (item: T) => U
  ): U[] {
    return items.map(transformer);
  }
}

// ๐Ÿงช Usage example
const mapper = new DatabaseMapper();

const dbUser: DatabaseUser = {
  user_id: 123,
  first_name: "Alice",
  last_name: "Wonder",
  email_address: "[email protected]",
  phone_number: "+1-555-0123",
  date_of_birth: "1990-05-15T00:00:00Z",
  created_at: "2023-01-15T10:30:00Z",
  updated_at: "2023-12-01T14:45:00Z",
  is_active: true,
  profile_picture_url: "https://example.com/avatar.jpg"
};

const frontendUser = mapper.transformUser(dbUser);
console.log("๐ŸŽ‰ Transformed user:", frontendUser);
// TypeScript knows all property names are camelCase and dates are Date objects!

๐ŸŽฎ Example 2: React Component Props Generator

Letโ€™s create a system for generating component prop interfaces:

// ๐Ÿ† Base component configuration
interface ComponentConfig {
  title: string;
  description: string;
  variant: 'primary' | 'secondary' | 'danger';
  size: 'small' | 'medium' | 'large';
  disabled: boolean;
  loading: boolean;
  icon: string | null;
}

interface FormConfig {
  placeholder: string;
  required: boolean;
  validation: string;
  errorMessage: string;
  helpText: string;
}

// ๐ŸŽฏ Generate prop names with different patterns
type ComponentProps<T> = {
  // Regular props
  [K in keyof T]: T[K];
} & {
  // Default value props
  [K in keyof T as `default${Capitalize<string & K>}`]?: T[K];
} & {
  // Callback props for value changes
  [K in keyof T as `on${Capitalize<string & K>}Change`]?: (value: T[K]) => void;
} & {
  // Override props for conditional rendering
  [K in keyof T as `override${Capitalize<string & K>}`]?: T[K];
};

// ๐Ÿš€ Generate form field props
type FormFieldProps<T> = {
  // Field values
  [K in keyof T]: T[K];
} & {
  // Field change handlers
  [K in keyof T as `handle${Capitalize<string & K>}Change`]: (value: T[K]) => void;
} & {
  // Field validation functions
  [K in keyof T as `validate${Capitalize<string & K>}`]?: (value: T[K]) => string | null;
} & {
  // Field error states
  [K in keyof T as `${string & K}Error`]?: string;
} & {
  // Field touched states
  [K in keyof T as `${string & K}Touched`]?: boolean;
};

// ๐ŸŽจ Create theme props for styling
type ThemeProps<T> = {
  [K in keyof T as `${string & K}Color`]?: string;
} & {
  [K in keyof T as `${string & K}Size`]?: number | string;
} & {
  [K in keyof T as `${string & K}Style`]?: React.CSSProperties;
};

// โœจ Generate responsive props
type ResponsiveProps<T> = {
  [K in keyof T]: T[K];
} & {
  [K in keyof T as `${string & K}Sm`]?: T[K];  // Small screens
} & {
  [K in keyof T as `${string & K}Md`]?: T[K];  // Medium screens
} & {
  [K in keyof T as `${string & K}Lg`]?: T[K];  // Large screens
} & {
  [K in keyof T as `${string & K}Xl`]?: T[K];  // Extra large screens
};

// ๐Ÿงช Testing component prop generation
type ButtonProps = ComponentProps<ComponentConfig>;
// Result: Huge interface with all variants of props!

type FormFieldInterface = FormFieldProps<FormConfig>;
// Result: Complete form field management interface!

// ๐ŸŽฎ React component using generated props
interface BaseButtonConfig {
  variant: 'primary' | 'secondary';
  size: 'small' | 'large';
  disabled: boolean;
}

type GeneratedButtonProps = ComponentProps<BaseButtonConfig> & {
  children: React.ReactNode;
  onClick?: () => void;
};

// ๐Ÿš€ Smart button component
const SmartButton: React.FC<GeneratedButtonProps> = ({
  variant,
  size,
  disabled,
  defaultVariant = 'primary',
  defaultSize = 'small',
  defaultDisabled = false,
  onVariantChange,
  onSizeChange,
  onDisabledChange,
  overrideVariant,
  overrideSize,
  overrideDisabled,
  children,
  onClick
}) => {
  // Component logic using all the generated props
  const actualVariant = overrideVariant ?? variant ?? defaultVariant;
  const actualSize = overrideSize ?? size ?? defaultSize;
  const actualDisabled = overrideDisabled ?? disabled ?? defaultDisabled;
  
  return (
    <button 
      className={`btn btn-${actualVariant} btn-${actualSize}`}
      disabled={actualDisabled}
      onClick={onClick}
    >
      {children}
    </button>
  );
};

// ๐Ÿงช Usage with full type safety
const App = () => {
  return (
    <SmartButton
      variant="primary"
      size="large"
      disabled={false}
      defaultVariant="secondary"  // TypeScript knows this exists!
      onVariantChange={(newVariant) => {
        console.log("Variant changed:", newVariant);
      }}
      overrideSize="small"        // TypeScript knows this exists!
    >
      Click me! ๐ŸŽ‰
    </SmartButton>
  );
};

๐Ÿš€ Advanced Key Remapping Techniques

๐Ÿง™โ€โ™‚๏ธ Conditional Key Filtering

// ๐ŸŽจ Keep only certain types of properties
type FilterByType<T, U> = {
  [K in keyof T as T[K] extends U ? K : never]: T[K];
};

// ๐Ÿงช Testing type filtering
interface MixedInterface {
  name: string;
  age: number;
  active: boolean;
  callback: () => void;
  data: object;
}

type OnlyFunctions = FilterByType<MixedInterface, Function>;
// Result: { callback: () => void }

type OnlyPrimitives = FilterByType<MixedInterface, string | number | boolean>;
// Result: { name: string; age: number; active: boolean }

// ๐Ÿš€ Remove properties by pattern
type OmitByPattern<T, Pattern extends string> = {
  [K in keyof T as K extends `${Pattern}${string}` ? never : K]: T[K];
};

// ๐ŸŽฎ Testing pattern filtering
interface ComponentWithPrivates {
  _internalState: any;
  _privateMethod: () => void;
  publicProp: string;
  publicMethod: () => string;
}

type PublicOnly = OmitByPattern<ComponentWithPrivates, '_'>;
// Result: { publicProp: string; publicMethod: () => string }

๐Ÿ—๏ธ Complex Key Transformations

// ๐ŸŽฏ Create pluralized collection methods
type CollectionMethods<T> = {
  [K in keyof T as `get${Capitalize<string & K>}s`]: () => T[K][];
} & {
  [K in keyof T as `add${Capitalize<string & K>}`]: (item: T[K]) => void;
} & {
  [K in keyof T as `remove${Capitalize<string & K>}`]: (item: T[K]) => void;
} & {
  [K in keyof T as `clear${Capitalize<string & K>}s`]: () => void;
};

// ๐Ÿงช Testing collection methods
interface EntityTypes {
  user: { id: number; name: string };
  post: { id: number; title: string };
  comment: { id: number; content: string };
}

type EntityManager = CollectionMethods<EntityTypes>;
// Result: {
//   getUsers: () => { id: number; name: string }[];
//   addUser: (item: { id: number; name: string }) => void;
//   removeUser: (item: { id: number; name: string }) => void;
//   clearUsers: () => void;
//   getPosts: () => { id: number; title: string }[];
//   addPost: (item: { id: number; title: string }) => void;
//   // ... and so on for all entity types
// }

// โœจ Create nested property accessors
type NestedAccessors<T, Prefix extends string = ''> = {
  [K in keyof T as `${Prefix}${Capitalize<string & K>}`]: T[K] extends object
    ? NestedAccessors<T[K], `${Prefix}${Capitalize<string & K>}`>
    : () => T[K];
};

// ๐ŸŽฎ Testing nested accessors
interface ComplexConfig {
  database: {
    host: string;
    port: number;
    credentials: {
      username: string;
      password: string;
    };
  };
  api: {
    baseUrl: string;
    timeout: number;
  };
}

type ConfigAccessors = NestedAccessors<ComplexConfig>;
// Result: Deeply nested accessor methods for all properties!

โš ๏ธ Common Pitfalls and Solutions

๐Ÿ˜ฑ Pitfall 1: Key Type Constraints

// โŒ Forgetting string constraint
type BadRemapping<T> = {
  [K in keyof T as `prefix_${K}`]: T[K];  // Error! K might not be string
};

// โœ… Proper string constraint
type GoodRemapping<T> = {
  [K in keyof T as `prefix_${string & K}`]: T[K];  // Works correctly
};

// โœ… Alternative with conditional
type SafeRemapping<T> = {
  [K in keyof T as K extends string ? `prefix_${K}` : never]: T[K];
};

๐Ÿคฏ Pitfall 2: Never Values Disappearing

// โŒ Properties becoming never disappear
type FilterProblematic<T> = {
  [K in keyof T as T[K] extends string ? K : never]: T[K];
};

interface TestInterface {
  name: string;     // Kept
  age: number;      // Becomes never, disappears!
  active: boolean;  // Becomes never, disappears!
}

type Filtered = FilterProblematic<TestInterface>;
// Result: { name: string } - other properties are gone!

// โœ… This is actually the intended behavior for filtering
// If you want to keep all properties, don't use conditional keys:
type KeepAllProps<T> = {
  [K in keyof T]: T[K] extends string ? T[K] : `converted_${string}`;
};

๐Ÿ” Pitfall 3: Template Literal Complexity

// โŒ Overly complex template literals
type OverlyComplex<T> = {
  [K in keyof T as `${string & K}_${T[K] extends string ? 'str' : T[K] extends number ? 'num' : 'other'}_${string & K}_property`]: T[K];
};

// โœ… Break down complex transformations
type GetTypeSuffix<T> = T extends string ? 'str' : T extends number ? 'num' : 'other';

type CleanerApproach<T> = {
  [K in keyof T as `${string & K}_${GetTypeSuffix<T[K]>}_property`]: T[K];
};

๐Ÿ› ๏ธ Best Practices

  1. ๐ŸŽฏ Use String Constraints: Always use string & K for template literals
  2. ๐Ÿ“ Break Down Complex Logic: Use helper types for readability
  3. ๐Ÿ›ก๏ธ Test Edge Cases: Consider never, unknown, and union types
  4. ๐ŸŽจ Be Consistent: Follow naming conventions in your remapping patterns
  5. โœจ Document Your Patterns: Complex remapping can be hard to understand later
  6. ๐Ÿ” Consider Performance: Very complex remapping can slow down type checking

๐Ÿงช Hands-On Exercise

๐ŸŽฏ Challenge: Build a Type-Safe State Management System

Create a sophisticated state management system using key remapping:

๐Ÿ“‹ Requirements:

  • โœ… Generate action creators from state interface
  • ๐Ÿ”ง Create reducer methods for each state property
  • ๐ŸŽจ Build selector functions with memoization types
  • ๐Ÿ“Š Type-safe subscription system
  • โœจ Middleware support with type safety!

๐Ÿš€ Bonus Points:

  • Add time-travel debugging types
  • Create dev tools integration
  • Build persistence layer types

๐Ÿ’ก Solution

๐Ÿ” Click to see solution
// ๐ŸŽฏ Application state interface
interface AppState {
  user: {
    id: number;
    name: string;
    email: string;
    preferences: {
      theme: 'light' | 'dark';
      language: string;
      notifications: boolean;
    };
  } | null;
  posts: Array<{
    id: number;
    title: string;
    content: string;
    author: string;
    published: boolean;
  }>;
  ui: {
    loading: boolean;
    error: string | null;
    currentPage: string;
    sidebarOpen: boolean;
  };
  settings: {
    apiUrl: string;
    timeout: number;
    retryAttempts: number;
  };
}

// ๐Ÿš€ Generate action creators
type ActionCreators<T> = {
  [K in keyof T as `set${Capitalize<string & K>}`]: (payload: T[K]) => {
    type: `SET_${Uppercase<string & K>}`;
    payload: T[K];
  };
} & {
  [K in keyof T as `update${Capitalize<string & K>}`]: (updater: (current: T[K]) => T[K]) => {
    type: `UPDATE_${Uppercase<string & K>}`;
    updater: (current: T[K]) => T[K];
  };
} & {
  [K in keyof T as `reset${Capitalize<string & K>}`]: () => {
    type: `RESET_${Uppercase<string & K>}`;
  };
};

// ๐ŸŽจ Generate selectors
type Selectors<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: (state: T) => T[K];
} & {
  [K in keyof T as `use${Capitalize<string & K>}`]: () => T[K];  // React hooks
};

// โœจ Generate action types
type ActionTypes<T> = {
  [K in keyof T as `SET_${Uppercase<string & K>}`]: `SET_${Uppercase<string & K>}`;
} & {
  [K in keyof T as `UPDATE_${Uppercase<string & K>}`]: `UPDATE_${Uppercase<string & K>}`;
} & {
  [K in keyof T as `RESET_${Uppercase<string & K>}`]: `RESET_${Uppercase<string & K>}`;
};

// ๐Ÿงช Generate all action objects
type Actions<T> = {
  [K in keyof ActionCreators<T>]: ReturnType<ActionCreators<T>[K]>;
}[keyof ActionCreators<T>];

// ๐ŸŽฎ Type-safe store implementation
class TypeSafeStore<T extends Record<string, any>> {
  private state: T;
  private listeners: Array<(state: T) => void> = [];
  private middlewares: Array<(action: Actions<T>, state: T) => Actions<T>> = [];

  constructor(initialState: T) {
    this.state = initialState;
  }

  // ๐Ÿš€ Generate action creators dynamically
  createActions(): ActionCreators<T> {
    const actions = {} as ActionCreators<T>;

    for (const key in this.state) {
      const capitalizedKey = key.charAt(0).toUpperCase() + key.slice(1);
      
      // Set action
      const setKey = `set${capitalizedKey}` as keyof ActionCreators<T>;
      (actions as any)[setKey] = (payload: T[typeof key]) => ({
        type: `SET_${key.toUpperCase()}`,
        payload
      });

      // Update action
      const updateKey = `update${capitalizedKey}` as keyof ActionCreators<T>;
      (actions as any)[updateKey] = (updater: (current: T[typeof key]) => T[typeof key]) => ({
        type: `UPDATE_${key.toUpperCase()}`,
        updater
      });

      // Reset action
      const resetKey = `reset${capitalizedKey}` as keyof ActionCreators<T>;
      (actions as any)[resetKey] = () => ({
        type: `RESET_${key.toUpperCase()}`
      });
    }

    return actions;
  }

  // ๐ŸŽฏ Generate selectors dynamically
  createSelectors(): Selectors<T> {
    const selectors = {} as Selectors<T>;

    for (const key in this.state) {
      const capitalizedKey = key.charAt(0).toUpperCase() + key.slice(1);
      
      // Get selector
      const getKey = `get${capitalizedKey}` as keyof Selectors<T>;
      (selectors as any)[getKey] = (state: T) => state[key];

      // React hook selector
      const useKey = `use${capitalizedKey}` as keyof Selectors<T>;
      (selectors as any)[useKey] = () => this.state[key];
    }

    return selectors;
  }

  // โœจ Dispatch actions with middleware
  dispatch(action: Actions<T>): void {
    // Apply middlewares
    let processedAction = action;
    for (const middleware of this.middlewares) {
      processedAction = middleware(processedAction, this.state);
    }

    // Update state based on action type
    const actionType = processedAction.type;
    const key = actionType.split('_')[1]?.toLowerCase();

    if (!key || !(key in this.state)) return;

    if (actionType.startsWith('SET_')) {
      this.state = {
        ...this.state,
        [key]: (processedAction as any).payload
      };
    } else if (actionType.startsWith('UPDATE_')) {
      this.state = {
        ...this.state,
        [key]: (processedAction as any).updater(this.state[key as keyof T])
      };
    } else if (actionType.startsWith('RESET_')) {
      // Would need initial state reference for reset
      console.log(`Resetting ${key}`);
    }

    // Notify listeners
    this.listeners.forEach(listener => listener(this.state));
  }

  // ๐ŸŽฎ Subscribe to state changes
  subscribe(listener: (state: T) => void): () => void {
    this.listeners.push(listener);
    return () => {
      const index = this.listeners.indexOf(listener);
      if (index > -1) {
        this.listeners.splice(index, 1);
      }
    };
  }

  // ๐Ÿš€ Add middleware
  use(middleware: (action: Actions<T>, state: T) => Actions<T>): void {
    this.middlewares.push(middleware);
  }

  // โœจ Get current state
  getState(): T {
    return { ...this.state };
  }
}

// ๐Ÿงช Usage example
const store = new TypeSafeStore<AppState>({
  user: null,
  posts: [],
  ui: {
    loading: false,
    error: null,
    currentPage: 'home',
    sidebarOpen: false
  },
  settings: {
    apiUrl: 'https://api.example.com',
    timeout: 5000,
    retryAttempts: 3
  }
});

// ๐ŸŽฏ Create type-safe actions and selectors
const actions = store.createActions();
const selectors = store.createSelectors();

// โœ… All of these are fully type-safe!
const setUserAction = actions.setUser({
  id: 1,
  name: "Alice",
  email: "[email protected]",
  preferences: {
    theme: 'dark',
    language: 'en',
    notifications: true
  }
});

const updateUIAction = actions.updateUi(current => ({
  ...current,
  loading: true,
  currentPage: 'profile'
}));

// ๐Ÿš€ Dispatch actions
store.dispatch(setUserAction);
store.dispatch(updateUIAction);

// ๐ŸŽฎ Use selectors
const currentUser = selectors.getUser(store.getState());
const uiState = selectors.getUi(store.getState());

console.log("๐ŸŽ‰ Type-safe state management!", { currentUser, uiState });

// โœจ Subscribe to changes
const unsubscribe = store.subscribe((newState) => {
  console.log("๐Ÿ“Š State updated:", newState);
});

// ๐Ÿงช Middleware example
store.use((action, state) => {
  console.log(`๐Ÿ” Action dispatched: ${action.type}`);
  return action; // Could transform action here
});

console.log("๐ŸŽฏ Type-safe store setup complete!");

๐ŸŽ“ Key Takeaways

Youโ€™ve mastered key remapping! Hereโ€™s what you can now do:

  • โœ… Transform property names with surgical precision using advanced patterns ๐Ÿ’ช
  • โœ… Build sophisticated type utilities that reshape entire interfaces ๐Ÿ›ก๏ธ
  • โœ… Create elegant API design patterns with perfect type safety ๐ŸŽฏ
  • โœ… Filter and transform properties conditionally like a pro ๐Ÿ›
  • โœ… Design scalable type systems that adapt to any structure ๐Ÿš€

Remember: Key remapping is like having a master key ๐Ÿ—๏ธ that can unlock and reshape any type structure according to your vision!

๐Ÿค Next Steps

Congratulations! ๐ŸŽ‰ Youโ€™ve conquered key remapping in mapped types!

Hereโ€™s what to explore next:

  1. ๐Ÿ’ป Practice with the state management exercise above
  2. ๐Ÿ—๏ธ Build your own key remapping utilities for real projects
  3. ๐Ÿ“š Move on to our next tutorial: Template Literal Types: String Manipulation at Type Level
  4. ๐ŸŒŸ Share your key remapping patterns with the TypeScript community!

You now possess one of TypeScriptโ€™s most sophisticated transformation tools. Use it to create type systems that are both powerful and elegant. Remember - every advanced TypeScript architect started with curiosity about types. Keep experimenting, keep building, and most importantly, have fun reshaping the type world! ๐Ÿš€โœจ


Happy key remapping! ๐ŸŽ‰๐Ÿ—๏ธโœจ