TypeScript
TypeScript
Typed superset of JavaScript. Catches errors at compile time. Standard for Next.js, React, Node.js in 2026. Your portfolio (vectorbuilds.dev) is TypeScript + Next.js.
npx tsc --init # create tsconfig.json
npx tsc # compile
npx tsc --watch # watch mode
Basic Types
// Primitives
let name: string = "Alice";
let age: number = 30;
let active: boolean = true;
let nothing: null = null;
let notSet: undefined = undefined;
// Arrays
let nums: number[] = [1, 2, 3];
let strs: Array<string> = ["a", "b"];
// Tuple — fixed-length, typed array
let pair: [string, number] = ["Alice", 30];
// Enum
enum Direction { Up, Down, Left, Right }
let dir: Direction = Direction.Up;
// any — escape hatch (avoid)
let anything: any = 42;
anything = "now a string"; // no error
// unknown — safer alternative to any
let val: unknown = fetchData();
if (typeof val === "string") {
console.log(val.toUpperCase()); // type-narrowed
}
// never — function that never returns
function fail(msg: string): never {
throw new Error(msg);
}
Interfaces & Type Aliases
// Interface — for objects and classes
interface User {
id: number;
name: string;
email?: string; // optional
readonly createdAt: Date; // readonly
}
// Type alias — for anything
type ID = string | number;
type Point = { x: number; y: number };
type Callback = (err: Error | null, result?: string) => void;
// When to use which:
// Interface: object shapes, extendable, class contracts
// Type: unions, intersections, primitives, utility types
// Interface extension
interface Admin extends User {
permissions: string[];
}
// Type intersection (merge)
type AdminUser = User & { permissions: string[] };
Union & Intersection Types
// Union — one of
type Status = "pending" | "active" | "inactive";
type ID = string | number;
function process(id: string | number) {
if (typeof id === "string") {
return id.toUpperCase(); // narrowed to string
}
return id * 2; // narrowed to number
}
// Intersection — combine all
type HasName = { name: string };
type HasAge = { age: number };
type Person = HasName & HasAge; // { name: string; age: number }
Generics
// Generic function
function identity<T>(arg: T): T {
return arg;
}
identity<string>("hello");
identity(42); // inferred: T = number
// Generic interface
interface ApiResponse<T> {
data: T;
error: string | null;
status: number;
}
type UsersResponse = ApiResponse<User[]>;
// Generic constraints
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user: User = { id: 1, name: "Alice", createdAt: new Date() };
getProperty(user, "name"); // ✓ string
getProperty(user, "foo"); // ✗ compile error
Utility Types
// Partial — all fields optional
type PartialUser = Partial<User>; // { id?: number; name?: string; ... }
// Required — all fields required
type RequiredUser = Required<PartialUser>;
// Pick — subset of fields
type UserPreview = Pick<User, "id" | "name">;
// Omit — remove fields
type CreateUser = Omit<User, "id" | "createdAt">;
// Readonly — immutable
type ImmutableUser = Readonly<User>;
// Record — key-value map type
type UserMap = Record<string, User>; // { [key: string]: User }
type StatusCount = Record<Status, number>; // { pending: number; ... }
// ReturnType — extract return type
function getUser() { return { id: 1, name: "Alice" }; }
type GetUserReturn = ReturnType<typeof getUser>; // { id: number; name: string }
// Parameters — extract function params
type GetUserParams = Parameters<typeof getUser>; // []
Type Narrowing
// typeof
function padLeft(value: string, padding: string | number) {
if (typeof padding === "number") {
return " ".repeat(padding) + value;
}
return padding + value;
}
// instanceof
function format(value: Date | string) {
if (value instanceof Date) {
return value.toISOString();
}
return value;
}
// in operator
interface Cat { meow(): void }
interface Dog { bark(): void }
function makeSound(animal: Cat | Dog) {
if ("meow" in animal) {
animal.meow(); // narrowed to Cat
} else {
animal.bark();
}
}
// Discriminated union — best pattern
type Shape =
| { kind: "circle"; radius: number }
| { kind: "rect"; width: number; height: number };
function area(shape: Shape) {
switch (shape.kind) {
case "circle": return Math.PI * shape.radius ** 2;
case "rect": return shape.width * shape.height;
}
}
as const — Literal Types
// Without as const: type is string[]
const colors = ["red", "green", "blue"];
// With as const: type is readonly ["red", "green", "blue"]
const COLORS = ["red", "green", "blue"] as const;
type Color = typeof COLORS[number]; // "red" | "green" | "blue"
// Object literal types
const config = {
endpoint: "https://api.example.com",
timeout: 5000
} as const;
// config.endpoint is "https://api.example.com", not string
TypeScript in React (common patterns)
// Component props
interface ButtonProps {
label: string;
onClick: () => void;
variant?: "primary" | "secondary";
children?: React.ReactNode;
}
const Button: React.FC<ButtonProps> = ({ label, onClick, variant = "primary" }) => (
<button onClick={onClick} className={variant}>{label}</button>
);
// useState with type
const [user, setUser] = useState<User | null>(null);
const [items, setItems] = useState<string[]>([]);
// useRef
const inputRef = useRef<HTMLInputElement>(null);
// Generic fetch hook
async function fetchData<T>(url: string): Promise<T> {
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json() as Promise<T>;
}
const users = await fetchData<User[]>("/api/users");
tsconfig.json Key Options
{
"compilerOptions": {
"target": "ES2022", // output JS version
"module": "ESNext", // module system
"strict": true, // enable all strict checks (always true)
"noImplicitAny": true, // error on implicit any
"strictNullChecks": true, // null/undefined must be explicit
"paths": {
"@/*": ["./src/*"] // import aliases
},
"baseUrl": ".",
"outDir": "./dist",
"jsx": "preserve" // for React/Next.js
}
}
Always use strict: true. It enables noImplicitAny, strictNullChecks, and others.
Interview Talking Points
- "TypeScript's discriminated unions are better than class hierarchies for modeling state — you get exhaustive type checking in switch statements at compile time."
- "
unknownis the safeany— you can't use it without a type guard, so it forces you to think about what you're handling." - "Utility types like
Partial<T>,Pick<T,K>,Omit<T,K>are the first tools I reach for when I need to derive types from existing ones — avoids repetition and stays in sync with the base type."
Related
- [[FrontEnd/React/React]] — TypeScript in React components
- [[FrontEnd/Java script/JavaScript]] — TypeScript builds on JS
- [[FrontEnd/FrontEnd Topics]] — frontend index