Lab 10: Type Manipulation

Objective

Master advanced TypeScript type manipulation: keyof, typeof, indexed access types, template literal types, mapped types with modifiers, and building complex type utilities.

Time

30 minutes

Prerequisites

  • Lab 03 (Interfaces), Lab 06 (Generics)

Tools

  • Docker image: zchencow/innozverse-ts:latest


Lab Instructions

Step 1: keyof & typeof

interface Product {
    id: number;
    name: string;
    price: number;
    stock: number;
    category: string;
}

// keyof — produces union of property names
type ProductKeys = keyof Product;  // "id" | "name" | "price" | "stock" | "category"

// typeof — get type of a value (at type level)
const defaultConfig = { host: "localhost", port: 3000, debug: false, timeout: 30 };
type Config = typeof defaultConfig;  // { host: string; port: number; debug: boolean; timeout: number }

// keyof typeof — keys of an object value
type ConfigKey = keyof typeof defaultConfig;  // "host" | "port" | "debug" | "timeout"

function getConfig<K extends keyof typeof defaultConfig>(key: K): (typeof defaultConfig)[K] {
    return defaultConfig[key];
}

console.log(getConfig("host"));    // string
console.log(getConfig("port"));    // number
console.log(getConfig("debug"));   // boolean

// typeof on functions
function add(a: number, b: number) { return a + b; }
type AddFn = typeof add;  // (a: number, b: number) => number
type AddReturn = ReturnType<typeof add>;  // number
type AddParams = Parameters<typeof add>;  // [number, number]

// keyof for generic lookup
function pluck<T, K extends keyof T>(items: T[], key: K): T[K][] {
    return items.map(item => item[key]);
}

const products: Product[] = [
    { id: 1, name: "Surface Pro", price: 864, stock: 15, category: "Laptop" },
    { id: 2, name: "Surface Pen", price: 49.99, stock: 80, category: "Accessory" },
];

console.log(pluck(products, "name"));    // ["Surface Pro", "Surface Pen"]
console.log(pluck(products, "price"));   // [864, 49.99]

💡 as const freezes an object literal's types to their literal values — { port: 3000 } has type { port: number } normally, but { port: 3000 } as const has type { readonly port: 3000 }. Combined with typeof, you get precise literal types for all values.

📸 Verified Output:


Step 2: Indexed Access Types

💡 Type["items"][number] gets the element type of an array property — it's the TypeScript equivalent of "what's in this array?" This is essential for writing utilities that operate on array elements without duplicating the interface definition.

📸 Verified Output:


Steps 3–8: Template Literals, Mapped Modifiers, Recursive, Deep Utilities, as const, Capstone

📸 Verified Output:


Summary

TypeScript's type manipulation capabilities are a programming language within a programming language. You've covered keyof, typeof, indexed access types, template literal types, mapped type modifiers, deep utility types, type predicates for arrays, string manipulation types, and a schema-to-type inference system.

Further Reading

Last updated