TypeScript Cheatsheet
60+ TypeScript reference cards covering types, interfaces, generics, utility types, type guards, and advanced patterns. Click any card to copy the example.
87 of 87 items
TypeScript vs JavaScript
TypeScript is a statically typed superset of JavaScript that compiles to plain JavaScript. The core benefit is catching type errors at compile time rather than at runtime — before code ever reaches production. TypeScript adds a rich type system on top of JavaScript's dynamic types: you get primitive types (string, number, boolean), complex types (interfaces, classes, generics), and meta-types (utility types, conditional types, mapped types).
The trade-off is a build step and some additional verbosity, but modern tooling (Next.js, Vite, esbuild) strips types with near-zero overhead. The payoff is dramatic in larger codebases: refactoring is safe, IDE autocomplete is precise, and entire categories of bugs (undefined is not a function, cannot read property of null) are eliminated before runtime. Most teams adopting TypeScript report fewer production incidents within weeks.
Generics & Utility Types
Generics are TypeScript's mechanism for writing reusable, type-safe abstractions. Rather than accepting any and losing type information, generics preserve the concrete type at each use site. A function like function wrap<T>(x: T): T[] returns an array of exactly the same type as its input — TypeScript knows wrap(42) returns number[], not any[].
Utility types are built-in generic helpers that transform existing types. Instead of duplicating a type definition just to make all fields optional, you write Partial<User>. Instead of rewriting a type to remove the id field, you write Omit<User, 'id'>. This eliminates type drift between related definitions and makes your type system self-documenting — the relationship between types is explicit in the code.
Type Safety Best Practices
Enable "strict": true in your tsconfig from day one. This activates strictNullChecks, noImplicitAny, and several other checks that catch the most common TypeScript mistakes. Avoid any — use unknown instead and narrow with type guards. Prefer interfaces for object shapes used as public APIs (they support declaration merging and produce better error messages). Use as const on configuration objects and arrays to get narrow literal types without manual annotation.
Write discriminated unions instead of boolean flags on objects — a kind or type literal property enables exhaustive switch/case checking. Use satisfies (TypeScript 4.9+) when you want to validate an object matches a type without widening its inferred type. Keep utility type transformations in a shared types.ts file to avoid duplication across your codebase.
Frequently Asked Questions
What is the difference between type and interface in TypeScript?
Both can describe object shapes, but they differ in several ways. Interfaces support declaration merging — you can declare the same interface twice and TypeScript merges them, which is useful for extending third-party module types. Types (type aliases) are more flexible: they can represent primitives, unions, intersections, tuples, mapped types, and conditional types. A common rule of thumb is to use interface for public API shapes and object structures, and type for everything else.
What is the difference between unknown and any in TypeScript?
Both accept any value, but unknown is the type-safe alternative. When you assign a value as any, TypeScript lets you perform any operation on it without checks — you essentially opt out of type checking. With unknown, you must narrow the type first (using typeof, instanceof, or a type guard) before you can use the value in a type-specific way. Prefer unknown when you receive external data and want to enforce that you check the type before accessing it.
How do TypeScript generics work?
Generics let you write reusable functions, classes, and interfaces that work with any type while preserving type information. You declare a type parameter (like T) inside angle brackets, and TypeScript infers or accepts the concrete type at each call site. For example, function identity<T>(arg: T): T returns exactly the same type as its argument — TypeScript knows that identity('hello') returns string. You can constrain generics with extends (e.g., <T extends object>) to restrict which types are accepted.
What are TypeScript utility types and which ones should I know?
Utility types are built-in generic types that perform common type transformations. The most important ones are: Partial<T> (all properties optional), Required<T> (all properties required), Readonly<T> (all properties read-only), Pick<T,K> (select specific keys), Omit<T,K> (exclude specific keys), Record<K,V> (key-value map), ReturnType<T> (extract a function's return type), and Awaited<T> (unwrap a Promise's resolved type). These let you derive new types from existing ones instead of duplicating definitions.
What does TypeScript strict mode enable?
Enabling "strict": true in tsconfig.json turns on a suite of strict checks as a group: strictNullChecks (null and undefined are not assignable to other types), strictFunctionTypes (contra-variance for function parameters), strictBindCallApply (typed bind/call/apply), strictPropertyInitialization (class properties must be initialized), noImplicitAny (error when TypeScript infers any), and noImplicitThis (error on implicit this). Starting with strict mode is strongly recommended for any new TypeScript project — it catches a large class of runtime bugs at compile time.