TypeScript Types Guide
A complete, example-driven reference for every TypeScript type concept — from primitives to advanced mapped and conditional types.
Contents
Primitive Types
TypeScript has 7 primitive types, matching JavaScript primitives plus two additional types for safety:
// JavaScript primitives
let name: string = "Alice";
let age: number = 30; // includes integers and floats
let active: boolean = true;
let nothing: null = null;
let undef: undefined = undefined;
let id: bigint = 9007199254740993n;
let sym: symbol = Symbol("id");
// TypeScript extras
let val: any = "anything"; // disables type checking (avoid when possible)
let input: unknown = getData(); // safer than any — must narrow before use
let never: never; // value that can never occur (exhaustive checks)
let empty: void = undefined; // function return type for no return valueInterface vs Type Alias
Both define object shapes. Key differences:
Interface
interface User {
id: number;
name: string;
email?: string; // optional
readonly role: string; // read-only
}
// Extending
interface Admin extends User {
permissions: string[];
}
// Declaration merging (unique to interface)
interface User {
createdAt: Date; // added to original User
}Type Alias
type User = {
id: number;
name: string;
email?: string;
};
// Intersection (equivalent to extends)
type Admin = User & { permissions: string[] };
// Can also represent non-objects:
type ID = string | number;
type Status = "active" | "inactive";
type Handler = (e: Event) => void;
type Maybe<T> = T | null | undefined;Rule of thumb: Use
interface for object shapes and public APIs (extensible). Use type for unions, intersections, primitives, and computed types.Unions & Intersections
// Union — value can be one of several types
type ID = string | number;
type Status = "active" | "inactive" | "pending"; // literal union
// Discriminated union — each branch has a unique literal
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; side: number }
| { kind: "rectangle"; width: number; height: number };
function area(shape: Shape): number {
switch (shape.kind) {
case "circle": return Math.PI * shape.radius ** 2;
case "square": return shape.side ** 2;
case "rectangle": return shape.width * shape.height;
}
}
// Intersection — value must satisfy all types
type WithTimestamps = { createdAt: Date; updatedAt: Date };
type UserRecord = User & WithTimestamps;Generics
Generics allow writing reusable code that works over many types while maintaining type safety:
// Generic function
function first<T>(arr: T[]): T | undefined {
return arr[0];
}
const num = first([1, 2, 3]); // type: number | undefined
const str = first(["a", "b"]); // type: string | undefined
// Generic interface
interface ApiResponse<T> {
data: T;
status: number;
error?: string;
}
type UserResponse = ApiResponse<User>;
// Generic constraints
function getField<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
getField({ name: "Alice", age: 30 }, "name"); // ✓ string
getField({ name: "Alice" }, "missing"); // ✗ compile error
// Default type parameter
interface Container<T = string> {
value: T;
}
const c: Container = { value: "hello" }; // T defaults to stringUtility Types
TypeScript ships with built-in utility types that transform existing types:
interface User {
id: number;
name: string;
email: string;
password: string;
}
// Transform all fields
Partial<User> // all fields optional: { id?: number; name?: string; ... }
Required<User> // all fields required (removes ?)
Readonly<User> // all fields read-only
// Pick or remove fields
Pick<User, "id" | "name"> // { id: number; name: string }
Omit<User, "password"> // all fields except password
// Records and mappings
Record<string, User> // { [key: string]: User }
Record<"admin" | "user", User> // { admin: User; user: User }
// Set operations on union types
type ABC = "a" | "b" | "c";
Exclude<ABC, "a"> // "b" | "c"
Extract<ABC, "a" | "d"> // "a"
NonNullable<string | null | undefined> // string
// Function-related
ReturnType<typeof fetchUser> // infer return type
Parameters<typeof fetchUser> // infer parameter tuple
Awaited<Promise<User>> // UserType Narrowing
Narrowing refines a union type to a specific member inside a conditional block:
// typeof narrowing
function print(val: string | number) {
if (typeof val === "string") {
console.log(val.toUpperCase()); // string here
} else {
console.log(val.toFixed(2)); // number here
}
}
// instanceof narrowing
function handleError(err: Error | string) {
if (err instanceof Error) {
console.log(err.stack); // Error here
} else {
console.log(err); // string here
}
}
// 'in' narrowing
type Cat = { meow(): void };
type Dog = { bark(): void };
function speak(animal: Cat | Dog) {
if ("meow" in animal) {
animal.meow(); // Cat here
} else {
animal.bark(); // Dog here
}
}
// Custom type predicate
function isUser(val: unknown): val is User {
return (
typeof val === "object" &&
val !== null &&
"id" in val &&
"name" in val
);
}Advanced Types
// Mapped type — transform every key of a type
type Optional<T> = { [K in keyof T]?: T[K] };
type ReadOnly<T> = { readonly [K in keyof T]: T[K] };
// Conditional type
type IsArray<T> = T extends any[] ? true : false;
type IsArray_string = IsArray<string[]>; // true
type IsArray_number = IsArray<number>; // false
// Infer in conditional types
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
type Resolved = UnwrapPromise<Promise<User>>; // User
// Template literal types (TS 4.1+)
type EventName = "click" | "focus" | "blur";
type Handler = `on${Capitalize<EventName>}`;
// "onClick" | "onFocus" | "onBlur"
// Recursive types
type Json =
| string
| number
| boolean
| null
| Json[]
| { [key: string]: Json };Convert your JavaScript code to TypeScript automatically?
Use the JS to TS Converter →