TypeScript provides a sophisticated static type system that enables developers to build highly flexible and type-safe components. By unlocking advanced type mechanics, you can eliminate structural duplication and verify runtime constraints at compile time.
This reference sheet covers generics, utility type functions, conditional structures, mapped modifiers, template literal types, and custom type guards.
Before diving into this cheatsheet, check out my previous deep-dive on Hono Edge Web Framework Cheatsheet: The Complete Reference to see how we structured these patterns in practice.
Mastering Generics
Generics allow you to write algorithms and structures that operate on variable types while maintaining absolute type fidelity.
// 1. Generic Functions with Constraints
interface Identifiable {
id: string | number;
}
function getRecordById<T extends Identifiable>(records: T[], id: T['id']): T | undefined {
return records.find(item => item.id === id);
}
// 2. Generic Interfaces and Defaults
interface ApiResponse<Data = Record<string, unknown>, Meta = Record<string, unknown>> {
status: 'success' | 'error';
data: Data;
meta?: Meta;
}
interface UserProfile {
username: string;
email: string;
}
const response: ApiResponse<UserProfile> = {
status: 'success',
data: {
username: 'dev_scarlett',
email: 'scarlett@meshworld.in'
}
};
Leveraging Built-in Utility Types
TypeScript comes equipped with standard utility types designed to transform existing interfaces.
interface User {
id: number;
name: string;
email: string;
role: 'admin' | 'user';
age?: number;
}
// Make all fields optional (ideal for update payloads)
type PartialUser = Partial<User>;
// Make all fields mandatory
type CompleteUser = Required<User>;
// Make all fields immutable
type ImmutableUser = Readonly<User>;
// Construct a type consisting of select properties
type UserContactInfo = Pick<User, 'name' | 'email'>;
// Construct a type by removing select properties
type UserWithoutCredentials = Omit<User, 'id' | 'role'>;
// Maps keys of one type to another type
type UserDirectory = Record<string, User>;
Harnessing Conditional Types
Conditional types select one of two possible types based on a constraint check, utilizing syntax similar to a ternary operator.
// Syntax: T extends U ? X : Y
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
// Dynamic API Response Payload resolution
interface TextMessage {
text: string;
}
interface ImageMessage {
imageUrl: string;
}
type MessagePayload<T extends 'text' | 'image'> = T extends 'text'
? TextMessage
: ImageMessage;
function createMessage<T extends 'text' | 'image'>(type: T, payload: MessagePayload<T>) {
return { type, payload };
}
The infer Keyword
Use infer within a conditional type to declare a placeholder type variable to be extracted.
// Extract the resolved value of a Promise
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
type ResolvedString = UnwrapPromise<Promise<string>>; // string
type StandardNumber = UnwrapPromise<number>; // number
Operating Mapped Types
Mapped types allow you to create new types based on the keys of an existing type by iterating over them.
interface FeatureFlags {
enableSearch: boolean;
enableAnalytics: boolean;
enableBetaDashboard: boolean;
}
// Transform all property types to functions returning that type
type FeatureGetters<T> = {
[K in keyof T]: () => T[K];
};
type AppFeatureFlags = FeatureGetters<FeatureFlags>;
/*
Resolves to:
{
enableSearch: () => boolean;
enableAnalytics: () => boolean;
enableBetaDashboard: () => boolean;
}
*/
// Modify properties: remove readonly modifiers and make all optional
type MutablePartial<T> = {
-readonly [K in keyof T]?: T[K];
};
Constructing Template Literal Types
Template literal types build on string literal types, allowing you to combine and manipulate strings at the type level.
type Protocol = 'http' | 'https';
type Port = 80 | 443 | 8080;
// Generate all permutations of valid local addresses
type LocalUrl = `${Protocol}://localhost:${Port}`;
// Resolves to: "http://localhost:80" | "http://localhost:443" | ...
// Auto-prefix event types
type Event = 'click' | 'hover' | 'submit';
type OnEvent = `on${Capitalize<Event>}`;
// Resolves to: "onClick" | "onHover" | "onSubmit"
Implementing Type Guards & Assertions
Type guards allow you to narrow down the type of an object within a conditional block using standard type predicate syntax.
interface Admin {
name: string;
privileges: string[];
}
interface Editor {
name: string;
sections: string[];
}
type Staff = Admin | Editor;
// 1. Custom Type Guard using 'is'
function isAdmin(staff: Staff): staff is Admin {
return (staff as Admin).privileges !== undefined;
}
function executeStaffAction(staff: Staff) {
if (isAdmin(staff)) {
// TypeScript now knows 'staff' is strictly Admin here
console.log(`Admin privileges: ${staff.privileges.join(', ')}`);
} else {
// TypeScript knows 'staff' is strictly Editor here
console.log(`Editor assigned sections: ${staff.sections.join(', ')}`);
}
}
// 2. Custom Type Assertion using 'asserts'
function assertIsString(value: unknown): asserts value is string {
if (typeof value !== 'string') {
throw new Error('Value must be a string');
}
}
function processInput(input: unknown) {
assertIsString(input);
// input is typed as string from this point forward
console.log(input.toUpperCase());
} Related Articles
Deepen your understanding with these curated continuations.
Hono Edge Web Framework Cheatsheet: The Complete Reference
Master Hono Edge Web Framework: routing, middleware, context, custom handlers, validation, and cloud deployments.
React Hooks & Custom Hooks Cheatsheet: The Complete Reference
A complete guide to React Hooks and writing robust custom hooks with optimal rendering performance.
PyTorch & CUDA ML Operations Cheatsheet: The Complete Reference
Optimize deep learning workloads: PyTorch tensor manipulations, CUDA memory management, multi-GPU training, and mixed precision.