TypeScript basics in one page.
Primitives
let n: number = 42;
let s: string = "hello";
let b: boolean = true;
let big: bigint = 100n;
let sym: symbol = Symbol("x");
let nothing: null = null;
let undef: undefined = undefined;
Prefer inference — annotate only when TS can’t figure it out.
const x = 42; // x: 42 (literal)
let y = 42; // y: number
Arrays / tuples
const xs: number[] = [1, 2, 3];
const ys: Array<number> = [1, 2];
// Tuple — fixed length, position-typed
const pair: [string, number] = ["age", 30];
// Readonly tuple
const rgb: readonly [number, number, number] = [255, 0, 0];
Object types
type User = {
id: number;
name: string;
email?: string; // optional
readonly createdAt: Date;
};
const u: User = { id: 1, name: "A", createdAt: new Date() };
Interfaces vs type aliases
interface User {
id: number;
name: string;
}
type User = { id: number; name: string };
Interfaces can be extended and merged:
interface User { id: number }
interface User { name: string } // merged
interface Admin extends User { role: string }
Type aliases support unions, intersections, mapped types:
type Status = "active" | "inactive";
type Admin = User & { role: string };
Default to type. Use interface for public API shapes or class-implementable contracts.
Unions
type Id = string | number;
function format(id: Id): string {
if (typeof id === "string") return id.toUpperCase();
return id.toFixed();
}
Literal types
type Direction = "up" | "down" | "left" | "right";
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
Functions
function add(a: number, b: number): number {
return a + b;
}
const sub = (a: number, b: number): number => a - b;
// Function type
type BinOp = (a: number, b: number) => number;
const mul: BinOp = (a, b) => a * b;
Optional and default params:
function greet(name: string, greeting = "Hello"): string {
return `${greeting}, ${name}`;
}
function find(id: number, options?: { strict: boolean }) {}
Rest params:
function sum(...xs: number[]): number {
return xs.reduce((a, b) => a + b, 0);
}
void / never
function log(msg: string): void { console.log(msg); }
function fail(msg: string): never {
throw new Error(msg);
}
never = no value ever returned.
any / unknown
let a: any = "x";
a.foo.bar; // no error (DANGEROUS)
let u: unknown = "x";
u.foo; // ERROR — must narrow first
if (typeof u === "string") u.toUpperCase();
Use unknown for “I don’t know yet”; never any.
Type assertions
const el = document.getElementById("x") as HTMLInputElement;
const n = "42" as unknown as number; // double-cast escape hatch
// Avoid `<HTMLInputElement>` — clashes with JSX.
Narrowing
function f(x: string | number) {
if (typeof x === "string") {
x.toUpperCase(); // string
} else {
x.toFixed(); // number
}
}
// instanceof
if (err instanceof Error) err.message;
// in
if ("name" in obj) obj.name;
// equality / discriminator
if (s.kind === "circle") s.radius;
Truthiness
if (s) {} // narrows to non-null/empty
if (s != null) {} // != null catches both null and undefined
Type guards
function isUser(x: unknown): x is User {
return typeof x === "object" && x !== null && "id" in x;
}
if (isUser(x)) {
x.id; // narrowed to User
}
Enums (avoid; prefer unions)
enum Status { Active, Inactive } // 0, 1
enum Role { Admin = "admin", User = "user" }
// Prefer:
type Status = "active" | "inactive";
const Status = { Active: "active", Inactive: "inactive" } as const;
as const
const config = {
host: "localhost",
port: 5432,
} as const;
// config.port is 5432, not number
Locks values to literals; makes objects deeply readonly.
satisfies (4.9+)
const config = {
host: "localhost",
port: 5432,
} satisfies Record<string, string | number>;
// Checks against the type but preserves literal types.
Non-null assertion (!)
const el = document.getElementById("x")!; // assert non-null
Use sparingly — it’s an unchecked promise to TS.
Optional chaining + nullish coalescing
const name = user?.profile?.name;
const port = config.port ?? 5432; // ?? only triggers for null/undef
Common mistakes
- Annotating where inference is fine — extra noise.
anyto silence errors — defer real fixes; useunknown.<T>casts in.tsxfiles — clash with JSX. Useas T.- Enums in libraries — produce JS runtime code; unions are cheaper.
!everywhere — defeats null safety.
Read this next
If you want my TS starter (strict tsconfig + Vitest + tsup), it’s at rajpoot.dev .
Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .