+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Part 66 of 72

๐Ÿ” Infer Keyword: Type Detective Magic

Master the infer keyword to extract and capture types from complex type structures like a TypeScript detective ๐Ÿ•ต๏ธโ€โ™€๏ธ

๐Ÿš€Intermediate
25 min read

Prerequisites

  • Understanding of conditional types ๐ŸŽฏ
  • Knowledge of generics ๐Ÿ“
  • Familiarity with type patterns โšก

What you'll learn

  • Master the infer keyword syntax ๐Ÿ—๏ธ
  • Extract types from complex structures ๐Ÿ”ง
  • Build powerful type utilities ๐ŸŽจ
  • Debug type inference like a pro ๐Ÿ›

๐ŸŽฏ Introduction

Welcome to the world of the infer keyword! ๐ŸŽ‰ Think of infer as TypeScriptโ€™s ultimate detective tool ๐Ÿ•ต๏ธโ€โ™€๏ธ - it can examine any type structure and extract exactly what you need, like finding a needle in a haystack โœจ.

Youโ€™re about to master one of TypeScriptโ€™s most elegant and powerful features. Whether youโ€™re building utility types ๐Ÿ› ๏ธ, parsing complex APIs ๐ŸŒ, or creating type-safe abstractions ๐Ÿ—๏ธ, the infer keyword will become your best friend.

By the end of this tutorial, youโ€™ll be extracting types with surgical precision and creating utilities that feel like magic! ๐Ÿช„ Letโ€™s embark on this detective journey! ๐Ÿš€

๐Ÿ“š Understanding Infer

๐Ÿค” What is the Infer Keyword?

Think of infer as a type-level capture tool ๐Ÿ“ธ. Itโ€™s like saying to TypeScript: โ€œHey, when you match this pattern, please remember what type was in this specific spot so I can use it later!โ€

The infer keyword can only be used within conditional types, and it creates a type variable that captures part of the type being matched:

// ๐ŸŽฏ Basic infer pattern
type ExtractReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

// ๐Ÿงช Let's see it in action!
type FunctionReturn1 = ExtractReturnType<() => string>;        // string
type FunctionReturn2 = ExtractReturnType<(x: number) => boolean>; // boolean
type FunctionReturn3 = ExtractReturnType<string>;              // never (not a function)

๐Ÿ’ก The Magic: infer R says โ€œcapture whatever the return type is and call it Rโ€!

๐ŸŽจ Infer in Action

Letโ€™s see how infer works its magic:

// ๐Ÿš€ Extract array element type
type ArrayElement<T> = T extends (infer U)[] ? U : never;

// ๐Ÿงช Testing array element extraction
type StringElement = ArrayElement<string[]>;     // string
type NumberElement = ArrayElement<number[]>;     // number
type ObjectElement = ArrayElement<{id: string}[]>; // {id: string}
type NotArray = ArrayElement<string>;            // never

// ๐ŸŽฎ Extract promise value type
type PromiseValue<T> = T extends Promise<infer U> ? U : never;

// โœจ Testing promise extraction
type StringPromise = PromiseValue<Promise<string>>;   // string
type UserPromise = PromiseValue<Promise<{name: string}>>; // {name: string}
type NotPromise = PromiseValue<string>;               // never

๐Ÿ” Key Insight: infer creates a type variable that captures the matched part of the pattern!

๐Ÿ”ง Basic Infer Patterns

๐Ÿ“ Essential Infer Recipes

Letโ€™s explore the most useful infer patterns:

// ๐ŸŽจ Extract function parameters
type Parameters<T> = T extends (...args: infer P) => any ? P : never;

// ๐Ÿงช Testing parameter extraction
type LoginParams = Parameters<(username: string, password: string) => boolean>;
// Result: [username: string, password: string]

type NoParams = Parameters<() => void>;
// Result: []

// ๐Ÿš€ Extract object property types
type PropertyType<T, K extends keyof T> = T extends { [key in K]: infer U } ? U : never;

// ๐ŸŽฎ Testing property extraction
interface User {
  name: string;
  age: number;
  email: string;
}

type NameType = PropertyType<User, 'name'>;  // string
type AgeType = PropertyType<User, 'age'>;    // number

// โœจ Extract first element of tuple
type Head<T> = T extends [infer H, ...any[]] ? H : never;

// ๐Ÿงช Testing head extraction
type FirstString = Head<[string, number, boolean]>;  // string
type FirstNumber = Head<[number]>;                   // number
type EmptyHead = Head<[]>;                          // never

๐ŸŽฏ Advanced Infer Tricks

// ๐ŸŽจ Extract last element of tuple
type Tail<T> = T extends [...any[], infer L] ? L : never;

// ๐Ÿงช Testing tail extraction
type LastBoolean = Tail<[string, number, boolean]>;  // boolean
type LastOnly = Tail<[string]>;                      // string
type EmptyTail = Tail<[]>;                          // never

// ๐Ÿš€ Extract middle elements (without first and last)
type Middle<T> = T extends [any, ...infer M, any] ? M : never;

// ๐ŸŽฎ Testing middle extraction
type MiddleElements = Middle<[string, number, boolean, Date]>;  // [number, boolean]
type TwoElements = Middle<[string, number]>;                   // []
type OneElement = Middle<[string]>;                           // never

// โœจ Extract nested property type
type DeepPropertyType<T, K extends string> = 
  K extends `${infer Head}.${infer Tail}`
    ? T extends { [key in Head]: infer U }
      ? DeepPropertyType<U, Tail>
      : never
    : T extends { [key in K]: infer U }
      ? U
      : never;

// ๐Ÿงช Testing deep property extraction
interface NestedUser {
  profile: {
    personal: {
      name: string;
      age: number;
    };
    settings: {
      theme: "light" | "dark";
    };
  };
}

type DeepName = DeepPropertyType<NestedUser, "profile.personal.name">; // string
type DeepTheme = DeepPropertyType<NestedUser, "profile.settings.theme">; // "light" | "dark"

๐Ÿ’ก Practical Examples

๐Ÿ›’ Example 1: Smart API Response Parser

Letโ€™s build a type-safe API response parser using infer:

// ๐ŸŽฏ Define different API response patterns
interface ApiSuccess<T> {
  status: "success";
  data: T;
  metadata?: {
    timestamp: string;
    requestId: string;
  };
}

interface ApiError {
  status: "error";
  message: string;
  code: number;
  details?: string[];
}

interface ApiPaginated<T> {
  status: "success";
  data: T[];
  pagination: {
    page: number;
    limit: number;
    total: number;
    hasMore: boolean;
  };
}

// ๐Ÿš€ Smart type extractors using infer
type ExtractApiData<T> = T extends ApiSuccess<infer U>
  ? U
  : T extends ApiPaginated<infer U>
    ? U[]
    : never;

type ExtractApiError<T> = T extends ApiError
  ? { message: string; code: number; details?: string[] }
  : never;

type ExtractPaginationInfo<T> = T extends ApiPaginated<any>
  ? T extends { pagination: infer P }
    ? P
    : never
  : never;

// ๐Ÿงช Testing our extractors
type UserApiResponse = ApiSuccess<{id: string; name: string; email: string}>;
type ProductListResponse = ApiPaginated<{id: string; title: string; price: number}>;
type ErrorResponse = ApiError;

type UserData = ExtractApiData<UserApiResponse>;
// Result: {id: string; name: string; email: string}

type ProductData = ExtractApiData<ProductListResponse>;
// Result: {id: string; title: string; price: number}[]

type ErrorInfo = ExtractApiError<ErrorResponse>;
// Result: { message: string; code: number; details?: string[] }

type PaginationInfo = ExtractPaginationInfo<ProductListResponse>;
// Result: { page: number; limit: number; total: number; hasMore: boolean }

// ๐ŸŽฎ Smart API response handler
class ApiResponseHandler {
  // โœจ Generic data extractor
  extractData<T>(response: T): ExtractApiData<T> {
    if (this.isSuccess(response)) {
      return (response as any).data;
    }
    if (this.isPaginated(response)) {
      return (response as any).data;
    }
    throw new Error("Cannot extract data from this response type ๐Ÿ˜ข");
  }

  // ๐ŸŽฏ Type guards with infer-powered return types
  isSuccess<T>(response: T): response is T extends ApiSuccess<any> ? T : never {
    return (response as any).status === "success" && 
           "data" in response && 
           !("pagination" in response);
  }

  isPaginated<T>(response: T): response is T extends ApiPaginated<any> ? T : never {
    return (response as any).status === "success" && 
           "pagination" in response;
  }

  isError<T>(response: T): response is T extends ApiError ? T : never {
    return (response as any).status === "error";
  }

  // ๐Ÿš€ Smart response processor
  processResponse<T>(response: T): void {
    if (this.isSuccess(response)) {
      const data = this.extractData(response);
      console.log("โœ… Success data:", data);
    } else if (this.isPaginated(response)) {
      const data = this.extractData(response);
      const pagination = response as any as ApiPaginated<any>;
      console.log("๐Ÿ“Š Paginated data:", data);
      console.log("๐Ÿ“„ Pagination:", pagination.pagination);
    } else if (this.isError(response)) {
      console.log("โŒ Error:", response.message);
      console.log("๐Ÿ”ข Code:", response.code);
    }
  }
}

// ๐Ÿงช Usage example
const apiHandler = new ApiResponseHandler();

const userResponse: ApiSuccess<{id: string; name: string}> = {
  status: "success",
  data: { id: "user123", name: "Alice Wonder" },
  metadata: { timestamp: "2023-12-01T10:30:00Z", requestId: "req-456" }
};

const productListResponse: ApiPaginated<{id: string; title: string}> = {
  status: "success",
  data: [
    { id: "prod1", title: "TypeScript Guide" },
    { id: "prod2", title: "JavaScript Mastery" }
  ],
  pagination: { page: 1, limit: 10, total: 25, hasMore: true }
};

// ๐ŸŽฏ Process with full type safety
apiHandler.processResponse(userResponse);
apiHandler.processResponse(productListResponse);

๐ŸŽฎ Example 2: Redux Action Type Extractor

Letโ€™s create a type-safe Redux pattern using infer:

// ๐Ÿ† Define action patterns
interface BaseAction {
  type: string;
}

interface PayloadAction<T = any> extends BaseAction {
  payload: T;
}

interface MetaAction<T = any, M = any> extends PayloadAction<T> {
  meta: M;
}

// ๐ŸŽฏ Action creators with different patterns
const createUser = (userData: {name: string; email: string}) => ({
  type: "CREATE_USER" as const,
  payload: userData
});

const deleteUser = (userId: string) => ({
  type: "DELETE_USER" as const,
  payload: { userId }
});

const updateUserWithMeta = (userId: string, updates: Partial<{name: string; email: string}>) => ({
  type: "UPDATE_USER" as const,
  payload: { userId, updates },
  meta: { timestamp: Date.now(), source: "user-action" }
});

const resetState = () => ({
  type: "RESET_STATE" as const
});

// ๐Ÿš€ Infer-powered type extractors
type ExtractActionType<T> = T extends { type: infer U } ? U : never;

type ExtractPayload<T> = T extends { payload: infer P } ? P : never;

type ExtractMeta<T> = T extends { meta: infer M } ? M : never;

type ExtractActionFromCreator<T> = T extends (...args: any[]) => infer A ? A : never;

// ๐Ÿงช Testing our extractors
type CreateUserAction = ExtractActionFromCreator<typeof createUser>;
// Result: { type: "CREATE_USER"; payload: {name: string; email: string} }

type DeleteUserAction = ExtractActionFromCreator<typeof deleteUser>;
// Result: { type: "DELETE_USER"; payload: { userId: string } }

type UpdateUserAction = ExtractActionFromCreator<typeof updateUserWithMeta>;
// Result: { type: "UPDATE_USER"; payload: {...}; meta: {...} }

type ResetAction = ExtractActionFromCreator<typeof resetState>;
// Result: { type: "RESET_STATE" }

// โœจ Extract specific parts
type CreateUserType = ExtractActionType<CreateUserAction>;        // "CREATE_USER"
type CreateUserPayload = ExtractPayload<CreateUserAction>;        // {name: string; email: string}
type UpdateUserMeta = ExtractMeta<UpdateUserAction>;              // {timestamp: number; source: string}

// ๐ŸŽจ Create union of all action types
type AllActionCreators = 
  | typeof createUser
  | typeof deleteUser  
  | typeof updateUserWithMeta
  | typeof resetState;

type AllActions = ExtractActionFromCreator<AllActionCreators>;

// ๐ŸŽฎ Type-safe reducer using infer
type ReducerFunction<S, A> = (state: S, action: A) => S;

interface UserState {
  users: Array<{id: string; name: string; email: string}>;
  loading: boolean;
  lastUpdated?: number;
}

// ๐Ÿš€ Smart reducer with infer-powered action handling
const userReducer: ReducerFunction<UserState, AllActions> = (state, action) => {
  // TypeScript knows exactly what action.type and action.payload can be!
  switch (action.type) {
    case "CREATE_USER":
      return {
        ...state,
        users: [...state.users, {
          id: `user-${Date.now()}`,
          ...action.payload  // TypeScript knows this is {name: string; email: string}
        }],
        loading: false
      };
    
    case "DELETE_USER":
      return {
        ...state,
        users: state.users.filter(user => 
          user.id !== action.payload.userId  // TypeScript knows payload has userId
        ),
        loading: false
      };
    
    case "UPDATE_USER":
      return {
        ...state,
        users: state.users.map(user =>
          user.id === action.payload.userId
            ? { ...user, ...action.payload.updates }
            : user
        ),
        lastUpdated: action.meta.timestamp,  // TypeScript knows meta exists!
        loading: false
      };
    
    case "RESET_STATE":
      return {
        users: [],
        loading: false
      };
    
    default:
      return state;
  }
};

// ๐Ÿงช Usage with full type safety
const initialState: UserState = { users: [], loading: false };

// All actions are type-safe!
const action1 = createUser({ name: "Alice", email: "[email protected]" });
const action2 = deleteUser("user123");
const action3 = updateUserWithMeta("user123", { name: "Alice Wonder" });

const newState1 = userReducer(initialState, action1);
const newState2 = userReducer(newState1, action2);
const newState3 = userReducer(newState2, action3);

console.log("๐ŸŽฏ Final state:", newState3);

๐Ÿš€ Advanced Infer Techniques

๐Ÿง™โ€โ™‚๏ธ Multiple Infer Variables

You can use multiple infer variables in the same conditional type:

// ๐ŸŽจ Extract both key and value types from object entries
type ObjectEntry<T> = T extends { [K in infer Key]: infer Value } 
  ? { key: Key; value: Value }
  : never;

// ๐Ÿงช Testing multiple infer
interface ProductCatalog {
  electronics: { laptops: number; phones: number };
  books: { fiction: number; technical: number };
  clothing: { shirts: number; pants: number };
}

type CatalogEntry = ObjectEntry<ProductCatalog>;
// Result: { key: "electronics" | "books" | "clothing"; value: object }

// ๐Ÿš€ Extract function signature components
type FunctionSignature<T> = T extends (
  ...args: infer Args
) => infer Return 
  ? { parameters: Args; returnType: Return }
  : never;

// ๐ŸŽฎ Testing signature extraction
type LoginSignature = FunctionSignature<(username: string, password: string) => Promise<boolean>>;
// Result: { parameters: [username: string, password: string]; returnType: Promise<boolean> }

๐Ÿ—๏ธ Recursive Infer Patterns

// ๐ŸŽฏ Deep flatten array types
type DeepFlatten<T> = T extends (infer U)[]
  ? U extends any[]
    ? DeepFlatten<U>
    : U
  : T;

// ๐Ÿงช Testing deep flatten
type NestedArray = string[][][];
type FlattenedArray = DeepFlatten<NestedArray>;  // string

// โœจ Extract all property names recursively
type DeepKeys<T> = T extends object
  ? {
      [K in keyof T]: K extends string
        ? T[K] extends object
          ? `${K}` | `${K}.${DeepKeys<T[K]>}`
          : `${K}`
        : never;
    }[keyof T]
  : never;

// ๐Ÿš€ Testing deep keys
interface NestedConfig {
  api: {
    endpoints: {
      users: string;
      products: string;
    };
    timeout: number;
  };
  ui: {
    theme: string;
    language: string;
  };
}

type AllConfigKeys = DeepKeys<NestedConfig>;
// Result: "api" | "api.endpoints" | "api.endpoints.users" | "api.endpoints.products" | "api.timeout" | "ui" | "ui.theme" | "ui.language"

โš ๏ธ Common Pitfalls and Solutions

๐Ÿ˜ฑ Pitfall 1: Infer in Wrong Context

// โŒ Can't use infer outside conditional types!
type BadInfer<T> = infer U;  // โŒ Error!

// โœ… Infer only works in conditional type expressions
type GoodInfer<T> = T extends Array<infer U> ? U : never;

๐Ÿคฏ Pitfall 2: Multiple Infer Same Variable

// โŒ Using same infer variable name can be confusing
type ConfusingInfer<T> = T extends {
  a: infer U;
  b: infer U;  // Same U - will be intersection type!
} ? U : never;

// ๐Ÿงช Testing confusing infer
type TestConfusing = ConfusingInfer<{ a: string; b: number }>;  // string & number (never!)

// โœ… Use different variable names for clarity
type ClearInfer<T> = T extends {
  a: infer A;
  b: infer B;
} ? { propA: A; propB: B } : never;

type TestClear = ClearInfer<{ a: string; b: number }>;  // { propA: string; propB: B }

๐Ÿ” Pitfall 3: Infer with Distribution

// โŒ Distribution can affect infer behavior
type DistributedInfer<T> = T extends Array<infer U> ? U : never;

// ๐Ÿงช Testing with union
type UnionArrays = string[] | number[];
type DistributedResult = DistributedInfer<UnionArrays>;  // string | number (distributed!)

// โœ… Prevent distribution if needed
type NonDistributedInfer<T> = [T] extends [Array<infer U>] ? U : never;
type NonDistributedResult = NonDistributedInfer<UnionArrays>;  // never (no distribution)

๐Ÿ› ๏ธ Best Practices

  1. ๐ŸŽฏ Use Descriptive Infer Names: infer ElementType is better than infer U
  2. ๐Ÿ“ Start Simple: Master basic patterns before attempting complex recursion
  3. ๐Ÿ›ก๏ธ Handle Edge Cases: Always consider what happens with never and unknown
  4. ๐ŸŽจ Test Your Patterns: Use type tests to verify your infer logic works correctly
  5. โœจ Combine with Distribution: Understand how infer interacts with union types
  6. ๐Ÿ” Debug Step by Step: Break complex infer patterns into smaller, testable pieces

๐Ÿงช Hands-On Exercise

๐ŸŽฏ Challenge: Build a Smart Database Query Type System

Create an infer-powered type system for type-safe database queries:

๐Ÿ“‹ Requirements:

  • โœ… Extract table names from query strings
  • ๐Ÿ”ง Parse SELECT fields and infer result types
  • ๐ŸŽจ Handle WHERE clauses with type safety
  • ๐Ÿ“Š Support JOIN operations
  • โœจ Type-safe query builder!

๐Ÿš€ Bonus Points:

  • Add INSERT/UPDATE/DELETE query parsing
  • Create nested query support
  • Build query validation system

๐Ÿ’ก Solution

๐Ÿ” Click to see solution
// ๐ŸŽฏ Database schema definition
interface DatabaseSchema {
  users: {
    id: number;
    name: string;
    email: string;
    age: number;
    active: boolean;
  };
  products: {
    id: number;
    title: string;
    price: number;
    category: string;
    inStock: boolean;
  };
  orders: {
    id: number;
    userId: number;
    productId: number;
    quantity: number;
    total: number;
  };
}

// ๐Ÿš€ Query parsing with infer
type ParseSelectQuery<Q extends string> = 
  Q extends `SELECT ${infer Fields} FROM ${infer Table}`
    ? {
        operation: "SELECT";
        fields: ParseFields<Fields>;
        table: Table extends keyof DatabaseSchema ? Table : never;
        resultType: Table extends keyof DatabaseSchema 
          ? ProjectFields<DatabaseSchema[Table], ParseFields<Fields>>
          : never;
      }
    : never;

// ๐ŸŽจ Parse field list
type ParseFields<F extends string> = 
  F extends "*"
    ? "*"
    : F extends `${infer Field}, ${infer Rest}`
      ? [Field, ...ParseFields<Rest>]
      : [F];

// โœจ Project specific fields from table schema
type ProjectFields<Schema, Fields> = 
  Fields extends "*"
    ? Schema
    : Fields extends readonly (infer F)[]
      ? F extends keyof Schema
        ? Pick<Schema, F>
        : never
      : never;

// ๐Ÿงช Advanced query parsing with WHERE clauses
type ParseQueryWithWhere<Q extends string> = 
  Q extends `${infer BaseQuery} WHERE ${infer WhereClause}`
    ? ParseSelectQuery<BaseQuery> extends infer BaseResult
      ? BaseResult extends { operation: "SELECT"; table: infer Table }
        ? BaseResult & {
            where: ParseWhereClause<WhereClause>;
            filters: Table extends keyof DatabaseSchema 
              ? ExtractWhereFields<WhereClause, DatabaseSchema[Table]>
              : never;
          }
        : never
      : never
    : ParseSelectQuery<Q>;

// ๐ŸŽฏ Parse WHERE clause conditions
type ParseWhereClause<W extends string> = 
  W extends `${infer Field} = ${infer Value}`
    ? { field: Field; operator: "="; value: Value }
    : W extends `${infer Field} > ${infer Value}`
      ? { field: Field; operator: ">"; value: Value }
      : W extends `${infer Field} < ${infer Value}`
        ? { field: Field; operator: "<"; value: Value }
        : { unparsed: W };

// ๐Ÿš€ Extract and validate WHERE fields
type ExtractWhereFields<W extends string, Schema> = 
  W extends `${infer Field} = ${infer Value}`
    ? Field extends keyof Schema
      ? { [K in Field]: Schema[Field] }
      : never
    : {};

// ๐ŸŽฎ Type-safe query builder
class TypeSafeQueryBuilder<Schema extends DatabaseSchema = DatabaseSchema> {
  // โœจ Execute SELECT query with full type safety
  select<Q extends string>(
    query: Q
  ): ParseQueryWithWhere<Q> extends { resultType: infer R } ? R[] : never {
    // Mock implementation - in real app, this would execute the actual query
    console.log(`๐Ÿ” Executing query: ${query}`);
    
    // Parse and validate query at runtime
    const parsed = this.parseQuery(query);
    console.log(`๐Ÿ“Š Parsed query:`, parsed);
    
    // Return mock results with correct typing
    return [] as any;
  }

  // ๐ŸŽฏ Parse query at runtime (implementation detail)
  private parseQuery(query: string) {
    // Simple parser for demonstration
    const selectMatch = query.match(/SELECT (.+) FROM (\w+)/i);
    if (selectMatch) {
      const [, fields, table] = selectMatch;
      return {
        operation: "SELECT",
        fields: fields === "*" ? "*" : fields.split(",").map(f => f.trim()),
        table: table.toLowerCase()
      };
    }
    return null;
  }

  // ๐Ÿš€ Fluent interface with type safety
  from<T extends keyof Schema>(table: T) {
    return new TableQueryBuilder<Schema, T>(table);
  }
}

// ๐ŸŽจ Table-specific query builder
class TableQueryBuilder<Schema extends DatabaseSchema, Table extends keyof Schema> {
  constructor(private table: Table) {}

  // โœจ Type-safe field selection
  select<Fields extends keyof Schema[Table]>(
    ...fields: Fields[]
  ): Pick<Schema[Table], Fields>[] {
    console.log(`๐Ÿ” SELECT ${fields.join(", ")} FROM ${String(this.table)}`);
    return [] as Pick<Schema[Table], Fields>[];
  }

  // ๐ŸŽฏ Type-safe WHERE conditions
  where<Field extends keyof Schema[Table]>(
    field: Field,
    operator: "=" | ">" | "<" | ">=",
    value: Schema[Table][Field]
  ): this {
    console.log(`๐Ÿ“‹ WHERE ${String(field)} ${operator} ${value}`);
    return this;
  }

  // ๐Ÿš€ Get all records with full typing
  all(): Schema[Table][] {
    console.log(`๐ŸŽฎ SELECT * FROM ${String(this.table)}`);
    return [] as Schema[Table][];
  }
}

// ๐Ÿงช Usage examples with full type safety
const queryBuilder = new TypeSafeQueryBuilder<DatabaseSchema>();

// โœ… Type-safe string queries
const userResults = queryBuilder.select("SELECT name, email FROM users");
// Type: { name: string; email: string }[]

const productResults = queryBuilder.select("SELECT * FROM products");
// Type: { id: number; title: string; price: number; category: string; inStock: boolean }[]

const queryWithWhere = queryBuilder.select("SELECT title, price FROM products WHERE category = 'electronics'");
// Type: { title: string; price: number }[]

// ๐ŸŽฏ Fluent interface with type safety
const users = queryBuilder
  .from("users")
  .select("name", "email", "age")  // TypeScript ensures these fields exist!
  .where("active", "=", true)      // TypeScript ensures 'active' is boolean!

const products = queryBuilder
  .from("products")
  .where("price", ">", 50)         // TypeScript ensures 'price' is number!
  .where("inStock", "=", true)
  .all();

// โŒ These would cause TypeScript errors:
// queryBuilder.select("SELECT invalid_field FROM users");  // invalid_field doesn't exist
// queryBuilder.from("users").where("age", "=", "25");      // age should be number, not string
// queryBuilder.from("invalid_table");                      // table doesn't exist

console.log("๐ŸŽ‰ All queries executed with type safety!");

๐ŸŽ“ Key Takeaways

Youโ€™ve mastered the infer keyword! Hereโ€™s what you can now do:

  • โœ… Extract types from complex structures with surgical precision ๐Ÿ’ช
  • โœ… Build powerful utility types that adapt automatically ๐Ÿ›ก๏ธ
  • โœ… Parse and transform type patterns like a detective ๐ŸŽฏ
  • โœ… Debug type inference issues with confidence ๐Ÿ›
  • โœ… Create type-safe APIs that feel magical ๐Ÿš€

Remember: The infer keyword is like having X-ray vision ๐Ÿ‘€ for types - it can see inside any type structure and extract exactly what you need!

๐Ÿค Next Steps

Congratulations! ๐ŸŽ‰ Youโ€™ve conquered the infer keyword!

Hereโ€™s what to explore next:

  1. ๐Ÿ’ป Practice with the database query exercise above
  2. ๐Ÿ—๏ธ Build your own infer-powered utility types
  3. ๐Ÿ“š Explore combining infer with mapped types and template literals
  4. ๐ŸŒŸ Share your infer magic with the TypeScript community!

You now possess one of TypeScriptโ€™s most powerful type-level tools. Use it to create utilities that feel like magic and APIs that are impossible to use incorrectly. Remember - every TypeScript wizard started with curiosity. Keep experimenting, keep learning, and most importantly, have fun with types! ๐Ÿš€โœจ


Happy type detective work! ๐ŸŽ‰๐Ÿ”โœจ