Annotating Class with TypeScript: A Complete Guide

Updated: January 7, 2024 By: Guest Contributor Post a comment

Overview

Delve into the world of TypeScript class annotations with this comprehensive guide, covering from the foundational syntax to more complex types and decorators, adorned with plentiful examples.

Introduction

TypeScript brings about a sturdy structure to the supple nature of JavaScript, primarily through types, interfaces, and decorators. It ensconces JS’s burgeoning class syntax, bringing forth a slew of annotations for type safety and legibility. In this guidance, we’ll muster through the art of annotating TypeScript classes, with examples yonder from the basic ridge to the complex frontiers.

In the infancy of our journey, we commit to TypeScript’s annotations with akin kinship to JavaScript’s classes. Observing crucial conventions, we shall adorn classes with types built for scalawags and gentlemen alike, types that declare the nature of the class members with staunch precision.

class Ship {
   name: string;
   speed: number;

   constructor(name: string, speed: number) {
       this.name = name;
       this.speed = speed;
   }

   sail(): void {
       console.log(`The ${this.name} sails at ${this.speed} knots!`);
   }
}

Prying further ‘long into the complexities of TypeScript, interfaces are likened to the navigator’s map, outlining the shape of an object that a class must follow, without mist or mirage.

interface IShip {
   name: string;
   speed: number;
   sail(): void;
}

class PirateShip implements IShip {
   // Class implementation anchored to interface
}

The advanced buccaneer in TypeScript’s seas heeds decorum, for decorators—a proposal under JavaScript’s flag, yet implemented by TypeScript first—offer automation on the high-level seas of meta-programming.

function sealed(constructor: Function) {
   console.log(`Sealing the constructor: ${constructor.name}`);
   Object.seal(constructor);
   Object.seal(constructor.prototype);
}

@sealed
class SecretMap {
   // ...
}

As we sail through this guide, buckle your sea legs and prepare for learning the ropes of TypeScript with thorough exemplars from the basic reckonings to the cunning constructs enthused by generic types, mixins, and reflection.

Types and Properties

We set sail with TypeScript’s basic class annotations—types essential as the sturdy mast of a ship. By properly typing class members, we preserve order among the crew, and maintain clairvoyant communication with TypeScript’s compiling quartermaster.

class Buccaneer {
   favoriteDrink: string = 'Rum';
   goldCoins: number;

   constructor(initialCoins: number = 0) {
       this.goldCoins = initialCoins;
   }

   plunder(): void {
       this.goldCoins++;
       console.log('Har! Another coin for me booty!');
   }
}

Moving forth, property modifiers such as ‘public’, ‘private’, and ‘protected’, determine who has the right to parley with our class members, applying constraints as tight as the knots that bind a ship’s sails.

class SecretCove {
   private secretMap: string;
   protected navigatorsName: string;

   constructor(map: string, navigator: string) {
       this.secretMap = map;
       this.navigatorsName = navigator;
   }
}

Methods and Inheritance

With properties well-secured in the hull, we consider methods, the routine actions of a class’s voyage. Here we chart courses and ordain functionality with TypeScript’s annotation compass.

class Sloop extends Ship {
   fireCannons(target: string): void {
       console.log(`Cannons fired at ${target}!`);
   }
   // Additional actions
}

When the wind is favorable, inheritance allows a fleet of subclasses to gain the prowess and property of the flagship class. Yet TypeScript’s annotations keep inheritances orderly lest they mutiny against the runtime.

class Galleon extends Ship {
   // Inherits all from Ship and may introduce new annotations
}

Advanced Typing and Decorators

In waters deep, we uncover complex types—those that befriend generics and unions, and allow for flexible arrangements akin to a ship’s adaptable sails.

class Treasure {
   content: T;

   constructor(content: T) {
       this.content = content;
   }

   revealContent(): T {
       return this.content;
   }
}

Decorators paint our classes with intricate designs, augmenting their functionality with the finesse of a scrimshaw engraved by salty sea artists—applying cross-cutting concerns without much fuss or flurry.

function loggable(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
   let originalMethod = descriptor.value;
   descriptor.value = function(...args: any[]) {
       console.log(`Calling ${propertyKey}`);
       return originalMethod.apply(this, args);
   };
}

class SeaLegend {
   @loggable
   tellTale(tale: string): void {
       // Tales and adventures are logged as they are told.
   }
}

We witness classes veiled in interfaces, hold sway over the inheritance hierarchy, summon constructor signatures from thin air, and cast the dark enchantments of mixins, blending multiple types to form mighty legend.

Conclusion

As we dock at our destination, recall the whispers of safe TypeScript shores. Cladded in annotations, our classes triumph over ambiguity, and our flotilla of features sails beyond the horizon of benign, but base JavaScript. Such are the treasures we accumulate sailing with TypeScript—an exquisite command over the formless contemporary dilemmas of scripting across the seven webs.

Might ye venture into uncharted territories, wave the flag of TypeScript annotations high, for by following the charts laid out in this compendium, your code, like the most legendary galleons, shall weather the harshest storms and discover the marvels of typified order.