October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsClean PCRecommendedOne scan can reveal what keeps slowing WindowsLook for cleanup and repair opportunities.Run ScanOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk5 min

7 TypeScript Patterns for Clearer, Safer Projects

Seven practical TypeScript patterns help make nullable values, union states, generic relationships, and runtime boundaries easier to reason about.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Useful TypeScript patterns make uncertainty visible, represent each state clearly, and help the compiler catch omissions. If you already read TypeScript, these seven practices offer a practical answer to the question, “What language features or tricks are useful day to day?” They are a set of techniques, not a ranking or a claim that every project should adopt them without adjustment.

1. Enable strict null checks when your project can support them

A value that might be null or undefined is a different situation from a value that is always present. With strictNullChecks enabled, TypeScript requires code to account for those possibilities instead of treating nullable values as interchangeable with ordinary values.

As an Amazon Associate I earn from qualifying purchases.

For example, a lookup may fail to find a user:

function findUser(id: string): User | undefined {
  // ...
}

const user = findUser("u-123");
console.log(user.name); // Error: user might be undefined

Check the result before accessing it:

const user = findUser("u-123");

if (user !== undefined) {
  console.log(user.name);
}

The TypeScript Handbook encourages enabling strictNullChecks where possible. In an older codebase, however, turning it on can expose many places that rely on values being present without proving it. Treat adoption as a project change: assess the resulting errors, fix or deliberately handle uncertain values, and consider the migration effort before changing the setting.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

2. Narrow uncertain values before using them

When a value has more than one possible type, use ordinary JavaScript checks to establish which case applies. TypeScript follows control flow and narrows the type inside the relevant branch.

Check union members with a type test

A function receiving either a string or a number can handle the two types explicitly:

function format(value: string | number): string {
  if (typeof value === "string") {
    return value.trim();
  }

  return value.toFixed(2);
}

Within the first branch, TypeScript knows value is a string; after that branch, it knows the remaining possibility is a number.

Check nullable values before access

function displayName(name: string | null): string {
  if (name === null) {
    return "Anonymous";
  }

  return name.toUpperCase();
}

The check makes the function’s handling of the missing case explicit. Similar narrowing can come from property checks, truthiness checks, loops, and other control-flow branches. Pick a check that matches the actual domain: a truthiness test, for instance, also treats an empty string as false, which may not mean “missing” in your application.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Rank #2
TypeScript Programming Language - Software Engineer & Coder T-Shirt
  • TypeScript implements a superset of syntax for strictly typed development, facilitating deep static analysis and enhanced development environment integration. The compiler translates source into standard script formats, ensuring parity across any runtime.
  • TypeScript is ideal for front-end developers, full-stack engineers, and software architects who build large-scale web applications. It serves those looking to improve code excellence, reduce bugs through static checking, and maintain complex projects more.
  • Lightweight, Classic fit, Double-needle sleeve and bottom hem

3. Represent distinct states with discriminated unions

When an object can represent different states, model each state as its own object shape and give every shape a shared literal-valued discriminant. This keeps state-specific data attached to the state where it is valid.

Before: loosely related optional fields

type RequestResult = {
  loading?: boolean;
  data?: User[];
  error?: string;
};

This shape permits combinations that may not make sense, such as loading and success data at the same time, or an error without a clear indication of the request state.

After: one shape per state

type RequestResult =
  | { state: "loading" }
  | { state: "success"; data: User[] }
  | { state: "failed"; error: string };

Now a successful result carries data, a failed result carries an error, and loading needs neither. Checking state narrows the value to the matching member:

function render(result: RequestResult): string {
  if (result.state === "success") {
    return `${result.data.length} users`;
  }

  if (result.state === "failed") {
    return result.error;
  }

  return "Loading…";
}

The discriminant is useful when the alternatives are genuinely different states, not merely optional details on one stable object.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

4. Make union handling exhaustive

Once a union defines the possible cases, handle them all at important decision points. An exhaustive switch paired with a never check can make adding a new variant surface as a type error where the union is handled.

type RequestResult =
  | { state: "loading" }
  | { state: "success"; data: User[] }
  | { state: "failed"; error: string };

function describe(result: RequestResult): string {
  switch (result.state) {
    case "loading":
      return "Loading";
    case "success":
      return `Loaded ${result.data.length} users`;
    case "failed":
      return `Failed: ${result.error}`;
    default: {
      const unreachable: never = result;
      return unreachable;
    }
  }
}

If a new state such as cancelled is added to RequestResult, the assignment to never no longer type-checks until the switch accounts for it. This check applies to the declared union during type checking; it does not verify that external data actually conforms to that declaration.

5. Use generics to preserve useful type relationships

A generic is valuable when a function works across multiple types while preserving a relationship between its input and output. It is not a reason to make every function generic.

Before: the output loses the input type

function identity(value: unknown): unknown {
  return value;
}

const id = identity(42); // unknown

After: carry the type through

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

const id = identity(42); // number

The type parameter links the argument and return value: callers can pass different types, and the returned value retains the corresponding type information. Use a generic when that relationship helps callers; if a function only accepts one known type, a direct type may be clearer.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

6. Use utility types for common transformations

TypeScript’s built-in utility types express recurring type transformations without requiring a new handwritten type for every variation. Two common examples are Partial<T>, which makes the properties of T optional, and Pick<T, K>, which selects a subset of its properties.

type User = {
  id: string;
  name: string;
  email: string;
};

type UserUpdate = Partial<User>;
type UserSummary = Pick<User, "id" | "name">;

UserUpdate can describe a partial update, while UserSummary describes a shape containing only the selected fields. These types transform static type information; they do not remove fields from an object or validate data at runtime. Choose a utility type when its meaning fits the domain, and use a named type when the result needs its own semantics or rules.

7. Keep type assertions rare and deliberate

A type assertion tells TypeScript to trust that a value has a particular type. It does not check or reshape the value at runtime. The TypeScript Handbook puts it this way: “A type assertion is like a type cast in other languages, but it performs no special checking or restructuring of data.”

Do not cast untrusted data into shape

const user = JSON.parse(responseText) as User;

This assertion does not establish that the response contains a valid User. If data comes from a network, file, or other untrusted boundary, validate its runtime shape before relying on it.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use an assertion only when you can justify the trust

An assertion can be appropriate when a check or invariant exists that TypeScript cannot represent, and the programmer can explain why the value meets the asserted type. If the justification is simply that an error disappears, the assertion may be hiding a real mismatch. Prefer narrowing or a runtime validation when the value’s shape is uncertain.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.