Fundamentals

2 min read
Rapid overview

TypeScript Fundamentals

Core TypeScript concepts for frontend development.

Table of Contents

Types and Inference

let count = 0; // number inferred
let label: string = 'Tasks';
let isOpen = false; // boolean inferred

const ids = [1, 2, 3]; // number[] inferred
const mixed: (string | number)[] = [1, 'two'];

Use explicit annotations when inference is unclear or when defining public APIs.

Unions and Intersections

type Id = string | number;

function formatId(id: Id) {
  return typeof id === 'number' ? id.toFixed(0) : id.toUpperCase();
}

type WithTimestamp = { createdAt: string };
type WithAuthor = { author: string };

type AuditRecord = WithTimestamp & WithAuthor;

Unions represent alternatives, intersections combine required fields.

Interfaces vs Type Aliases

interface User {
  id: string;
  name: string;
}

interface Admin extends User {
  role: 'admin';
}

type Status = 'idle' | 'loading' | 'success' | 'error';

type ApiResponse = {
  status: Status;
  data?: unknown;
};

Use interfaces for object shapes you expect to extend, and type aliases for unions, primitives, and composable helpers.

Generics

function identity<T>(value: T): T {
  return value;
}

const name = identity('Ada');
const countValue = identity(42);

function mapValues<T, U>(items: T[], mapper: (item: T) => U): U[] {
  return items.map(mapper);
}

Add constraints to keep generics safe:

function toMap<T extends { id: string }>(items: T[]) {
  return new Map(items.map((item) => [item.id, item]));
}

Narrowing and Guards

type Result = { ok: true; value: string } | { ok: false; error: string };

function handleResult(result: Result) {
  if (result.ok) {
    return result.value;
  }
  return result.error;
}

function isString(value: unknown): value is string {
  return typeof value === 'string';
}

Use typeof, instanceof, in, and custom predicates to narrow types safely.

Functions and Overloads

function parseInput(input: string): number;
function parseInput(input: number): number;
function parseInput(input: string | number): number {
  return typeof input === 'string' ? Number(input) : input;
}

const fromString = parseInput('42');
const fromNumber = parseInput(42);

Overloads let you expose precise call signatures while implementing a single function body.

Classes and Access Modifiers

class Session {
  public readonly id: string;
  private token: string;

  constructor(id: string, token: string) {
    this.id = id;
    this.token = token;
  }

  refresh(newToken: string) {
    this.token = newToken;
  }
}

Use public, private, and protected to communicate intent and enforce encapsulation.

Modules and tsconfig Basics

export function toTitleCase(value: string) {
  return value.replace(/\b\w/g, (char) => char.toUpperCase());
}

import { toTitleCase } from './strings';

Common tsconfig.json settings for frontend apps:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "strict": true,
    "noImplicitAny": true
  }
}