TypeScript Programs

Practice Code Examples

Hands-on TypeScript programs to practice the type system. From basic annotations to advanced generic utilities.

TypeScript 5 ✓ Practice 35 Programs
01

Basic Programs

#1Typed Function: Check PrimeBasic
TypeScript
function isPrime(n: number): boolean {
  if (n < 2) return false;
  for (let i = 2; i <= Math.sqrt(n); i++)
    if (n % i === 0) return false;
  return true;
}
isPrime(17);   // true
#2Factorial with Type AnnotationBasic
TypeScript
const factorial = (n: number): number =>
  n <= 1 ? 1 : n * factorial(n - 1);
factorial(5);   // 120
#3Reverse a StringBasic
TypeScript
const reverse = (s: string): string =>
  s.split("").reverse().join("");
reverse("hello");   // "olleh"
#4Check PalindromeBasic
TypeScript
function isPalindrome(s: string): boolean {
  const c = s.toLowerCase().replace(/[^a-z0-9]/g, "");
  return c === c.split("").reverse().join("");
}
isPalindrome("Race car");   // true
#5Typed Object LiteralBasic
TypeScript
interface User { name: string; age: number; }
const ada: User = { name: "Ada", age: 36 };
ada.name;   // "Ada" — autocompleted & type-checked
#6Literal Union: Traffic LightBasic
TypeScript
type Light = "red" | "yellow" | "green";
const next: Record<Light, Light> = {
  red: "green", green: "yellow", yellow: "red",
};
next["red"];   // "green"
#7Celsius to FahrenheitBasic
TypeScript
const toF = (c: number): number => c * 9 / 5 + 32;
toF(100);   // 212
#8Count VowelsBasic
TypeScript
const vowels = (s: string): number =>
  (s.match(/[aeiou]/gi) ?? []).length;
vowels("TypeScript");   // 2
#9FizzBuzz (typed)Basic
TypeScript
function fizzbuzz(n: number): string {
  if (n % 15 === 0) return "FizzBuzz";
  if (n % 3 === 0) return "Fizz";
  if (n % 5 === 0) return "Buzz";
  return String(n);
}
#10Sum a number[]Basic
TypeScript
const sum = (nums: number[]): number =>
  nums.reduce((a, b) => a + b, 0);
sum([1, 2, 3, 4]);   // 10
#11Enum: Order StatusBasic
TypeScript
enum Status { Pending = "PENDING", Shipped = "SHIPPED" }
let s: Status = Status.Pending;
s === Status.Pending;   // true
#12Optional Chaining + NullishBasic
TypeScript
interface Cfg { server?: { port?: number }; }
const port = (c: Cfg): number => c.server?.port ?? 8080;
port({});   // 8080
#13Tuple: Coordinate SwapBasic
TypeScript
const swap = ([a, b]: [number, number]): [number, number] => [b, a];
swap([1, 2]);   // [2, 1]
02

Intermediate Programs

#14Generic first<T>Intermediate
TypeScript
function first<T>(arr: T[]): T | undefined {
  return arr[0];
}
first(["a", "b"]);   // string | undefined
#15Type Guard: isStringIntermediate
TypeScript
function isString(v: unknown): v is string {
  return typeof v === "string";
}
const x: unknown = "hi";
if (isString(x)) x.trim();   // x is string here
#16Discriminated Union: AreaIntermediate
TypeScript
type Shape =
  | { kind: "circle"; r: number }
  | { kind: "square"; s: number };
const area = (sh: Shape): number =>
  sh.kind === "circle" ? Math.PI * sh.r ** 2 : sh.s ** 2;
#17Generic groupByIntermediate
TypeScript
function groupBy<T>(arr: T[], key: (x: T) => string): Record<string, T[]> {
  return arr.reduce((acc, item) => {
    (acc[key(item)] ??= []).push(item);
    return acc;
  }, {} as Record<string, T[]>);
}
#18keyof Property GetterIntermediate
TypeScript
function pluck<T, K extends keyof T>(arr: T[], key: K): T[K][] {
  return arr.map(x => x[key]);
}
pluck([{ id: 1 }, { id: 2 }], "id");   // number[]
#19Debounce (typed generic)Intermediate
TypeScript
function debounce<A extends unknown[]>(
  fn: (...args: A) => void, ms: number
): (...args: A) => void {
  let t: ReturnType<typeof setTimeout>;
  return (...args) => { clearTimeout(t); t = setTimeout(() => fn(...args), ms); };
}
#20Result<T, E> UnionIntermediate
TypeScript
type Result<T, E = Error> =
  | { ok: true; value: T }
  | { ok: false; error: E };

function divide(a: number, b: number): Result<number> {
  return b === 0
    ? { ok: false, error: new Error("div by zero") }
    : { ok: true, value: a / b };
}
#21Word Frequency (Map)Intermediate
TypeScript
function freq(text: string): Map<string, number> {
  const m = new Map<string, number>();
  for (const w of text.split(/\s+/)) m.set(w, (m.get(w) ?? 0) + 1);
  return m;
}
#22Binary Search (generic-ish)Intermediate
TypeScript
function bsearch(a: number[], t: number): number {
  let lo = 0, hi = a.length - 1;
  while (lo <= hi) {
    const mid = (lo + hi) >>> 1;
    if (a[mid] === t) return mid;
    a[mid] < t ? (lo = mid + 1) : (hi = mid - 1);
  }
  return -1;
}
#23Assert Never (exhaustive)Intermediate
TypeScript
function assertNever(x: never): never {
  throw new Error(`Unexpected: ${x}`);
}
// use in a switch default to force handling every case
#24Pick a Subset TypeIntermediate
TypeScript
interface User { id: string; name: string; pw: string; }
type SafeUser = Omit<User, "pw">;
const strip = ({ pw, ...rest }: User): SafeUser => rest;
03

Advanced Programs

#25Generic Stack<T>Advanced
TypeScript
class Stack<T> {
  private items: T[] = [];
  push(x: T): void { this.items.push(x); }
  pop(): T | undefined { return this.items.pop(); }
  get size(): number { return this.items.length; }
}
#26Typed EventEmitterAdvanced
TypeScript
class Emitter<E extends Record<string, unknown[]>> {
  private m: { [K in keyof E]?: ((...a: E[K]) => void)[] } = {};
  on<K extends keyof E>(e: K, fn: (...a: E[K]) => void) {
    (this.m[e] ??= []).push(fn);
  }
  emit<K extends keyof E>(e: K, ...a: E[K]) {
    this.m[e]?.forEach(fn => fn(...a));
  }
}
#27Memoize (generic)Advanced
TypeScript
function memoize<A extends unknown[], R>(fn: (...a: A) => R): (...a: A) => R {
  const cache = new Map<string, R>();
  return (...a) => {
    const k = JSON.stringify(a);
    if (!cache.has(k)) cache.set(k, fn(...a));
    return cache.get(k)!;
  };
}
#28DeepReadonly<T> Mapped TypeAdvanced
TypeScript
type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object
    ? DeepReadonly<T[K]>
    : T[K];
};
// every nested property becomes readonly, recursively
#29Typed fetch WrapperAdvanced
TypeScript
async function getJson<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 u = await getJson<User>("/api/me");
#30Builder Pattern (fluent, typed)Advanced
TypeScript
class Query {
  private parts: string[] = [];
  select(c: string): this { this.parts.push(`SELECT ${c}`); return this; }
  from(t: string): this { this.parts.push(`FROM ${t}`); return this; }
  build(): string { return this.parts.join(" "); }
}
new Query().select("*").from("users").build();
#31Merge Sort (typed generic)Advanced
TypeScript
function mergeSort<T>(a: T[], cmp: (x: T, y: T) => number): T[] {
  if (a.length <= 1) return a;
  const mid = a.length >> 1;
  const l = mergeSort(a.slice(0, mid), cmp);
  const r = mergeSort(a.slice(mid), cmp);
  const out: T[] = []; let i = 0, j = 0;
  while (i < l.length && j < r.length)
    out.push(cmp(l[i], r[j]) <= 0 ? l[i++] : r[j++]);
  return [...out, ...l.slice(i), ...r.slice(j)];
}
#32Branded Type (nominal)Advanced
TypeScript
type UserId = string & { __brand: "UserId" };
const toUserId = (s: string): UserId => s as UserId;
// prevents mixing a UserId with a plain string at compile time
#33Awaited Return of a FnAdvanced
TypeScript
async function load() { return { id: 1 }; }
type Loaded = Awaited<ReturnType<typeof load>>;   // { id: number }
#34Pipe (variadic-ish compose)Advanced
TypeScript
const pipe = <T>(...fns: ((x: T) => T)[]) =>
  (x: T): T => fns.reduce((acc, fn) => fn(acc), x);
const f = pipe<number>(n => n + 1, n => n * 2);
f(3);   // 8
#35Type-Safe Object.entriesAdvanced
TypeScript
function entries<T extends object>(obj: T): [keyof T, T[keyof T]][] {
  return Object.entries(obj) as [keyof T, T[keyof T]][];
}
entries({ a: 1, b: "x" });   // ["a"|"b", number|string][]