Creates named reference to any type - unions, intersections, primitives
Complex unions, utility compositions
Interface
interface Name { }
Defines object shape - extendable and mergeable
Object contracts, class implementations
Interface Extension
extends Interface
Inherit properties from parent interfaces
Hierarchical type relationships
Type Intersection
Type1 & Type2
Combine multiple type aliases
Mixins, composed types
Declaration Merging
Multiple interface declarations
Interfaces with same name automatically merge - not available for types
Augmenting libraries, modules
Type vs Interface
Feature
Type
Interface
Unions
✓
✗
Intersections
✓
Use extends
Primitives
✓
✗
Tuples
✓
✗
Merging
✗
✓
Computed Props
✓
✗
Example: Type alias
type ID = string | number;type Point = [number, number];type Callback = (data: string) => void;
Example: Interface
interface User { id: number; name: string;}interface Admin extends User { permissions: string[];}
Example: Type aliases vs interfaces
// Type alias - flexible for any typetype StringOrNumber = string | number;type Coordinate = [number, number];type UserID = string;// Interface - best for object shapesinterface Person { name: string; age: number;}interface Employee extends Person { employeeId: string; department: string;}// Declaration merging (interfaces only)interface Window { customProperty: string;}interface Window { anotherProperty: number;}// Window now has both properties// Type compositiontype WithTimestamp = { timestamp: Date };type TrackedPerson = Person & WithTimestamp;
2. Enum Types and Const Enums
Type
Syntax
Description
Compiled Output
Numeric Enum
enum Name { A, B }
Auto-incrementing numeric values starting from 0
Object with reverse mapping
String Enum
enum { A = "a" }
Explicit string values - no auto-increment
Object without reverse mapping
Heterogeneous Enum
enum { A = 1, B = "b" }
Mix of numeric and string values - not recommended
Mixed object
Const Enum
const enum Name { }
Completely inlined at compile time - no runtime object
Values inlined, no object
Computed Member
A = expression
Value computed from expression
Evaluated at compile time
Ambient Enum
declare enum Name { }
Describe existing enum from external source
Type-only, no JS output
Example: Enum types
// Numeric enumenum Direction { Up, // 0 Down, // 1 Left, // 2 Right // 3}// String enum (preferred)enum Status { Pending = "PENDING", Active = "ACTIVE", Completed = "COMPLETED"}// Custom numeric valuesenum HttpStatus { OK = 200, NotFound = 404, ServerError = 500}// Const enum (performance optimization)const enum Colors { Red = "#FF0000", Green = "#00FF00", Blue = "#0000FF"}// Usagelet status: Status = Status.Active;let color = Colors.Red; // Inlined to "#FF0000" at compile time
Note: Prefer string enums or union of
string literals over numeric enums for better debugging and runtime safety. Use const enum
for zero-runtime overhead.
3. Type Assertions
Syntax
Form
Description
Use Case
as Syntax
value as Type
Preferred modern syntax - works in JSX
All scenarios, especially TSX/JSX files
Angle Bracket
<Type>value
Original syntax - conflicts with JSX
Non-JSX files only
Double Assertion
x as unknown as T
Force assertion through unknown - escape hatch
Incompatible types, migrations
Const Assertion TS 3.4+
as const
Make literal types readonly and narrow to literal
Immutable data, exact types
Non-null Assertion
value!
Assert value is not null/undefined
When you know value exists
Example: Type assertions
// as syntax (preferred)let input = document.getElementById("input") as HTMLInputElement;input.value = "Hello";// Angle bracket syntax (avoid in JSX)let element = <HTMLElement>document.querySelector(".item");// Double assertion (escape hatch)let num = "123" as unknown as number; // Dangerous!// Const assertion - deep readonlylet config = { host: "localhost", port: 3000, flags: ["debug", "verbose"]} as const;// Type: { readonly host: "localhost"; readonly port: 3000; readonly flags: readonly ["debug", "verbose"] }// Array as constlet arr = [1, 2, 3] as const; // Type: readonly [1, 2, 3]// Non-null assertionfunction process(value: string | null) { let length = value!.length; // Assert value is not null}
Warning: Type assertions bypass type checking - use sparingly. Prefer type guards and
narrowing. as const is safe and recommended.
4. Type Guards and Narrowing Techniques
Technique
Syntax
Description
Narrows To
typeof Guard
typeof x === "type"
Check primitive types at runtime
string, number, boolean, etc.
instanceof Guard
x instanceof Class
Check if object is instance of class
Class type
in Operator
"prop" in obj
Check property existence
Type with that property
Truthiness Check
if (value) { }
Filters out null, undefined, 0, "", false
Non-nullable type
Equality Check
x === value
Compare with specific value
Literal type
User-defined Guard
is Type
Custom type predicate function
Custom type
Discriminated Union
switch (x.kind)
Use discriminant property to narrow
Specific union member
Example: Built-in type guards
function process(value: string | number) { // typeof guard if (typeof value === "string") { console.log(value.toUpperCase()); // string } else { console.log(value.toFixed(2)); // number }}// instanceof guardfunction handle(x: Date | string) { if (x instanceof Date) { console.log(x.getFullYear()); // Date } else { console.log(x.length); // string }}// in operatortype Fish = { swim: () => void };type Bird = { fly: () => void };function move(animal: Fish | Bird) { if ("swim" in animal) { animal.swim(); // Fish } else { animal.fly(); // Bird }}// Truthiness narrowingfunction print(text: string | null) { if (text) { console.log(text.toUpperCase()); // string }}
Example: User-defined type guards
// Type predicate functionfunction isString(value: unknown): value is string { return typeof value === "string";}function process(input: unknown) { if (isString(input)) { console.log(input.toUpperCase()); // Narrowed to string }}// Complex type guardinterface Cat { meow: () => void; }interface Dog { bark: () => void; }function isCat(animal: Cat | Dog): animal is Cat { return (animal as Cat).meow !== undefined;}function speak(animal: Cat | Dog) { if (isCat(animal)) { animal.meow(); } else { animal.bark(); }}
5. Discriminated Unions and Tagged Unions
Component
Description
Example
Discriminant Property
Common property with literal type - acts as type tag
kind: "success" | "error"
Union Members
Each type in union has unique discriminant value
Success | Error
Exhaustiveness Check
Ensure all union cases are handled
default: assertNever(x)
Example: Discriminated unions for safe state management
// Define discriminated uniontype Result<T> = | { status: "loading" } | { status: "success"; data: T } | { status: "error"; error: Error };function handle<T>(result: Result<T>) { switch (result.status) { case "loading": console.log("Loading..."); break; case "success": console.log(result.data); // Type: T break; case "error": console.log(result.error.message); // Type: Error break; }}// Shape discriminated uniontype Shape = | { kind: "circle"; radius: number } | { kind: "rectangle"; width: number; height: number } | { kind: "triangle"; base: number; height: number };function area(shape: Shape): number { switch (shape.kind) { case "circle": return Math.PI * shape.radius ** 2; case "rectangle": return shape.width * shape.height; case "triangle": return 0.5 * shape.base * shape.height; }}// Exhaustiveness checkfunction assertNever(x: never): never { throw new Error("Unexpected: " + x);}function getArea(shape: Shape): number { switch (shape.kind) { case "circle": return Math.PI * shape.radius ** 2; case "rectangle": return shape.width * shape.height; case "triangle": return 0.5 * shape.base * shape.height; default: return assertNever(shape); // Compile error if case missing }}
6. Mapped Types and Key Remapping
Pattern
Syntax
Description
Result
Basic Mapped Type
{ [K in Keys]: Type }
Iterate over keys and map to type
New object type with mapped properties
Readonly Mapping
{ readonly [K in Keys] }
Make all properties readonly
Readonly<T>
Optional Mapping
{ [K in Keys]?: Type }
Make all properties optional
Partial<T>
Remove Modifiers
-readonly, -?
Remove readonly or optional modifiers
Required<T>
Key Remapping TS 4.1+
as NewKey
Transform key names during mapping
Renamed property keys
Filter Keys
as K extends X ? K : never
Conditionally include/exclude keys
Filtered property set
Example: Mapped types
// Basic mapped typetype Nullable<T> = { [K in keyof T]: T[K] | null;};type User = { name: string; age: number };type NullableUser = Nullable<User>;// Result: { name: string | null; age: number | null }// Add/remove modifierstype Mutable<T> = { -readonly [K in keyof T]: T[K];};type Required<T> = { [K in keyof T]-?: T[K];};// Key remapping with template literalstype Getters<T> = { [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];};type Person = { name: string; age: number };type PersonGetters = Getters<Person>;// Result: { getName: () => string; getAge: () => number }// Filter properties by typetype StringKeys<T> = { [K in keyof T as T[K] extends string ? K : never]: T[K];};type Data = { id: number; name: string; email: string; count: number };type StringProps = StringKeys<Data>;// Result: { name: string; email: string }
7. Conditional Types and Utility Types
Pattern
Syntax
Description
Use Case
Conditional Type
T extends U ? X : Y
Type-level if-else - select type based on condition
Dynamic type selection
infer Keyword TS 2.8+
infer R
Extract type from another type within conditional
Return types, array elements
Distributive
T extends U when T is union
Conditional applied to each union member separately
Union transformations
Non-Distributive
[T] extends [U]
Conditional applied to whole type, not distributed
Exact type matching
Example: Conditional types
// Basic conditional typetype IsString<T> = T extends string ? true : false;type A = IsString<string>; // truetype B = IsString<number>; // false// Extract return type with infertype ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;type Func = () => string;type Result = ReturnType<Func>; // string// Extract array element typetype ElementType<T> = T extends (infer U)[] ? U : never;type Items = ElementType<number[]>; // number// Distributive conditional (union)type ToArray<T> = T extends any ? T[] : never;type Arrays = ToArray<string | number>;// Result: string[] | number[] (distributed)// Non-distributive (tuple)type ToArrayNonDist<T> = [T] extends [any] ? T[] : never;type TupleArray = ToArrayNonDist<string | number>;// Result: (string | number)[] (not distributed)// Complex conditional - function overloadstype UnwrapPromise<T> = T extends Promise<infer U> ? U : T;type Value1 = UnwrapPromise<Promise<string>>; // stringtype Value2 = UnwrapPromise<number>; // number// Recursive conditional typetype Flatten<T> = T extends Array<infer U> ? Flatten<U> : T;type Deep = Flatten<number[][][]>; // number
Key Takeaways
Use interfaces for object shapes and type
aliases for unions/intersections
Prefer string enums or union literals over numeric enums
as const assertions are safe; regular type assertions are dangerous
Type guards enable safe type narrowing at runtime
Discriminated unions with exhaustiveness checks ensure type safety