// Interface (can be merged)
interface User { name: string; }
interface User { age: number; }
// Type (can define unions)
type ID = string | number;
Real World Example
// Use Interface for Object Shapes (e.g., React Props)
interface ButtonProps {
label: string;
onClick: () => void;
}
// Use Type for Unions or Complex Mappings
type ButtonVariant = 'primary' | 'secondary' | 'danger';
They are very similar, but:
// Interface
interface User {
name: string;
}
interface User { // Merges with above
age: number;
}
// Type
type Status = "success" | "error"; // Union (Interface can't do this)
let loose: any = 4;
loose.method(); // No error (dangerous)
let strict: unknown = 4;
// strict.method(); // Error: Object is of type 'unknown'
Real World Example
// Use 'unknown' for external data (e.g., API response)
function parseResponse(response: unknown) {
if (typeof response === 'string') {
return JSON.parse(response); // Safe now
}
throw new Error("Invalid response");
}
any: Turns off type checking. "I don't care." Dangerous.
unknown: "I don't know yet." You must check the type (narrowing) before using it. Safe.
let x: unknown = "hello";
// console.log(x.length); // Error: Object is of type 'unknown'.
if (typeof x === "string") {
console.log(x.length); // OK
}
function identity<T>(arg: T): T {
return arg;
}
const num = identity(5); // T is number
Real World Example
// Reusable API Response Wrapper
interface ApiResponse<Data> {
status: number;
data: Data;
error?: string;
}
type UserResponse = ApiResponse<{ id: number; name: string }>;
Generics allow you to create reusable components/functions that work with multiple types while retaining type safety.
// Without Generics (Lost type info)
function identity(arg: any): any { return arg; }
// With Generics (Preserves type info)
function identity<T>(arg: T): T {
return arg;
}
const num = identity(5); // TS knows 'num' is number
const str = identity("hi"); // TS knows 'str' is string
function identity<T>(arg: T): T {
return arg;
}
let output = identity<string>("myString");
Real World Example
// Reusable API Response Wrapper
interface ApiResponse<T> {
data: T;
status: number;
error?: string;
}
// Usage
interface User { id: number; name: string; }
const response: ApiResponse<User> = await fetchUser();
Generics allow you to create reusable components that work with a variety of types rather than a single one. They act like variables for types.
interface Todo { id: number; title: string; desc: string; }
type P = Partial<Todo>; // { id?: number; title?: string; ... }
type K = Pick<Todo, 'title'>; // { title: string; }
type O = Omit<Todo, 'desc'>; // { id: number; title: string; }
Real World Example
// Updating a user profile (not all fields required)
function updateUser(id: number, fields: Partial<User>) {
// ...
}
// Component Props excluding internal props
type PublicProps = Omit<ButtonProps, 'internalId'>;
interface Todo {
id: number;
title: string;
desc: string;
}
// Partial: All properties become optional
type UpdateTodo = Partial<Todo>; // { id?: number, title?: string... }
// Pick: Select specific keys
type TodoPreview = Pick<Todo, "id" | "title">;
// Omit: Remove specific keys
type TodoInput = Omit<Todo, "id">;
interface User {
id: number;
name: string;
email: string;
}
// Pick: Select specific keys
type UserPreview = Pick<User, "id" | "name">;
// Omit: Remove specific keys
type UserInput = Omit<User, "id">;
// Partial: Make all keys optional
type UserUpdate = Partial<User>;
TypeScript provides built-in utility types to facilitate common type transformations.
let x = 10; // TS infers x is number
// x = "hello"; // Error
Real World Example
// Return type inferred automatically
function add(a: number, b: number) {
return a + b; // Inferred as number
}
// Array type inferred
const numbers = [1, 2, 3]; // number[]
TS automatically deduces the type based on the value. let x = 5; is inferred as number. You don't always need to write types explicitly.
let tuple: [string, number];
tuple = ["hello", 10]; // OK
// tuple = [10, "hello"]; // Error
Real World Example
// React Hooks return tuples
function useState<T>(initial: T): [T, (v: T) => void] {
// ...
return [state, setState];
}
const [count, setCount] = useState(0);
An array with a fixed number of elements whose types are known. let x: [string, number] = ["hello", 10];.
type Union = string | number; // One OR the other
type Intersection = { a: string } & { b: number }; // Both
Real World Example
// Union: Handling different states
type State = { status: 'loading' } | { status: 'success', data: any };
// Intersection: Combining Props
type ButtonProps = BaseProps & { variant: 'primary' | 'secondary' };
Union (|): A value can be one of several types. string | number.
Intersection (&): Combines multiple types into one. TypeA & TypeB (must have properties of both).
function throwError(msg: string): never {
throw new Error(msg);
}
Real World Example
// Exhaustive checking in switch
function getArea(shape: Shape) {
switch (shape.kind) {
case 'circle': return ...;
case 'square': return ...;
default:
const _exhaustiveCheck: never = shape; // Error if new shape added
return _exhaustiveCheck;
}
}
Represents values that never occur. Used for functions that throw errors or infinite loops, or for exhaustive checks in switch statements.
interface Person { name: string; age: number; }
type Keys = keyof Person; // "name" | "age"
Real World Example
// Type-safe property access
function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
const user = { name: "Alice", age: 30 };
getProperty(user, "name"); // OK
// getProperty(user, "email"); // Error
Takes an object type and produces a string or numeric literal union of its keys. keyof User might be "name" | "age".
function log(): void { console.log("Hi"); }
function getVal(): undefined { return undefined; }
Real World Example
// Callback that doesn't return anything
function runCallback(cb: () => void) {
cb();
}
// Even if cb returns something, it's ignored
void means a function doesn't return a value. undefined is a value. A function returning void can return undefined, but void is semantically "no return".
interface Config {
readonly apiUrl: string;
}
const c: Config = { apiUrl: "http://..." };
// c.apiUrl = "new"; // Error
Real World Example
// Immutable State in Redux/Zustand
type State = {
readonly count: number;
readonly user: Readonly<User>;
};
Prevents a property of an object or class from being changed after initialization.
let x = "hello" as const; // Type is "hello"
const arr = [1, 2] as const; // Type is readonly [1, 2]
Real World Example
// Redux Action Types
const ACTIONS = {
LOGIN: 'LOGIN',
LOGOUT: 'LOGOUT'
} as const;
// ACTIONS.LOGIN is type "LOGIN", not string
Tells the compiler to infer the narrowest possible type (literals) and make properties readonly. let x = "hello" as const; (Type is "hello", not string).
enum Direction { Up, Down, Left, Right } // 0, 1, 2, 3
enum Status { Success = "OK", Error = "ERR" }
Real World Example
// Better alternative: Union Types
type Status = 'loading' | 'success' | 'error';
// Less runtime overhead than Enums
Numeric: Auto-incrementing numbers. String: Explicit string values. Prefer Union Types over Enums in modern TS.
abstract class Animal {
abstract makeSound(): void;
move() { console.log("Moving"); }
}
Real World Example
// Base Service Class
abstract class BaseService<T> {
abstract getAll(): Promise<T[]>;
abstract getById(id: string): Promise<T>;
}
Classes that cannot be instantiated directly. Used as base classes for other classes to extend.
class Car {
public make: string;
private vin: string;
protected speed: number;
}
Real World Example
// Encapsulation in Services
class AuthService {
private token: string;
public login() { ... }
private validateToken() { ... }
}
class Dog extends Animal { ... } // Inherits code
class Car implements Vehicle { ... } // Enforces shape
Real World Example
// React Class Components
class MyComp extends React.Component<Props> { ... }
// Custom Class implementing Interface
class LocalStorageService implements StorageService {
save(key: string, val: string) { ... }
}
extends: Inherit from a class (code reuse). implements: Contract that a class must satisfy an interface (shape enforcement).
if (typeof x === "string") { ... }
if (x instanceof Date) { ... }
if ("role" in user) { ... }
Real World Example
// Handling API Errors
try {
await apiCall();
} catch (e) {
if (e instanceof Error) {
console.log(e.message);
}
}
Techniques to narrow down the type of a variable within a conditional block. typeof, instanceof, in.
function isString(x: any): x is string {
return typeof x === "string";
}
Real World Example
// Validating API Data
interface User { id: number; name: string; }
function isUser(obj: any): obj is User {
return obj && typeof obj.id === 'number';
}
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; side: number };
Real World Example
// Redux Actions
type Action =
| { type: 'LOGIN_SUCCESS'; payload: User }
| { type: 'LOGIN_FAIL'; error: string };
function reducer(state: State, action: Action) {
if (action.type === 'LOGIN_SUCCESS') {
// action.payload is available
}
}
A pattern where every type in a union has a common literal property (the discriminant) used to narrow the type.
interface Circle { kind: "circle"; radius: number; }
interface Square { kind: "square"; side: number; }
type Shape = Circle | Square;
const el = document.getElementById("app")!;
// I know 'app' exists, so don't worry TS.
Real World Example
// Refs in React (sometimes)
const inputRef = useRef<HTMLInputElement>(null);
// Later...
inputRef.current!.focus();
Tells TS that a value is not null or undefined, even if the type suggests it might be. Use with caution.
function getLength<T extends { length: number }>(arg: T) {
return arg.length;
}
Real World Example
// React Component Props Constraint
interface BaseProps { id: string; }
function Card<T extends BaseProps>(props: T) {
return <div id={props.id}>...</div>;
}
Restricting the types that can be used with a generic. function logLength<T extends { length: number }>(arg: T).
type Scores = Record<string, number>;
const s: Scores = { "Alice": 10, "Bob": 20 };
Real World Example
// Mapping IDs to Data
type UserMap = Record<number, User>;
const usersById: UserMap = {
1: { id: 1, name: "Alice" },
2: { id: 2, name: "Bob" }
};
Constructs an object type whose property keys are K and whose property values are T.
type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
Real World Example
// Removing specific keys from a union of keys
type AllKeys = "id" | "name" | "email";
type PublicKeys = Exclude<AllKeys, "email">;
Constructs a type by excluding from T all union members that are assignable to U.
type T0 = Extract<"a" | "b" | "c", "a" | "f">; // "a"
Real World Example
// Extracting common types from two unions
type Common = Extract<TypeA | TypeB, TypeC>;
Constructs a type by extracting from T all union members that are assignable to U.
type T0 = NonNullable<string | number | undefined>; // string | number
Real World Example
// Ensuring a prop is present
type Props = { title?: string };
function render(title: NonNullable<Props['title']>) {
// title is string, not undefined
}
Constructs a type by excluding null and undefined from T.
function getUser() { return { name: "Alice", age: 20 }; }
type User = ReturnType<typeof getUser>;
Real World Example
// Getting Redux State type from reducer
type RootState = ReturnType<typeof rootReducer>;
Constructs a type consisting of the return type of function T.
function add(a: number, b: number) {}
type Args = Parameters<typeof add>; // [number, number]
Real World Example
// Reusing function parameters for a wrapper
type HandlerArgs = Parameters<typeof eventHandler>;
function wrapper(...args: HandlerArgs) {
console.log("Called");
eventHandler(...args);
}
Constructs a tuple type from the types used in the parameters of a function type T.
type OptionsFlags<Type> = {
[Property in keyof Type]: boolean;
};
Real World Example
// Creating a validation schema type from a model
type Validator<T> = {
[P in keyof T]: (value: T[P]) => boolean;
};
Creating new types based on old ones by iterating over property keys. { [P in K]: T }.
type IsString<T> = T extends string ? true : false;
Real World Example
// Flattening Array Types
type Flatten<T> = T extends any[] ? T[number] : T;
type Str = Flatten<string[]>; // string
T extends U ? X : Y. Selects one of two types based on a condition.
type World = "world";
type Greeting = `hello ${World}`; // "hello world"
Real World Example
// Generating Event Names
type Event = "click" | "hover";
type Handler = `on${Capitalize<Event>}`; // "onClick" | "onHover"
Builds types via string interpolation. type Greeting = `Hello ${string}`;.
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
Real World Example
// Unpacking Promise types
type Unpack<T> = T extends Promise<infer U> ? U : T;
type Data = Unpack<Promise<string>>; // string
Used within conditional types to infer a type variable to be used in the true branch.
declare const myLibrary: any;
Real World Example
// Declaring global variables injected by build tools
declare const __DEV__: boolean;
if (__DEV__) { console.log("Debug mode"); }
Used to tell the compiler about variables/functions that exist elsewhere (e.g., from a CDN script) without defining implementation.
// my-lib.d.ts
export function doSomething(a: string): number;
Real World Example
// Adding types to untyped JS libraries
// npm install @types/lodash
Files that contain only type information, no executable code. Used to provide types for JS libraries.
{
"compilerOptions": {
"target": "es6",
"strict": true
}
}
Real World Example
// Path Aliases
"paths": {
"@components/*": ["src/components/*"]
}
Root file of a TS project. Configures compiler options like target, module, strict, etc.
import { x } from "./file"; // Relative
import { y } from "react"; // Node_modules
Real World Example
// Configuring "baseUrl" and "paths" in tsconfig
// to support absolute imports
The process the compiler uses to figure out what an import refers to (Classic vs Node).
namespace Utils {
export function log(msg: string) { ... }
}
Utils.log("Hi");
Real World Example
// Mostly legacy. Use ES Modules instead.
// Sometimes used for merging types with classes/functions.
Older way to organize code. Modern TS prefers ES Modules (import/export). Avoid namespaces in new code.
interface Props { a?: number; }
type AllProps = Required<Props>; // { a: number; }
Real World Example
// Ensuring all config options are present internally
function init(config: Config) {
const fullConfig: Required<Config> = { ...defaults, ...config };
}
Constructs a type consisting of all properties of T set to required.
interface Todo { title: string; }
const todo: Readonly<Todo> = { title: "Buy milk" };
// todo.title = "New"; // Error
Real World Example
// Freezing state in functional programming
function update(state: Readonly<State>) {
return { ...state, count: state.count + 1 };
}
Constructs a type with all properties of T set to readonly.
// Covariant: Return types (Dog -> Animal)
// Contravariant: Argument types (Animal -> Dog)
Real World Example
// Assigning functions
type Logger = (msg: string) => void;
let log: Logger;
let logAny = (msg: any) => console.log(msg);
log = logAny; // OK (Contravariance)
Complex topic regarding how subtypes relate to supertypes in complex structures (like function args vs return types).
function makeDate(timestamp: number): Date;
function makeDate(m: number, d: number, y: number): Date;
function makeDate(mOrTimestamp: number, d?: number, y?: number): Date {
if (d !== undefined && y !== undefined) {
return new Date(y, mOrTimestamp, d);
} else {
return new Date(mOrTimestamp);
}
}
Real World Example
// jQuery-style APIs
// $(selector) vs $(element) vs $(callback)
Defining multiple function signatures for a single implementation to handle different argument combinations.
interface StringArray {
[index: number]: string;
}
const myArray: StringArray = ["Bob", "Fred"];
Real World Example
// Dictionary / Map
interface Cache {
[key: string]: any;
}
const cache: Cache = {};
cache["user_1"] = { ... };
Defining types for objects when you don't know the property names ahead of time. { [key: string]: number }.
interface Counter {
(start: number): string;
interval: number;
reset(): void;
}
Real World Example
// Libraries like Axios or jQuery
// axios('url') // function
// axios.get('url') // object property
An object that acts as both a function and an object (has properties). Common in libraries like jQuery or Axios.
function toHex(this: Number) {
return this.toString(16);
}
const fiveToHex: OmitThisParameter<typeof toHex> = toHex.bind(5);
Real World Example
// Binding methods in classes or utilities
// to ensure 'this' is handled correctly.
Removes the this parameter from a function type.
type ObjectDescriptor<D, M> = {
data?: D;
methods?: M & ThisType<D & M>; // 'this' inside methods is D & M
};
Real World Example
// Vue 2 Options API
// 'this' in methods refers to data + props + methods
Marker utility type used to enable contextual this type in object literals.
interface Point { x: number; y: number; }
function logPoint(p: Point) { console.log(p); }
const point3D = { x: 1, y: 2, z: 3 };
logPoint(point3D); // OK (has x and y)
Real World Example
// Mocking objects for testing
// You only need to match the required shape, not the exact class.
TS uses structural typing: if it looks like a duck (has same props), it is a duck. Names don't matter, shape does.
type USD = number & { __brand: "USD" };
type EUR = number & { __brand: "EUR" };
let usd = 10 as USD;
let eur = 10 as EUR;
// usd = eur; // Error
Real World Example
// Preventing mixing up units (e.g. meters vs feet)
// or validated strings (e.g. EmailAddress vs string)
TS doesn't support this natively, but you can simulate it using "Branding" (adding a unique property) to distinguish types with the same shape.
type Colors = "red" | "green" | "blue";
type RGB = [number, number, number];
const palette = {
red: [255, 0, 0],
green: "#00ff00",
blue: [0, 0, 255]
} satisfies Record<Colors, string | RGB>;
Real World Example
// palette.red is inferred as [number, number, number]
// palette.green is inferred as string
// We get type checking AND specific inference.
Validates that an expression matches a type, but preserves the more specific type of the expression for inference.
type Json =
| string | number | boolean | null
| Json[]
| { [key: string]: Json };
Real World Example
// Nested Comment Structure
interface Comment {
text: string;
replies: Comment[];
}
Types that reference themselves. Example: JSON object type.