Typescript 2023: The Year in Review

3 min read

TypeScript continues to evolve with significant improvements in type safety and developer experience. Let’s explore the key features that landed this year and how they improve our codebases.

Decorators

After years of experimental status, decorators finally landed as a stable feature in TypeScript 5.0. This implementation aligns with the Stage 3 Decorator Proposal, the ECMAScript specification, and is thoroughly documented in the TypeScript Handbook.

function logged(value: any, context: DecoratorContext) {
  if (context.kind === "method") {
    return function (...args: any[]) {
      console.log(`Calling ${context.name}`);
      return value.call(this, ...args);
    };
  }
}
 
class API {
  @logged
  getData() {
    return fetch("/api/data");
  }
}

This stable implementation resolves years of uncertainty around decorator syntax and provides a standardized way to add metadata and behavior to classes and their members.

Resource Management with using

The using declaration implements the TC39 Explicit Resource Management proposal, specified in the ECMAScript draft, and detailed in TypeScript’s documentation.

class FileHandle implements Disposable {
  #handle: number;
  
  [Symbol.dispose]() {
    // Cleanup happens automatically
    closeHandle(this.#handle);
  }
}
 
{
  using file = new FileHandle("data.txt");
  // File closes automatically at block end
}

This feature eliminates common resource leaks by ensuring cleanup code runs predictably, similar to Python’s context managers or C#‘s using statements.

Variadic Tuple Types

Variadic tuple types, detailed in the TypeScript specification and handbook, enable powerful type-level array operations:

type Concat<T extends unknown[], U extends unknown[]> = [...T, ...U];
 
// Type system understands the result
type Combined = Concat<[1, 2], [3, 4, 5]>; // [1, 2, 3, 4, 5]
 
// Real-world example
function concat<T extends unknown[], U extends unknown[]>(
  arr1: T,
  arr2: U
): [...T, ...U] {
  return [...arr1, ...arr2];
}

const Type Parameters

The const modifier for generics, introduced in TypeScript 5.0 and specified in the type system, preserves literal types through generic operations:

function identity<const T>(value: T): T {
  return value;
}
 
// Preserves exact literal types
const x = identity("hello"); // type is "hello"
const y = identity([1, 2, 3]); // type is [1, 2, 3]
const z = identity({ x: 10 }); // type is { x: 10 }

This feature is particularly useful when working with tuple types and object literals where maintaining exact types is crucial.

satisfies Operator

The satisfies operator, documented in the TypeScript handbook and specification, enables type validation without type widening:

type RGB = [number, number, number];
type Colors = "red" | "green" | "blue";
 
const palette = {
  red: [255, 0, 0],
  green: [0, 255, 0],
  blue: [0, 0, 255],
} satisfies Record<Colors, RGB>;
 
// Type safety with precise inference
palette.red[0]; // number
palette.purple; // Error: property doesn't exist

Type-Only Import Attributes

Type-only import attributes, specified in the ECMAScript proposal and TypeScript implementation, improve module resolution:

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

This feature helps bundlers and runtimes better understand dependencies while maintaining type safety.

Looking Forward

Several exciting features are in development:

The TypeScript roadmap provides a comprehensive view of upcoming features and improvements.

Resources

Comments