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:
- Modern JavaScript alignment — Stage 3 decorators,
usingdeclarations, import attributes - Type system precision —
NoInfer, inferred type predicates, better narrowing in closures and generics - Tooling and performance — isolated declarations,
--erasableSyntaxOnly, regex checking
Related Katas
- Kata: Decorator Pattern — exercises Stage 3 decorators
- Kata: Observer Pattern — uses generics and type predicates
- Kata: Strategy Pattern — leverages interfaces and discriminated unions