HeoLab
ToolsBlogAboutContact
HeoLab

Free developer tools with AI enhancement. Built for developers who ship.

Tools

  • JSON Formatter
  • JWT Decoder
  • Base64 Encoder
  • Timestamp Converter
  • Regex Tester
  • All Tools →

Resources

  • Blog
  • What is JSON?
  • JWT Deep Dive
  • Base64 Explained

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 HeoLab. All rights reserved.

Tools work in your browser. Zero data retention.

HomeBlogTypeScript Best Practices Every Developer Should Follow in 2025
Table of Contents▾
  • Always Use Strict Mode
  • Utility Types You Should Know
  • Discriminated Unions — Better Than Optional Properties
  • Type Guards
  • Template Literal Types
  • Never Use `any` — Use `unknown` Instead
  • Const Assertions and Enums
tutorials#typescript#javascript#programming

TypeScript Best Practices Every Developer Should Follow in 2025

Write better TypeScript — utility types, strict mode, discriminated unions, type guards, and patterns that eliminate runtime errors.

Trong Ngo
February 23, 2026
4 min read

TypeScript adds a powerful type system to JavaScript — but it's easy to use it superficially while still writing code that breaks at runtime. These are the patterns that make TypeScript genuinely safer.

Always Use Strict Mode

// tsconfig.json — start with this
{
  "compilerOptions": {
    "strict": true,              // enables all strict checks
    "noUncheckedIndexedAccess": true,  // arr[i] is T | undefined
    "exactOptionalPropertyTypes": true, // {a?: string} ≠ {a: string | undefined}
    "noImplicitReturns": true,    // all code paths must return
    "noFallthroughCasesInSwitch": true
  }
}

Utility Types You Should Know

interface User {
  id: string;
  name: string;
  email: string;
  role: 'admin' | 'user';
  createdAt: Date;
}

// Partial — all properties optional
type UserUpdate = Partial<User>;
// { id?: string; name?: string; ... }

// Required — all properties required
type FullUser = Required<Partial<User>>;

// Pick — select specific properties
type UserPreview = Pick<User, 'id' | 'name'>;
// { id: string; name: string }

// Omit — exclude specific properties
type CreateUser = Omit<User, 'id' | 'createdAt'>;
// { name: string; email: string; role: ... }

// Readonly — prevent mutation
type ImmutableUser = Readonly<User>;

// Record — typed object/map
type RoleMap = Record<'admin' | 'user', string[]>;

// ReturnType — extract function return type
type GetUserFn = () => Promise<User>;
type GetUserResult = Awaited<ReturnType<GetUserFn>>; // User

// Parameters — extract function parameter types
type FetchParams = Parameters<typeof fetch>; // [input: RequestInfo, init?: RequestInit]

Discriminated Unions — Better Than Optional Properties

// ✗ Hard to work with — what's available when?
interface ApiResponse {
  data?: User;
  error?: string;
  loading?: boolean;
}

// ✓ Discriminated union — exhaustive and explicit
type ApiResponse =
  | { status: 'loading' }
  | { status: 'success'; data: User }
  | { status: 'error'; error: string };

function render(state: ApiResponse) {
  switch (state.status) {
    case 'loading': return <Spinner />;
    case 'success': return <UserCard user={state.data} />; // data is User here
    case 'error':   return <Error msg={state.error} />;   // error is string here
  }
  // TypeScript knows this is exhaustive
}

Type Guards

// typeof guard
function process(value: string | number) {
  if (typeof value === 'string') {
    return value.toUpperCase(); // string here
  }
  return value.toFixed(2); // number here
}

// instanceof guard
function handleError(err: unknown) {
  if (err instanceof Error) {
    console.log(err.message); // Error here
  }
}

// Custom type guard
interface Cat { meow(): void }
interface Dog { bark(): void }

function isCat(animal: Cat | Dog): animal is Cat {
  return 'meow' in animal;
}

// in narrowing
function handleShape(shape: Circle | Square) {
  if ('radius' in shape) {
    return Math.PI * shape.radius ** 2; // Circle
  }
  return shape.side ** 2; // Square
}

Template Literal Types

// Build string types at compile time
type Method = 'GET' | 'POST' | 'PUT' | 'DELETE';
type Endpoint = '/users' | '/posts';
type Route = `${Method} ${Endpoint}`;
// 'GET /users' | 'GET /posts' | 'POST /users' | ...

// Event naming patterns
type EventName<T extends string> = `on${Capitalize<T>}`;
type UserEvents = EventName<'click' | 'focus' | 'blur'>;
// 'onClick' | 'onFocus' | 'onBlur'

// CSS property types
type CSSValue = `${number}px` | `${number}rem` | `${number}%`;

Never Use any — Use unknown Instead

// ✗ any — disables all type checking
function parse(data: any) {
  return data.user.name; // no error even if wrong
}

// ✓ unknown — forces you to narrow before using
function parse(data: unknown) {
  if (typeof data === 'object' && data !== null && 'user' in data) {
    // now TypeScript knows data has 'user'
  }
}

// ✓ Or validate with Zod (recommended for API data)
import { z } from 'zod';
const UserSchema = z.object({ name: z.string(), id: z.number() });
type User = z.infer<typeof UserSchema>;

const parsed = UserSchema.parse(apiResponse); // throws if invalid
const safe = UserSchema.safeParse(apiResponse); // returns { success, data/error }

Const Assertions and Enums

// const assertion — narrow to literal types
const ROLES = ['admin', 'user', 'guest'] as const;
type Role = (typeof ROLES)[number]; // 'admin' | 'user' | 'guest'

// Object const assertion
const CONFIG = {
  endpoint: 'https://api.heolab.com',
  timeout: 5000,
} as const;
type Endpoint = typeof CONFIG.endpoint; // 'https://api.heolab.com'

// Prefer const over enum for most cases
// enum has runtime footprint; const doesn't
const Direction = { Up: 'UP', Down: 'DOWN' } as const;
type Direction = (typeof Direction)[keyof typeof Direction]; // 'UP' | 'DOWN'

Use the JSON to TypeScript tool to instantly generate TypeScript interfaces from any JSON structure.

Try These Tools

JSON → TypeScript Interface

Paste JSON and instantly get a typed TypeScript interface. Handles nested objects and arrays.

JSON Formatter & Validator

Format, validate, and beautify JSON data instantly. Detect errors with precise line numbers.

Related Articles

Web Performance Optimization: The 2025 Practical Guide

4 min read

JavaScript Array Methods: The Complete Guide with Examples

5 min read

Async/Await vs Promises in JavaScript: A Complete Guide

4 min read

Back to Blog

Table of Contents

  • Always Use Strict Mode
  • Utility Types You Should Know
  • Discriminated Unions — Better Than Optional Properties
  • Type Guards
  • Template Literal Types
  • Never Use `any` — Use `unknown` Instead
  • Const Assertions and Enums

Related Articles

Web Performance Optimization: The 2025 Practical Guide

4 min read

JavaScript Array Methods: The Complete Guide with Examples

5 min read

Async/Await vs Promises in JavaScript: A Complete Guide

4 min read