
Typescript Advanced Patterns
- 113 installs
- 28 repo stars
- Updated June 29, 2026
- nickcrew/claude-ctx-plugin
Helps with ai & agent building tasks.
About
typescript-advanced-patterns is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- typescript-advanced-patterns
- AI & Agent Building
- AI-coding skill
Typescript Advanced Patterns by the numbers
- 113 all-time installs (skills.sh)
- Ranked #3,931 of 16,556 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/nickcrew/claude-ctx-plugin --skill typescript-advanced-patternsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 113 |
|---|---|
| repo stars | ★ 28 |
| Last updated | June 29, 2026 |
| Repository | nickcrew/claude-ctx-plugin ↗ |
What it does
Helps with ai & agent building tasks.
Files
TypeScript Advanced Patterns
Expert guidance for leveraging TypeScript's advanced type system features to build robust, type-safe applications with sophisticated type inference, compile-time guarantees, and maintainable domain models.
When to Use This Skill
- Building type-safe APIs with strict contracts and validation
- Implementing complex domain models with compile-time enforcement
- Creating reusable libraries with sophisticated type inference
- Enforcing business rules through the type system
- Building type-safe state machines and builders
- Developing framework integrations requiring advanced types
- Implementing runtime validation with type-level guarantees
Core Concepts
TypeScript's type system enables compile-time safety through:
1. Conditional Types: Type selection based on conditions (type-level if/else) 2. Mapped Types: Transform object types systematically (Partial, Readonly, Pick, Omit) 3. Template Literal Types: String manipulation at compile time 4. Type Guards: Runtime checking with type narrowing (value is Type) 5. Discriminated Unions: Type-safe state machines with exhaustiveness checking 6. Branded Types: Nominal types for preventing primitive mixing 7. Builder Pattern: Type-safe fluent APIs with progressive type constraints 8. Advanced Generics: Constraints, inference, and higher-kinded type patterns 9. Utility Types: Deep transformations and compositions 10. Type Inference: Const assertions and contextual typing
Quick Reference
Load detailed references on-demand:
| Topic | Reference File |
|---|---|
| Conditional Types | skills/typescript-advanced-patterns/references/conditional-types.md |
| Mapped Types | skills/typescript-advanced-patterns/references/mapped-types.md |
| Template Literal Types | skills/typescript-advanced-patterns/references/template-literal-types.md |
| Type Guards | skills/typescript-advanced-patterns/references/type-guards.md |
| Discriminated Unions | skills/typescript-advanced-patterns/references/discriminated-unions.md |
| Branded Types | skills/typescript-advanced-patterns/references/branded-types.md |
| Builder Pattern | skills/typescript-advanced-patterns/references/builder-pattern.md |
| Advanced Generics | skills/typescript-advanced-patterns/references/advanced-generics.md |
| Utility Types | skills/typescript-advanced-patterns/references/utility-types.md |
| Type Inference | skills/typescript-advanced-patterns/references/type-inference.md |
| Decorators | skills/typescript-advanced-patterns/references/decorators.md |
| Performance Best Practices | skills/typescript-advanced-patterns/references/performance-best-practices.md |
| Common Pitfalls | skills/typescript-advanced-patterns/references/common-pitfalls.md |
| Testing Types | skills/typescript-advanced-patterns/references/testing-types.md |
Implementation Workflow
1. Identify Pattern Need
- Analyze type safety requirements
- Identify runtime vs compile-time constraints
- Choose appropriate pattern from Quick Reference
2. Load Reference
- Read specific reference file for pattern
- Review examples and use cases
- Understand trade-offs
3. Implement Pattern
- Start simple, add complexity as needed
- Use strict mode (
tsconfig.jsonwith"strict": true) - Test with type assertions
4. Validate
- Ensure type errors caught at compile time
- Verify runtime behavior matches types
- Check performance (avoid excessive type complexity)
5. Document
- Add JSDoc comments for public APIs
- Document type constraints and assumptions
- Provide usage examples
Common Mistakes to Avoid
1. Using `any` instead of `unknown`: Loses all type safety
- Use
unknownand type guards instead
2. Type assertions without validation: Unsafe runtime behavior
- Prefer type guards (
value is Type) overas Type
3. Overusing generics: Unnecessary complexity
- Only use generics when types truly vary
4. Deep type nesting: Slow compilation, hard to debug
- Keep types composable and shallow
5. Forgetting `readonly`: Accidental mutations
- Mark immutable data structures as
readonly
6. Not enabling strict mode: Missing null checks and type errors
- Always use
"strict": trueintsconfig.json
7. Mixing type and interface incorrectly: Confusing semantics
- Use
typefor unions/utilities,interfacefor object shapes
Quick Patterns
Type-Safe ID
type UserId = string & { readonly __brand: 'UserId' };
function createUserId(id: string): UserId { return id as UserId; }Discriminated Union
type State =
| { status: 'loading' }
| { status: 'success'; data: string }
| { status: 'error'; error: Error };Mapped Type Transformation
type Readonly<T> = { readonly [P in keyof T]: T[P] };
type Partial<T> = { [P in keyof T]?: T[P] };Type Guard
function isString(value: unknown): value is string {
return typeof value === 'string';
}Resources
- TypeScript Handbook: https://www.typescriptlang.org/docs/handbook/
- Type Challenges: https://github.com/type-challenges/type-challenges
- ts-toolbelt: Advanced type utilities library
- zod: Runtime validation with TypeScript inference
- tsd: Test TypeScript type definitions
Advanced Generics
Generic constraints and inference:
Basic Constraints
// Constrain to objects with specific keys
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = { name: 'John', age: 30 };
const name = getProperty(user, 'name'); // Type: string
// const invalid = getProperty(user, 'invalid'); // Error
// Multiple constraints
function merge<T extends object, U extends object>(
obj1: T,
obj2: U
): T & U {
return { ...obj1, ...obj2 };
}Generic Inference
// Infer from function implementation
function createAction<T extends string, P>(
type: T,
payload: P
) {
return { type, payload };
}
const action = createAction('UPDATE_USER', { id: 1, name: 'John' });
// Type: { type: 'UPDATE_USER'; payload: { id: number; name: string } }
// Infer generic types from usage
function useState<S>(
initialState: S | (() => S)
): [S, (newState: S) => void] {
// Implementation
}
const [count, setCount] = useState(0); // S inferred as number
const [user, setUser] = useState({ name: 'John' }); // S inferred as { name: string }Higher-Kinded Types Pattern
// Type-safe data structures
interface Functor<F> {
map<A, B>(fa: F extends { value: any } ? F : never, f: (a: A) => B): any;
}
interface Box<T> {
value: T;
}
const boxFunctor: Functor<Box<any>> = {
map<A, B>(fa: Box<A>, f: (a: A) => B): Box<B> {
return { value: f(fa.value) };
}
};Conditional Generic Types
// Return type varies based on parameter
type ApiResponse<T extends string> =
T extends 'json' ? object :
T extends 'text' ? string :
T extends 'blob' ? Blob :
never;
async function fetch<T extends 'json' | 'text' | 'blob'>(
url: string,
type: T
): Promise<ApiResponse<T>> {
// Implementation
}
const json = await fetch('/api', 'json'); // Type: object
const text = await fetch('/api', 'text'); // Type: string
const blob = await fetch('/api', 'blob'); // Type: BlobBranded Types
Create nominal types for type safety:
Preventing Primitive Mixing
// Prevent mixing similar primitive types
type UserId = string & { readonly __brand: 'UserId' };
type PostId = string & { readonly __brand: 'PostId' };
function createUserId(id: string): UserId {
return id as UserId;
}
function createPostId(id: string): PostId {
return id as PostId;
}
function getUser(userId: UserId): User {
// Implementation
}
const userId = createUserId('user-123');
const postId = createPostId('post-456');
getUser(userId); // Valid
// getUser(postId); // Type error: PostId not assignable to UserIdValidation with Branded Types
type ValidEmail = string & { readonly __brand: 'ValidEmail' };
type ValidURL = string & { readonly __brand: 'ValidURL' };
function validateEmail(email: string): ValidEmail | null {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return regex.test(email) ? (email as ValidEmail) : null;
}
function sendEmail(to: ValidEmail, subject: string, body: string) {
// Guaranteed to have valid email
}
const email = validateEmail('user@example.com');
if (email) {
sendEmail(email, 'Hello', 'World');
}Benefits
- Compile-time prevention of ID mixing
- Self-documenting code through type names
- Enforced validation at boundaries
- Zero runtime overhead
Builder Pattern with Types
Type-safe fluent APIs:
Query Builder Example
interface QueryBuilder<TSelect = unknown, TWhere = unknown> {
select<T>(): QueryBuilder<T, TWhere>;
where<T>(): QueryBuilder<TSelect, T>;
execute(): TSelect extends unknown ? never : Promise<TSelect[]>;
}
// Usage ensures select() called before execute()
const results = await query
.select<User>()
.where<{ age: number }>()
.execute(); // Type: Promise<User[]>
// query.execute(); // Error: select() not calledProgressive Builder Types
interface ConfigBuilder<
THost extends string | undefined = undefined,
TPort extends number | undefined = undefined
> {
host: THost;
port: TPort;
withHost<H extends string>(host: H): ConfigBuilder<H, TPort>;
withPort<P extends number>(port: P): ConfigBuilder<THost, P>;
build: THost extends string
? TPort extends number
? () => { host: THost; port: TPort }
: never
: never;
}
const config = new ConfigBuilder()
.withHost('localhost')
.withPort(3000)
.build(); // Valid
// new ConfigBuilder().build(); // Error: host and port requiredType State Pattern
// Enforce method call order at compile time
interface EmptyBuilder {
addItem<T>(item: T): FilledBuilder<T>;
}
interface FilledBuilder<T> {
addItem(item: T): FilledBuilder<T>;
build(): T[];
}
// Must call addItem() before build()
const items = builder
.addItem('first')
.addItem('second')
.build(); // Valid
// builder.build(); // Error: can't build emptyCommon Pitfalls
Type Assertions vs Type Guards
// Bad - unsafe type assertion
const value = input as string;
// Good - safe type guard
function assertString(value: unknown): asserts value is string {
if (typeof value !== 'string') {
throw new Error('Not a string');
}
}
assertString(input);
// input is now narrowed to stringAny vs Unknown
// Bad - loses type safety
function process(data: any) {
return data.toUpperCase(); // No type checking
}
// Good - maintains type safety
function processUnknown(data: unknown) {
if (typeof data === 'string') {
return data.toUpperCase(); // Type guard required
}
throw new Error('Expected string');
}Overusing Generics
// Bad - unnecessary complexity
function add<T extends number, U extends number>(a: T, b: U): number {
return a + b;
}
// Good - simple and clear
function add(a: number, b: number): number {
return a + b;
}Incorrect Type Narrowing
// Bad - doesn't narrow type
function isString(value: any): boolean {
return typeof value === 'string';
}
// Good - properly narrows type
function isString(value: unknown): value is string {
return typeof value === 'string';
}Forgetting Readonly
// Bad - mutable when should be immutable
interface Config {
apiUrl: string;
timeout: number;
}
// Good - prevent accidental mutations
interface Config {
readonly apiUrl: string;
readonly timeout: number;
}Enum Pitfalls
// Bad - numeric enums allow invalid values
enum Status {
Active,
Inactive
}
const status: Status = 999; // Valid but meaningless
// Good - use string enums or const objects
enum Status {
Active = 'ACTIVE',
Inactive = 'INACTIVE'
}
// Or use const object with as const
const Status = {
Active: 'ACTIVE',
Inactive: 'INACTIVE'
} as const;Not Using Strict Mode
// Always enable in tsconfig.json
{
"compilerOptions": {
"strict": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictPropertyInitialization": true
}
}Type vs Interface Confusion
// Use type for unions, intersections, utilities
type ID = string | number;
type Point = { x: number } & { y: number };
// Use interface for object shapes that may be extended
interface User {
id: ID;
name: string;
}
interface Admin extends User {
permissions: string[];
}Conditional Types
Type selection based on conditions:
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
// Extract function return types
type ReturnTypeOf<T> = T extends (...args: any[]) => infer R ? R : never;
type Fn = () => { name: string; age: number };
type Result = ReturnTypeOf<Fn>; // { name: string; age: number }
// Extract array element types
type ElementOf<T> = T extends (infer E)[] ? E : never;
type Items = ElementOf<string[]>; // stringUse Cases
- Type transformation and extraction
- Conditional API responses based on request types
- Generic utility type creation
- Framework integration types
Decorators (Stage 3)
Class and method decorators for cross-cutting concerns:
Method Decorators
// Method decorator for logging
function log(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const original = descriptor.value;
descriptor.value = function(...args: any[]) {
console.log(`Calling ${propertyKey} with`, args);
const result = original.apply(this, args);
console.log(`Result:`, result);
return result;
};
return descriptor;
}
class Calculator {
@log
add(a: number, b: number): number {
return a + b;
}
}Property Decorators
// Property decorator for validation
function validate(validator: (value: any) => boolean) {
return function(target: any, propertyKey: string) {
let value = target[propertyKey];
Object.defineProperty(target, propertyKey, {
get: () => value,
set: (newValue) => {
if (!validator(newValue)) {
throw new Error(`Invalid value for ${propertyKey}`);
}
value = newValue;
}
});
};
}
class User {
@validate(email => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email))
email: string;
}Class Decorators
// Class decorator for metadata
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@sealed
class SealedClass {
constructor(public name: string) {}
}Decorator Factories
// Decorator with parameters
function component(config: { selector: string }) {
return function(constructor: Function) {
constructor.prototype.selector = config.selector;
};
}
@component({ selector: 'app-user' })
class UserComponent {
// selector property added at runtime
}Common Use Cases
- Logging: Automatic method call logging
- Validation: Property value validation
- Memoization: Cache method results
- Authorization: Check permissions before execution
- Dependency Injection: Inject dependencies into classes
- Metadata: Attach runtime metadata for frameworks
Discriminated Unions
Type-safe state machines and variants:
State Machines
// State machine with exhaustive checking
type LoadingState =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: string[] }
| { status: 'error'; error: Error };
function renderState(state: LoadingState): string {
switch (state.status) {
case 'idle':
return 'Not started';
case 'loading':
return 'Loading...';
case 'success':
return `Loaded ${state.data.length} items`;
case 'error':
return `Error: ${state.error.message}`;
}
// Exhaustiveness checking ensures all cases handled
}Action Types
// API action types
type Action =
| { type: 'FETCH_USER'; payload: { userId: string } }
| { type: 'UPDATE_USER'; payload: { userId: string; data: Partial<User> } }
| { type: 'DELETE_USER'; payload: { userId: string } }
| { type: 'CLEAR_USERS' };
function reducer(state: State, action: Action): State {
switch (action.type) {
case 'FETCH_USER':
// action.payload is { userId: string }
return { ...state, loading: true };
case 'UPDATE_USER':
// action.payload is { userId: string; data: Partial<User> }
return updateUser(state, action.payload);
case 'DELETE_USER':
return deleteUser(state, action.payload.userId);
case 'CLEAR_USERS':
// action has no payload
return { ...state, users: [] };
}
}Best Practices
- Always include a discriminant property (e.g.,
status,type) - Use string literal types for discriminant values
- Enable
strictNullChecksfor exhaustiveness checking - Use
neverto ensure all cases are handled
Mapped Types
Transform object types systematically:
// Make all properties optional
type Partial<T> = {
[P in keyof T]?: T[P];
};
// Make all properties readonly
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
// Pick specific properties
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
interface User {
id: number;
name: string;
email: string;
password: string;
}
// Create API response type
type UserResponse = Omit<User, 'password'>;
// Create update type (all optional)
type UserUpdate = Partial<User>;
// Create creation type (no id)
type UserCreate = Omit<User, 'id'>;Advanced Mapping
// Add prefix to all keys
type Prefixed<T, Prefix extends string> = {
[K in keyof T as `${Prefix}${string & K}`]: T[K];
};
type Events = {
click: MouseEvent;
focus: FocusEvent;
};
type Handlers = Prefixed<Events, 'on'>;
// { onclick: MouseEvent; onfocus: FocusEvent }Performance Best Practices
Avoid Excessive Type Complexity
Keep types simple and composable:
// Bad - deeply nested types
type Complex<T> = T extends Array<infer U>
? U extends Array<infer V>
? V extends Array<infer W>
? W extends Array<infer X>
? X
: never
: never
: never
: never;
// Good - iterative approach
type ElementType<T> = T extends (infer E)[] ? E : T;
type Deep1<T> = ElementType<T>;
type Deep2<T> = ElementType<Deep1<T>>;Use Type Aliases for Reusability
Extract common patterns:
// Define once, reuse everywhere
type ID = string | number;
type Timestamp = number;
type Optional<T> = T | null | undefined;
interface User {
id: ID;
createdAt: Timestamp;
lastLogin: Optional<Timestamp>;
}Leverage Inference
Let TypeScript infer when possible:
// Don't over-annotate
const users = [
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' }
]; // Type inferred automatically
// Use inference in generics
function identity<T>(value: T): T {
return value;
}
const num = identity(42); // T inferred as 42 (literal type)Avoid Type Computation Overhead
// Bad - expensive type computation on every use
type ExpensiveUnion<T> = T extends any
? { [K in keyof T]: SomeComplexType<T[K]> }
: never;
// Good - compute once, reuse
type PrecomputedType = ExpensiveUnion<MyType>;
function useType(value: PrecomputedType) { }Use Index Signatures Wisely
// Bad - loses type safety
interface LooseMap {
[key: string]: any;
}
// Good - constrained types
interface TypedMap {
[key: string]: string | number;
}
// Better - use Record for known types
type StrictMap = Record<'id' | 'name' | 'age', string | number>;Optimize Union Types
// Bad - large union causes slow checking
type ManyStrings = 'a' | 'b' | 'c' | /* ...100 more */ | 'z';
// Good - use branded types or enums for large sets
enum StringEnum {
A = 'a',
B = 'b',
// ...
}Template Literal Types
String type manipulation at compile time:
// Event handler types
type EventNames = 'click' | 'focus' | 'blur';
type EventHandlers = `on${Capitalize<EventNames>}`;
// 'onClick' | 'onFocus' | 'onBlur'
// URL path types
type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
type Endpoint = `/api/${'users' | 'posts' | 'comments'}`;
type Route = `${HTTPMethod} ${Endpoint}`;
// 'GET /api/users' | 'POST /api/users' | ...
// CSS property types
type CSSUnit = 'px' | 'em' | 'rem' | '%';
type Size = `${number}${CSSUnit}`;
const width: Size = '100px'; // Valid
const height: Size = '2em'; // Valid
// const invalid: Size = '100'; // ErrorNested Template Literals
type DeepKey<T> = T extends object
? {
[K in keyof T & string]: K | `${K}.${DeepKey<T[K]>}`;
}[keyof T & string]
: never;
interface Config {
database: {
host: string;
port: number;
credentials: {
username: string;
password: string;
};
};
}
type ConfigKeys = DeepKey<Config>;
// 'database' | 'database.host' | 'database.port' |
// 'database.credentials' | 'database.credentials.username' | ...Testing Type-Safe Code
Type Assertion Tests
// Type equality checker
type AssertEqual<T, U> = T extends U ? (U extends T ? true : false) : false;
type Test1 = AssertEqual<Pick<User, 'name'>, { name: string }>; // true
type Test2 = AssertEqual<string, number>; // falseCompile-Time Validation
// Expect type to match
function expectType<T>(value: T): T {
return value;
}
const user: User = { id: 1, name: 'John', email: 'john@example.com', password: 'secret' };
expectType<UserResponse>(user); // Error: password should not existTest Helper Types
// Assert never (for exhaustiveness checking)
type AssertNever<T extends never> = T;
// Assert extends
type AssertExtends<T, U extends T> = U;
// Assert assignable
type AssertAssignable<T, U> = U extends T ? true : false;Runtime Type Testing
import { expectType, expectError, expectAssignable } from 'tsd';
// Test type inference
const result = identity(42);
expectType<number>(result);
// Test error cases
// @ts-expect-error
expectError(getUser('invalid-id'));
// Test assignability
interface Base { id: number; }
interface Extended extends Base { name: string; }
expectAssignable<Base>({} as Extended);Property-Based Type Testing
// Test all properties of a type
type TestUserProperties = {
[K in keyof User]: User[K] extends string | number ? true : false;
};
// Ensure required properties exist
type RequiredFields = Required<Pick<User, 'id' | 'name' | 'email'>>;Testing Discriminated Unions
type State =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: string };
// Test exhaustiveness
function testExhaustive(state: State) {
switch (state.status) {
case 'idle':
case 'loading':
case 'success':
return;
default:
// Should be never
const _exhaustive: never = state;
return _exhaustive;
}
}Type Coverage Tools
Use type-coverage to ensure high type safety:
npm install --save-dev type-coverage
# Check type coverage
npx type-coverage
# Require 100% coverage
npx type-coverage --at-least 100Testing Frameworks
- tsd: Test TypeScript type definitions
- dtslint: Linter for .d.ts files
- type-coverage: Measure type coverage percentage
- ts-expect: Runtime type checking for tests
Type Guards
Runtime type checking with type narrowing:
Basic Type Guards
// Basic type guard
function isString(value: unknown): value is string {
return typeof value === 'string';
}
// Discriminated union guard
interface Success {
status: 'success';
data: string;
}
interface Error {
status: 'error';
message: string;
}
type Result = Success | Error;
function isSuccess(result: Result): result is Success {
return result.status === 'success';
}
function handleResult(result: Result) {
if (isSuccess(result)) {
console.log(result.data); // Type narrowed to Success
} else {
console.log(result.message); // Type narrowed to Error
}
}Generic Type Guards
function isArrayOf<T>(
value: unknown,
check: (item: unknown) => item is T
): value is T[] {
return Array.isArray(value) && value.every(check);
}
const data: unknown = [1, 2, 3];
if (isArrayOf(data, (x): x is number => typeof x === 'number')) {
data.forEach(n => n.toFixed(2)); // Type: number[]
}Assertion Functions
// Type assertion function
function assertString(value: unknown): asserts value is string {
if (typeof value !== 'string') {
throw new Error('Not a string');
}
}
const input: unknown = 'hello';
assertString(input);
// input is now narrowed to string
input.toUpperCase(); // ValidType Inference Techniques
Leverage TypeScript's type inference:
Const Assertions
// Without const assertion
const colors1 = ['red', 'green', 'blue'];
// Type: string[]
// With const assertion
const colors2 = ['red', 'green', 'blue'] as const;
// Type: readonly ['red', 'green', 'blue']
// Narrow object types
const config = {
endpoint: '/api/users',
method: 'GET'
} as const;
// Type: { readonly endpoint: '/api/users'; readonly method: 'GET' }Inference from Implementation
// Infer from function implementation
function createAction<T extends string, P>(
type: T,
payload: P
) {
return { type, payload };
}
const action = createAction('UPDATE_USER', { id: 1, name: 'John' });
// Type: { type: 'UPDATE_USER'; payload: { id: number; name: string } }Inference with Generics
// Let TypeScript infer generic types
function identity<T>(value: T): T {
return value;
}
const num = identity(42); // T inferred as 42 (literal type)
const str = identity('hello'); // T inferred as 'hello'
// Inference from array methods
const numbers = [1, 2, 3];
const doubled = numbers.map(n => n * 2); // Type: number[]Discriminated Union Inference
// Use in discriminated unions
type Action =
| ReturnType<typeof createAction<'INCREMENT'>>
| ReturnType<typeof createAction<'DECREMENT'>>;
function reducer(state: number, action: Action): number {
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
}
}Tuple Inference
// Infer tuple types
function tuple<T extends any[]>(...args: T): T {
return args;
}
const pair = tuple(1, 'hello'); // Type: [number, string]
const triple = tuple(1, 'hello', true); // Type: [number, string, boolean]Contextual Typing
// Type inferred from context
interface Point {
x: number;
y: number;
}
const points: Point[] = [
{ x: 0, y: 0 }, // Type inferred from array type
{ x: 1, y: 1 }
];
// Callback inference
['1', '2', '3'].map(str => parseInt(str)); // str inferred as stringUtility Types Composition
Combine utility types for complex transformations:
Deep Transformations
// Deep partial
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
// Make specific keys required
type RequireKeys<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;
interface User {
id?: number;
name?: string;
email?: string;
}
type UserWithId = RequireKeys<User, 'id'>;
// { id: number; name?: string; email?: string }Function Type Utilities
// Extract function parameter types
type Parameters<T extends (...args: any[]) => any> =
T extends (...args: infer P) => any ? P : never;
function processUser(id: number, name: string): void {}
type ProcessUserParams = Parameters<typeof processUser>;
// [number, string]
// Extract return type
type ReturnType<T extends (...args: any[]) => any> =
T extends (...args: any[]) => infer R ? R : never;Advanced Transformations
// Flatten nested types
type Flatten<T> = T extends any[] ? T[number] : T;
type Nested = (string | number)[][];
type Flat = Flatten<Nested>; // (string | number)[]
// Exclude nullable values
type NonNullable<T> = T extends null | undefined ? never : T;
// Recursive type definitions
type JSONValue =
| string
| number
| boolean
| null
| JSONValue[]
| { [key: string]: JSONValue };Readonly Utilities
// Deep readonly
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object
? DeepReadonly<T[P]>
: T[P];
};
// Mutable (remove readonly)
type Mutable<T> = {
-readonly [P in keyof T]: T[P];
};Key Manipulation
// Extract keys of specific type
type KeysOfType<T, U> = {
[K in keyof T]: T[K] extends U ? K : never;
}[keyof T];
interface Example {
name: string;
age: number;
active: boolean;
count: number;
}
type StringKeys = KeysOfType<Example, string>; // 'name'
type NumberKeys = KeysOfType<Example, number>; // 'age' | 'count'