Lab 03: Interfaces & Utility Types
Objective
Use advanced interface patterns, built-in utility types (Partial, Required, Pick, Omit, Record, Readonly), mapped types, and conditional types to express complex data shapes.
Background
TypeScript's type system goes far beyond basic annotations. Utility types let you transform existing types — create a "partial" version of an interface for updates, pick only the fields you need, or make everything readonly. Mapped types and conditional types give you a "type-level programming" capability that catches entire classes of bugs at compile time.
Time
30 minutes
Prerequisites
Lab 01, Lab 02
Tools
Docker image:
zchencow/innozverse-ts:latest
Lab Instructions
Step 1: Interface Patterns
💡 Interface declaration merging lets you add properties to existing interfaces — including built-in ones like
Window,Array, orHTMLElement. This is how DefinitelyTyped (the @types packages) augment third-party libraries. It's a double-edged sword: powerful for library authors, dangerous if overused.
📸 Verified Output:
Step 2: Built-in Utility Types
💡
Omit<User, 'password'>is the most important utility type for APIs — create a type without sensitive fields.PickandOmitare complementary: usePickwhen you want a few fields,Omitwhen you want all-but-a-few. Both produce new types without modifying the original.
📸 Verified Output:
Step 3: Mapped Types
💡 Mapped types + key remapping (
as \get${Capitalize<...>}`) lets you generate new types from old ones — adding prefixes, converting names, filtering by value type. This is how TypeScript's ownPartial,Readonly, andRequired` utility types are implemented internally.
📸 Verified Output:
Step 4: Conditional Types
💡
inferkeyword lets you extract types from within other types.T extends Promise<infer R>means "if T is a Promise of something, call that something R." This is howReturnType<F>,Awaited<T>, andParameters<F>are implemented in TypeScript's standard library.
📸 Verified Output:
Step 5: Template Literal Types
💡 Template literal types generate string literal unions from combinations of other literals.
\on${Capitalize}`produces 4 types from 4 events.`${Method} ${Path}`` produces 12 route types from 3 methods × 4 paths. This enables typed route definitions, event maps, and CSS-in-JS systems.
📸 Verified Output:
Step 6: Discriminated Unions
💡 Discriminated unions + exhaustiveness checking is TypeScript's killer pattern for state machines. If you add a new variant (e.g.,
| { kind: "ellipse"; ... }), TypeScript immediately errors at everyswitchstatement that doesn't handle it. This is impossible in JavaScript — you'd only find the bug at runtime.
📸 Verified Output:
Step 7: Recursive Types
📸 Verified Output:
Step 8: Complete — Type-Safe API Client
💡
ApiParams<M>andApiResponse<M>are indexed access types — they look up theparamsandresponsefields from theEndpointsinterface using the method string as a key. This ties the request parameters and response type to the specific endpoint, makingclient.request()fully type-safe.
📸 Verified Output:
Summary
TypeScript's type system is a full programming language at the type level. You've used built-in utility types, mapped types with key remapping, conditional types with infer, template literal types, discriminated unions with exhaustiveness checking, recursive types, and a fully type-safe API client. These patterns are used in every professional TypeScript codebase.
Further Reading
Last updated
