Skip to content
Code Kata
Go back

TypeScript 5.x: Key Updates by Version

Suggest edit

Table of contents

Open Table of contents

Overview

TypeScript 5.x brought major improvements to the type system, module resolution, and developer experience. This post covers the most impactful additions from 5.0 through 5.7, serving as a quick reference for the features you’ll use in the kata exercises.

Version 5.0

Decorators (Stage 3)

TypeScript 5.0 ships with the TC39 Stage 3 decorators proposal, replacing the legacy experimental decorators.

function logged(originalMethod: any, context: ClassMethodDecoratorContext) {
  const methodName = String(context.name);
  function replacementMethod(this: any, ...args: any[]) {
    console.log(`Calling ${methodName}`);
    return originalMethod.call(this, ...args);
  }
  return replacementMethod;
}

class Calculator {
  @logged
  add(a: number, b: number) {
    return a + b;
  }
}

const Type Parameters

Allows inference of more specific literal types from generic arguments.

function createPair<const T>(values: T) {
  return values;
}

// Inferred as readonly ["hello", 42] instead of (string | number)[]
const pair = createPair(["hello", 42] as const);

Multiple Configuration Files with extends

tsconfig.json can now extend from multiple files:

{
  "extends": ["@tsconfig/node18/tsconfig.json", "./tsconfig.paths.json"]
}

Version 5.1

Easier Implicit Returns for undefined

Functions returning undefined no longer need an explicit return statement:

function logMessage(msg: string): undefined {
  console.log(msg);
  // no return needed
}

Unrelated Types for Getters and Setters

Getters and setters can now have unrelated types:

class Box {
  #value: string = "";

  get value(): string {
    return this.#value;
  }

  set value(newValue: string | number) {
    this.#value = String(newValue);
  }
}

Version 5.2

using Declarations (Explicit Resource Management)

Supports the TC39 Explicit Resource Management proposal for deterministic cleanup:

function readFile(path: string) {
  using file = openFile(path); // file[Symbol.dispose]() called at end of scope
  return file.read();
}

// Async variant
async function fetchData(url: string) {
  await using response = await fetch(url);
  return response.json();
}

Decorator Metadata

Decorators can now attach metadata to classes:

function track(
  target: any,
  context: ClassFieldDecoratorContext
) {
  context.metadata.tracked ??= [];
  (context.metadata.tracked as string[]).push(String(context.name));
}

Version 5.3

Import Attributes

Support for the with keyword on imports:

import data from "./config.json" with { type: "json" };

Narrowing in Generic Functions

Improved control flow narrowing when checking generic type parameters:

function process<T extends string | number>(value: T) {
  if (typeof value === "string") {
    // T is narrowed to string here
    return value.toUpperCase();
  }
  return value.toFixed(2);
}

Version 5.4

NoInfer Utility Type

Prevents inference from specific positions, giving you control over where TypeScript infers generic types:

function createStreetLight<C extends string>(
  colors: C[],
  defaultColor: NoInfer<C>
) {}

// Error: "purple" is not in the colors array type
createStreetLight(["red", "yellow", "green"], "purple");

Preserved Narrowing in Closures After Last Assignment

Variables narrowed after their last assignment stay narrowed in closures:

function example() {
  let value: string | number = getValue();
  value = "hello"; // last assignment

  // Previously lost narrowing, now preserved
  setTimeout(() => {
    console.log(value.toUpperCase()); // OK
  }, 100);
}

Version 5.5

Inferred Type Predicates

TypeScript can now infer type predicates from function bodies:

// TypeScript infers: (x: string | number) => x is string
function isString(x: string | number) {
  return typeof x === "string";
}

const values = ["a", 1, "b", 2];
const strings = values.filter(isString); // string[]

Regular Expression Syntax Checking

TypeScript now validates regular expression syntax at compile time, catching common errors before runtime.

Isolated Declarations

New --isolatedDeclarations flag enables parallel emit and faster builds by requiring explicit return type annotations on exported functions.

Version 5.6

Iterator Helper Methods

Built-in iterator methods like .map(), .filter(), .take(), .drop() on iterators:

function* naturals() {
  let n = 1;
  while (true) yield n++;
}

const firstFiveEvens = naturals()
  .filter(n => n % 2 === 0)
  .take(5)
  .toArray(); // [2, 4, 6, 8, 10]

Disallowed Nullish and Truthy Checks

New compiler checks flag suspicious nullish and truthy expressions that are likely bugs.

Version 5.7

--erasableSyntaxOnly

Restricts TypeScript to syntax that can be erased without semantic changes, aligning with Node.js’s built-in TypeScript support (type stripping).

Initialized Class Members in typeof

typeof on class instances now correctly reflects initialized members.

Key Takeaways

The TypeScript 5.x series focused on three themes:

  1. Modern JavaScript alignment — Stage 3 decorators, using declarations, import attributes
  2. Type system precisionNoInfer, inferred type predicates, better narrowing in closures and generics
  3. Tooling and performance — isolated declarations, --erasableSyntaxOnly, regex checking

Suggest edit
Share this post on:

Previous Post
TypeScript Core Concepts