TypeScript

Intro


    // install ts from npm
    npm install -g typescript

    // symlink to node for special cases
    ln -s /usr/bin/nodejs /usr/bin/node

    // compile ts file to js
    tsc intro.ts
  

Basic Types


    // use 'let' instead of 'var' whenever possible

    // --- BOOLEAN
    let isDone: boolean = false;

    // --- NUMBER
    let decimal: number = 6;
    let hex: number = 0xf00d;
    let binary: number = 0b1010;
    let octal: number = 0o744;

    // --- STRING
    let color: string = "blue";
    color = 'red';

    // --- TEMPLATE STRINGS
    let fullName: string = `Bob Bobbington`;
    let age: number = 37;
    let sentence: string = `Hello, my name is ${ fullName }.
    I'll be ${ age + 1 } years old next month.`;
    export interface Success {
      type: `${string}Success`;
      body: string;
    }
    export interface Error {
      type: `${string}Error`;
      message: string
    }
    export function handler(r: Success | Error) {
      if (r.type === "HttpSuccess") {
        const token = r.body; // (parameter) r: Success
      }
    }
    // Symbol value is not allowed in a template string, use String()
    let str = `hello ${Symbol()}`; // TypeError: Cannot convert a Symbol value to a string

    // --- ARRAY
    let list: number[] = [1, 2, 3];
    let list: Array<number> = [1, 2, 3];
    // ReadonlyArray - when no mutation is intended
    function foo(arr: ReadonlyArray<string>) {
      arr.slice();        // okay
      arr.push("hello!"); // error!
    }
    // new syntax
    function foo(arr: readonly string[]) {
      arr.slice();        // okay
      arr.push("hello!"); // error!
    }
    type Arr = readonly any[];
    function concat<T extends Arr, U extends Arr>(arr1: T, arr2: U): [...T, ...U] {
      return [...arr1, ...arr2];
    }

    // --- TUPLE
    let x: [string, number];
    x = ["hello", 10]; // OK
    x = [10, "hello"]; // Error
    x[0].substr(1); // OK
    x[1].substr(1); // Error, 'number' does not have 'substr'
    // tuple items out of range, uses defined types
    x[3] = "world"; // OK, 'string' can be assigned to 'string | number'
    x[5].toString() // OK, 'string' and 'number' both have 'toString'
    x[6] = true; // Error, 'boolean' isn't 'string | number'
    // spreads in tuple type syntax can be generic
    function tail<T extends any[]>(arr: readonly [any, ...T]) {
      const [_ignored, ...rest] = arr;
      return rest;
    }
    const myTuple = [1, 2, 3, 4] as const;
    const myArray = ["hello", "world"];
    const r1 = tail(myTuple);
    const r1: [2, 3, 4]
    const r2 = tail([...myTuple, ...myArray] as const);
    const r2: [2, 3, 4, ...string[]]
    // rest elements can occur anywhere in a tuple
    // so long as it iss not followed by another optional element or rest element.
    // only one rest element per tuple, and no optional elements after rest elements
    let e: [string, string, ...boolean[]];
    e = ["hello", "world"];
    e = ["hello", "world", false];
    e = ["hello", "world", true, false, true];
    let foo: [...string[], number];
    foo = [123];
    foo = ["hello", 123];
    foo = ["hello!", "hello!", "hello!", 123];
    let bar: [boolean, ...string[], boolean];
    bar = [true, false];
    bar = [true, "some text", false];
    bar = [true, "some", "separated", "text", false];
    declare function doStuff(...args: [...names: string[], shouldCapitalize: boolean]): void;
    doStuff(/*shouldCapitalize:*/ false)
    doStuff("fee", "fi", "fo", "fum", /*shouldCapitalize:*/ true);
    // tuple labels, all elements in the tuple must also be labeled
    type Range = [start: number, end: number];
    type Foo = [first: number, second?: string, ...rest: any[]];
    function foo(x: [first: string, second: number]) {
        // ...
        // no need to name these 'first' and 'second'
        const [a, b] = x;
        a // const a: string
        b // const b: number
    }
    // optional labels or names for each element:
    type Pair<T> = [first: T, second: T];
    type TwoOrMore<T> = [first: T, second: T, rest: ...T[]];

    // --- ENUM
    enum Color {Red, Green, Blue} // start members numbering with 0
    // enum Color {Red = 1, Green, Blue}
    // enum Color {Red = 1, Green = 2, Blue = 4}
    let c: Color = Color.Green;// c == 0

    // --- OBJECT - represents the non-primitive type:
    // NOT A number, string, boolean, symbol, null, or undefined
    // APIs like Object.create can be better represented:
    declare function create(o: object | null): void;
    create({ prop: 0 }); // OK
    create(null); // OK
    create(42); // Error
    create("string"); // Error
    create(false); // Error
    create(undefined); // Error

    // --- ANY - allows calling arbitrary methods (:Object - not)
    let notSure: any = 4;
    notSure = "maybe a string instead";
    notSure = false; // okay, definitely a boolean
    let list: any[] = [1, true, "free"];
    list[1] = 100;
    let prettySure: Object = 4;
    prettySure.toFixed(); // Error: Property 'toFixed' doesn't exist on type 'Object'

    // --- VOID - no type at all
    function warnUser(): void {
      console.log("This is my warning message");
    }
    let unusable: void = undefined; // or assing null, as second type allowed

    // --- NULL and UNDEFINED - subtypes of all other types.
    let u: undefined = undefined;
    let n: null = null;

    // --- NEVER - values that never occur, subtype of all, but not assignable by other
    // in function - when it throws error or has no return
    // in variables - when narrowed by any type guards that can never be true
    function error(message: string): never { // must have unreachable end point
      throw new Error(message);
    }
  

ASSERTION


    // --- TYPE ASSERTION - when you know more about a value than TS does
    // angle bracked syntax:
    let someValue: any = "this is a string";
    let strLength: number = (<string>someValue).length;
    // as - syntax:
    let someValue: any = "this is a string";
    let strLength: number = (someValue as string).length;

    // --- CONST ASSERTIONS
    //  - no literal types in that expression should be widened (e.g. no going from "hello" to string)
    //  - object literals get readonly properties
    //  - array literals become readonly tuples
    let x = "hello" as const; // type '"hello"'
    let y = [10, 20] as const; // type 'readonly [10, 20]'
    let z = { text: "hello" } as const; // type '{ readonly text: "hello" }'
    // outside of .tsx files, the angle bracket assertion syntax can also be used
    let x = <const>"hello"; // type '"hello"'
    let y = <const>[10, 20]; // type 'readonly [10, 20]'
    let z = <const>{ text: "hello" }; // type '{ readonly text: "hello" }'
    // can only be applied immediately on simple literal expressions
    let a = (Math.random() < 0.5 ? 0 : 1) as const; // error
    let b = Math.random() < 0.5 ?
      0 as const :
      1 as const; // works
    // types that would otherwise be used just to hint immutability to the compiler can often be omitted
    function getShapes() {
      let result = [
        { kind: "circle", radius: 100, },
        { kind: "square", sideLength: 50, },
      ] as const; // no types referenced or declared, only single const assertion
      return result;
    }
    for (const shape of getShapes()) {
      // Narrows perfectly!
      if (shape.kind === "circle") {
        console.log("Circle radius", shape.radius);
      }
      else {
        console.log("Square side length", shape.sideLength);
      }
    }
    // enum-like patterns in plain JavaScript
    export const Colors = {
      red: "RED",
      blue: "BLUE",
      green: "GREEN",
    } as const;
    export default {
      red: "RED",
      blue: "BLUE",
      green: "GREEN",
    } as const;

    // --- ASSERTION FUNCTIONS
    // whatever gets passed into the 'condition' parameter must be true
    // if the assert returns (because otherwise it would throw an error).
    // for the rest of the scope, that condition must be truthy
    function assert(condition: any, msg?: string): asserts condition {
      if (!condition) {
        throw new AssertionError(msg);
      }
    }
    // catch original yell example
    function yell(str) {
      assert(typeof str === "string");
      return str.toUppercase(); // Err: property 'toUppercase' does not exist on type 'string', did you mean 'toUpperCase'?
    }

    // tells TS that a specific variable or property has a different type
    function assertIsString(val: any): asserts val is string {
      if (typeof val !== "string") {
        throw new AssertionError("Not a string!");
      }
    }
    function yell(str: any) {
      assertIsString(str); // TS knows that 'str' is a 'string'
      return str.toUppercase(); // Err...
    }
    function isString(val: any): val is string {
      return typeof val === "string";
    }
    function yell(str: any) {
      if (isString(str)) {
        return str.toUppercase();
      }
      throw "Oops!";
    }
    function assertIsDefined<T>(val: T): asserts val is NonNullable<T> {
      if (val === undefined || val === null) {
        throw new AssertionError(
          `Expected 'val' to be defined, but received ${val}`
        );
      }
    }
    // also can be defined as:
    function throwIfNullable<T>(value: T): NonNullable<T> {
      if (value === undefined || value === null) {
        throw Error("Nullable value!");
      }
      return value;
    }
  

Variables


    // --- SCOPING
    function f(input: boolean) {
      let a = 100;
      if (input) {
        let b = a + 1; // Still okay to reference 'a'
        return b;
      }
      return b; // Error: 'b' doesn't exist here
    }

    // --- SHADOWING
    function sumMatrix(matrix: number[][]) {
      let sum = 0;
      for (let i = 0; i < matrix.length; i++) {
        var currentRow = matrix[i];
        for (let i = 0; i < currentRow.length; i++) {
          sum += currentRow[i];
        }
      }
      return sum;
    }

    // --- DESTRUCTURING
    let input = [1, 2];
    let [first, second] = input;
    console.log(first); // outputs 1
    console.log(second); // outputs 2
    // swap variables
    [first, second] = [second, first];

    function f([first, second]: [number, number]) {
      console.log(first);
      console.log(second);
    }
    f([1, 2]);

    let [first, ...rest] = [1, 2, 3, 4];
    console.log(first); // outputs 1
    console.log(rest); // outputs [ 2, 3, 4 ]

    let [first] = [1, 2, 3, 4];
    console.log(first); // outputs 1

    let [, second, , fourth] = [1, 2, 3, 4];
    // mark destructured variables as unused by prefixing them with an underscore _
    let [_first, second] = getValues();

    let o = {
      a: "foo",
      b: 12,
      c: "bar"
    };
    let { a, b } = o; // a=foo, b=12
    let { a: newName1, b: newName2 } = o; // property renaming and assigning: newName1=foo
    let { a, b }: { a: string, b: number } = o;

    let { a, ...passthrough } = o;
    let total = passthrough.b + passthrough.c.length;

    ({ a, b } = { a: "baz", b: 101 }); // assignment without declaration

    function keepWholeObject(
      wholeObject: { a: string, b?: number }
    ) {
      let { a, b = 1001 } = wholeObject; // default value
    }

    // function declarations
    type C = { a: string, b?: number }
    function f({ a, b }: C): void { /*...*/ }
    function f({ a="", b=0 } = {}): void { /*...*/ }
    function f({ a, b = 0 } = { a: "" }): void { /*...*/ }
    f({ a: "yes" }); // ok, default b = 0
    f(); // ok, default to { a: "" }, which then defaults b = 0
    f({}); // error, 'a' is required if you supply an argument

    class Thing {
      someProperty = 42;
      someMethod() {
      }
    }
    function foo<T extends Thing>(x: T) {
      let { someProperty, ...rest } = x;
      // error: Property 'someMethod' does not exist on type 'Omit<T, "someProperty" | "someMethod">'
      // unspreadable and non-public members are dropped
      rest.someMethod();
    }

    // --- SPREAD
    let first = [1, 2];
    let second = [3, 4];
    let bothPlus = [0, ...first, ...second, 5]; // [0,1,2,3,4,5]

    let defaults = { food: "spicy", price: "$$", ambiance: "noisy" };
    let search = { ...defaults, food: "rich" };
    // { food: "rich", price: "$$", ambiance: "noisy" }
    let search = { food: "rich", ...defaults };
    // { food: "spicy", price: "$$", ambiance: "noisy" }

    class C {
      p = 12;
      m() {
      }
    }
    let c = new C();
    let clone = { ...c };
    clone.p; // ok
    clone.m(); // error! , not enumerable property - function

    // "let" - create scope per iteration !
    for (let i = 0; i < 10 ; i++) {
      setTimeout(function() { console.log(i); }, 100 * i);
    }
    // instead of ...
    for (var i = 0; i < 10; i++) {
      // capture the current state of 'i'
      // by invoking a function with its current value
      (function(i) {
        setTimeout(function() { console.log(i); }, 100 * i);
      })(i);
    }
  

Interface


    // --- PROPERTIES type interface
    interface SquareConfig {
      id: number; // required
      // readonly id: number; // required, modifiable when an object is first created
      // const for variable, readonly for properties
      color?: string; // optional
      width?: number; // optional
      [propName: string]: any; // any number of other properties
    }
    function createSquare(config: SquareConfig): {color: string; area: number} {
      let newSquare = {color: "white", area: 100};
      if (config.color) {
        newSquare.color = config.color;
      }
      if (config.width) {
        newSquare.area = config.width * config.width;
      }
      return newSquare;
    }
    let mySquare = createSquare({id: 1, color: "black"});
    // avoid errors for undefined properties, and create
    let mySquare = createSquare({ width: 100, opacity: 0.5 } as SquareConfig);
    // same with precreated config object
    let squareOptions = { clr: "red", width: 100 };
    let mySquare = createSquare(squareOptions);

    let mySquare: SquareConfig = {id: 1, color: "black"};
    p1.id = 5; // error! if id was set to readonly

    let a: number[] = [1, 2, 3, 4];
    let ro: ReadonlyArray<number> = a;
    ro[0] = 12; // error!
    ro.push(5); // error!
    ro.length = 100; // error!
    a = ro; // error!
    a = ro as number[]; // OK, using type assertion

    interface Thing {
      get size(): number
      set size(value: number | string | boolean);
    }

    // --- FUNCTION type interface
    interface SearchFunc {
      (source: string, subString: string): boolean;
    }
    let mySearch: SearchFunc;
    mySearch = function(source: string, subString: string) {
    // names of the parameters do not need to match
    // mySearch = function(src: string, sub: string): boolean {
    // OR
    // mySearch = function(src, sub) {
      let result = source.search(subString);
      return result > -1;
    }

    // --- INDEXing, type index and value
    interface StringArray {
      // when a StringArray is indexed with a number, it will return a string
      [index: number]: string;
    }
    let myArray: StringArray;
    myArray = ["Bob", "Fred"];
    let myStr: string = myArray[0];

    class Animal { name: string; }
    class Dog extends Animal { breed: string; }
    // Error: indexing with a numeric string might get completely separate type of Animal!
    interface NotOkay {
      [x: number]: Animal;
      [x: string]: Dog;
    }

    interface NumberDictionary {
      [index: string]: number;
      length: number;    // ok, length is a number
      name: string;      // error, the type of 'name' is not a subtype of the indexer
    }

    interface ReadonlyStringArray {
      readonly [index: number]: string;
    }
    let myArray: ReadonlyStringArray = ["Alice", "Bob"];
    myArray[2] = "Mallory"; // error! readonly index

    // --- CLASS intefaces, describe only public side of the class
    // when a class implements an interface, only the instance side of the class is checked
    // since the constructor sits in the static side, it is not included in this check
    // separate cnstructor and static sides:
    interface ClockConstructor {
      new (hour: number, minute: number): ClockInterface;
    }
    interface ClockInterface { tick(); }
    function createClock(
      ctor: ClockConstructor, hour: number, minute: number
    ): ClockInterface {
      return new ctor(hour, minute);
    }
    class DigitalClock implements ClockInterface {
      constructor(h: number, m: number) { }
      tick() { console.log("beep beep"); }
    }
    class AnalogClock implements ClockInterface {
      constructor(h: number, m: number) { }
      tick() { console.log("tick tock"); }
    }
    let digital = createClock(DigitalClock, 12, 17);
    let analog = createClock(AnalogClock, 7, 32);

    // EXTEND one or more interfaces
    interface Shape { color: string; }
    interface PenStroke { penWidth: number; }
    interface Square extends Shape, PenStroke {
      sideLength: number;
    }
    let square = <Square>{};
    square.color = "blue";
    square.sideLength = 10;
    square.penWidth = 5.0;

    // --- HYBRID acts as object, function, props
    interface Counter {
      (start: number): string;
      interval: number;
      reset(): void;
    }
    function getCounter(): Counter {
      let counter = <Counter>function (start: number) { };
      counter.interval = 123;
      counter.reset = function () { };
      return counter;
    }
    let c = getCounter();
    c(10);
    c.reset();
    c.interval = 5.0;

    // --- EXTEND CLASS
    class Control { private state: any; }
    // within Control class it is possible to access state instance of SelectableControl
    interface SelectableControl extends Control { // contains all of the members of Control
      select(): void;
    }
    class Button extends Control implements SelectableControl {
      select() { }
    }
    class TextBox extends Control {
      select() { }
    }
    // Error: Property 'state' is missing in type 'Image'.
    class Image implements SelectableControl {
      select() { }
    }
    class Location { }

    // --- type parameter variance, good for libraries authors, accuracy and type-checking speed
    type Getter<out T> = () => T; // Getter is covariant on T - "out" modifier
    type Setter<in T> = (value: T) => void; // Setter is contravariant on T - "in" modifier
    interface State<in out T> { // T is used in both an output and input position - invariant
      get: () => T;
      set: (value: T) => void;
    }
  

Functions


    function myAdd(x: number, y: number): number {
      return x + y;
    }
    let myAdd = function(x: number, y: number): number { return x + y; };
    let myAdd: (baseValue: number, increment: number) => number =
      function(x: number, y: number): number { return x + y; };
    // to avoid error, if property is not references later in the signature:
    declare function makePerson(options: { name: string, age: number }): Person;
    declare function makePerson({ name, age }: { name: string, age: number }): Person;

    function makeThing(): Thing {
      let size = 0;
      return {
        get size(): number { return size; },
        set size(value: string | number | boolean) {
          let num = Number(value);
          if (!Number.isFinite(num)) { // dont allow NaN and stuff
            size = 0;
            return;
          }
          size = num;
        },
      };
    }

    // --- OPTIONAL parameter
    function buildName(firstName: string, lastName?: string) {
      if (lastName) { return firstName + " " + lastName; }
      else { return firstName; }
    }

    // --- DEFAULT-INITIALIZED parameter, pass undefined to use it if is in middle
    function buildName(firstName: string, lastName = "Smith") {
      return firstName + " " + lastName;
    }

    // --- REST parameters
    function buildName(firstName: string, ...restOfName: string[]) {
      return firstName + " " + restOfName.join(" ");
    }
    let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie");
    let buildNameFun: (fname: string, ...rest: string[]) => string = buildName;

    // arrow functions captures "this" where the function is created
    // rather than where it is invoked
    interface Card {
      suit: string;
      card: number;
    }
    interface Deck {
      suits: string[];
      cards: number[];
      createCardPicker(this: Deck): () => Card;
    }
    let deck: Deck = {
      suits: ["hearts", "spades", "clubs", "diamonds"],
      cards: Array(52),
      // // line below is now an arrow function, allowing us to capture 'this' right here
      // return () => {
      // function now explicitly specifies that its callee must be of type Deck
      createCardPicker: function(this: Deck) {
        return () => {
          let pickedCard = Math.floor(Math.random() * 52);
          let pickedSuit = Math.floor(pickedCard / 13);
          return {suit: this.suits[pickedSuit], card: pickedCard % 13};
        }
      }
    }
    let cardPicker = deck.createCardPicker();
    let pickedCard = cardPicker();
    alert("card: " + pickedCard.card + " of " + pickedCard.suit);

    // --- OVERLOAD, defining return types for multipurpose function
    let suits = ["hearts", "spades", "clubs", "diamonds"];
    function pickCard(x: {suit: string; card: number; }[]): number;
    function pickCard(x: number): {suit: string; card: number; };
    function pickCard(x): any {
      // Check to see if we're working with an object/array
      // if so, they gave us the deck and we'll pick the card
      if (typeof x == "object") {
        let pickedCard = Math.floor(Math.random() * x.length);
        return pickedCard;
      }
      // Otherwise just let them pick the card
      else if (typeof x == "number") {
        let pickedSuit = Math.floor(x / 13);
        return { suit: suits[pickedSuit], card: x % 13 };
      }
    }
    let myDeck = [
      { suit: "diamonds", card: 2 },
      { suit: "spades", card: 10 },
      { suit: "hearts", card: 4 }
    ];
    let pickedCard1 = myDeck[pickCard(myDeck)];
    alert("card: " + pickedCard1.card + " of " + pickedCard1.suit);
    let pickedCard2 = pickCard(15);
    alert("card: " + pickedCard2.card + " of " + pickedCard2.suit);
    // calling pickCard with any other parameter types would cause an error

    // --- allowed undefined-returning functions
    function f4(): undefined {
      // no returns
    }
    takesFunction((): undefined => {
      // no returns
    });
    takesFunction(function f() { // return type is undefined
      // no returns
    });
    takesFunction(function f() { // return type is undefined
      return;
    });
    // under '--noImplicitReturns'
    function f(): undefined {
      if (Math.random()) {
        // do some stuff...
        return;
      }
    }
  

Classes


      // superclass
      class Animal {
        name: string;

        name: string; // is as default
        // public name: string; // is as default
        // protected name: string; // same, can also be accessed within deriving classes
        // private name: string; // unavailable outside class, unique to the containing class
        // #name: string; // ES private fields
        // constructor(name: string) { this.#name = name; }
        // greet() { console.log(`Hello, my name is ${this.#name}!`); }

        // readonly name: string; // not modifiable after definition
        // readonly numberOfLegs: number = 8; // initialized or in constructor

        constructor(theName: string) { this.name = theName; }
        // create class parameter while constructing: public|private|protected|readonly
        // constructor(readonly name: string) {...}
        // protected constructor() {...} - class cant be instantiated, but can be extended

        move(distanceInMeters: number = 0) {
          console.log(`${this.name} moved ${distanceInMeters}m.`);
        }

        // private methods and accessors
        #someMethod() { }
        get #someValue() { return 100; }
        publicMethod() {
          this.#someMethod();
          return this.#someValue;
        }
        static #someMethod() { }
        equals(other: unknown) {
          return other &&
              typeof other === "object" &&
              #name in other && // narrow the type of other as Person
              this.#name === other.#name;
        }
      }
      // constructors in derived classes must call super()
      // before any references to 'this'
      class Snake extends Animal {
        constructor(name: string) { super(name); }
        move(distanceInMeters = 5) {
          console.log("Slithering...");
          super.move(distanceInMeters);
        }
      }
      class Horse extends Animal {
        constructor(name: string) { super(name); }
        move(distanceInMeters = 45) {
          console.log("Galloping...");
          super.move(distanceInMeters);
        }
      }
      let sam = new Snake("Sammy the Python");
      let tom: Animal = new Horse("Tommy the Palomino");
      sam.move();
      tom.move(34);

      // --- ACCESSORS (getters/setters)
      // set the compiler to output ES5 or higher !
      let passcode = "secret passcode";
      class Employee {
        private _fullName: string;
        get fullName(): string { return this._fullName; }
        set fullName(newName: string) {
          if (passcode && passcode == "secret passcode") {
            this._fullName = newName;
          }
          else {
            console.log("Error: Unauthorized update of employee!");
          }
        }
      }
      let employee = new Employee();
      employee.fullName = "Bob Smith";
      if (employee.fullName) {
        console.log(employee.fullName);
      }
      class Thing {
        #size = 0;
        get size(): number { return this.#size; }
        set size(value: string | number | boolean) {
          let num = Number(value);
          // Don't allow NaN and stuff.
          if (!Number.isFinite(num)) {
            this.#size = 0; return;
          }
          this.#size = num;
        }
      }
      let thing = new Thing();
      thing.size = "hello";
      thing.size = true;
      thing.size = 42;
      let mySize: number = thing.size; // reading 'thing.size' always produces a number!
      // ECMAScript auto-accessors
      // "de-sugared" to a get and set accessor with an unreachable private property: #__name: string;
      class Person {
        accessor name: string;
        constructor(name: string) {
          this.name = name;
        }
      }
      // completely unrelated types for get and set accessor with explicit type annotations are allowed
      interface CSSStyleRule {
        get style(): CSSStyleDeclaration; // always reads as a "CSSStyleDeclaration"
        set style(newValue: string); // can only write a "string" here
      }
      class SafeBox {
        #value: string | undefined;
        set value(newValue: string) { ... } // Only accepts strings!
        get value(): string | undefined { // Must check for 'undefined'!
          return this.#value;
        }
      }

      // --- STATIC properties
      class Grid {
        static origin = {x: 0, y: 0}; // instead of this.
        calculateDistanceFromOrigin(point: {x: number; y: number;}) {
          let xDist = (point.x - Grid.origin.x);
          let yDist = (point.y - Grid.origin.y);
          return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
        }
        constructor (public scale: number) { }
      }
      let grid1 = new Grid(1.0);  // 1x scale
      let grid2 = new Grid(5.0);  // 5x scale
      console.log(grid1.calculateDistanceFromOrigin({x: 10, y: 10}));
      console.log(grid2.calculateDistanceFromOrigin({x: 10, y: 10}));

      // --- STATIC blocks
      class Foo {
        static #count = 0;
        static prop = 1
        get count() { return Foo.#count; }
        static {
            try {
                const lastInstances = loadLastInstances();
                Foo.#count += lastInstances.length;
            }
            catch {}
        }
        // runs in the same order in which they are written
        static { console.log(Foo.prop++); }
      }

      // --- ABSTRACT classes, defines implementation details
      abstract class Department {
        constructor(public name: string) { }
        printName(): void {
          console.log("Department name: " + this.name);
        }
        abstract printMeeting(): void; // must be implemented in derived classes
        abstract prop: number; // no initializer, just type
      }
      class AccountingDepartment extends Department {
        constructor() {
          super("Accounting and Auditing");
        }
        printMeeting(): void {
          console.log("The Accounting Department meets each Monday at 10am.");
        }
        generateReports(): void {
          console.log("Generating accounting reports...");
        }
      }
      let department: Department; // ok to create a reference to an abstract type
      department = new Department(); // error: cannot create an instance of an abstract class
      department = new AccountingDepartment(); // ok to create and assign a non-abstract subclass
      department.printName();
      department.printMeeting();
      department.generateReports(); // error: method doesn't exist on declared abstract type

      // --- ADVANCED
      class Greeter {
        static standardGreeting = "Hello, there";
        greeting: string;
        greet() {
          if (this.greeting) {
            return "Hello, " + this.greeting;
          }
          else {
            return Greeter.standardGreeting;
          }
        }
      }
      let greeter1: Greeter; // using class type
      greeter1 = new Greeter(); // using class constructor
      console.log(greeter1.greet()); // ...
      let greeterMaker: typeof Greeter = Greeter; // obtain and override class
      greeterMaker.standardGreeting = "Hey there!";
      let greeter2: Greeter = new greeterMaker();
      console.log(greeter2.greet());
      // class as interface
      class Point {
        x: number;
        y: number;
      }
      interface Point3d extends Point {
        z: number;
      }
      let point3d: Point3d = {x: 1, y: 2, z: 3};
      // - override, TS will always make sure that a method with the same name exists in a the base class
      class SomeComponent {
        // show() { }
        // hide() { }
        setVisible(value: boolean) { }
      }
      class SpecializedComponent extends SomeComponent {
        // Err: member cannot have an 'override' modifier because it is not declared in the base class 'SomeComponent'
        override show() { }
      }
    

"using" declaration


    // ...
    {
        "compilerOptions": {
            "target": "es2022",
            "lib": ["es2022", "esnext.disposable", "dom"] // "esnext" or "esnext.disposable"
        }
    }
    // ...

    class TempFile implements Disposable { // "Disposable" is a global type
      #path: string;
      #handle: number;
      constructor(path: string) {
          this.#path = path;
          this.#handle = fs.openSync(path, "w+");
      }
      // other methods ...
      [Symbol.dispose]() {
          // close the file and delete it
          fs.closeSync(this.#handle);
          fs.unlinkSync(this.#path);
      }
    }
    export function doSomeWork() {
      const file = new TempFile(".some_temp_file");
      try { ... }
      finally {
        file[Symbol.dispose]();
      }
    }
    // or just
    export function doSomeWork() {
      // declare new fixed bindings, kind of like "const"
      using file = new TempFile(".some_temp_file");
      // use file...
      if (someCondition()) {
        // do some more work...
        return;
      }
      // Symbol.dispose method will be called at the end of the scope, oron return, throw
      // for variables declared with "using".
      // supposed to be resilient to exceptions; if an error is thrown, it is rethrown after disposal
    }
    // also disposes in a first-in-last-out order like a stack
    function loggy(id: string): Disposable {
      console.log(`Creating ${id}`);
      return {
        [Symbol.dispose]() {
          console.log(`Disposing ${id}`);
        }
      }
    }
    function func() {
      using a = loggy("a");
      using b = loggy("b");
        {
          using c = loggy("c");
          using d = loggy("d");
        }
      using e = loggy("e");
      return;
      // Unreachable.
      // Never created, never disposed.
      using f = loggy("f");
     }
     func();
     // Creating a
     // Creating b
     // Creating c
     // Creating d
     // Disposing d
     // Disposing c
     // Creating e
     // Disposing e
     // Disposing b
     // Disposing a

     // --- "await using" and asyncDispose - when resource disposal involves asynchronous operations
     // AsyncDisposable type describes any object with an asynchronous dispose method
     async function doWork() {
      await new Promise(resolve => setTimeout(resolve, 500));
    }
    function loggy(id: string): AsyncDisposable {
      console.log(`Constructing ${id}`);
      return {
        async [Symbol.asyncDispose]() {
          console.log(`Disposing (async) ${id}`);
          await doWork();
        },
      }
    }
    async function func() {
      await using a = loggy("a");
      await using b = loggy("b");
      {
        await using c = loggy("c");
        await using d = loggy("d");
      }
      await using e = loggy("e");
      return;
      // Unreachable.
      // Never created, never disposed.
      await using f = loggy("f");
    }
    func();
    // Constructing a
    // Constructing b
    // Constructing c
    // Constructing d
    // Disposing (async) d
    // Disposing (async) c
    // Constructing e
    // Disposing (async) e
    // Disposing (async) b
    // Disposing (async) a

    // --- DisposableStack and AsyncDisposableStack
    // for doing both one-off clean-up, along with arbitrary amounts of cleanup,
    // keeping track of Disposable objects, disposes of everything it keeps track of like a stack.
    // they are also Disposable, can be assigned with "using" to variables because
    function doSomeWork() {
      const path = ".some_temp_file";
      const file = fs.openSync(path, "w+");
      using cleanup = new DisposableStack();
      // then call "defer" immediately after creating a resource
      cleanup.defer(() => { // callback will be run once cleanup is disposed of
        fs.closeSync(file);
        fs.unlinkSync(path);
      });
      // use file...
      if (someCondition()) {
        // do some more work...
        return;
      }
      // ...
    }

    // --- SuppressedError (subtype of Error), when logic before or during disposal throws an error
    class ErrorA extends Error {
      name = "ErrorA";
    }
    class ErrorB extends Error {
      name = "ErrorB";
    }
    function throwy(id: string) {
      return {
        [Symbol.dispose]() {
          throw new ErrorA(`Error from ${id}`);
        }
      };
    }
    function func() {
      using a = throwy("a");
      throw new ErrorB("oops!")
    }
    try {
      func();
    }
    catch (e: any) {
      console.log(e.name); // SuppressedError
      console.log(e.message); // An error was suppressed during disposal.
      console.log(e.error.name); // ErrorA
      console.log(e.error.message); // Error from a
      console.log(e.suppressed.name); // ErrorB
      console.log(e.suppressed.message); // oops!
    }
  

Generics


    // uses and returns array of any type provided
    function loggingIdentity<T>(arg: T[]): T[] {
      console.log(arg.length);  // array has .length, so no error
      return arg;
    }
    function loggingIdentity<T>(arg: Array<T>): Array<T> {
      console.log(arg.length);  // Array has a .length, so no more error
      return arg;
    }

    // --- TYPES
    function identity<T>(arg: T): T { return arg; }
    let myIdentity: <T>(arg: T) => T = identity;
    let myIdentity: <U>(arg: U) => U = identity;
    let myIdentity: {<T>(arg: T): T} = identity;

    // --- INTERFACE
    interface GenericIdentityFn { <T>(arg: T): T; }
    function identity<T>(arg: T): T { return arg; }
    let myIdentity: GenericIdentityFn = identity;

    // --- CLASSES, one time type definition assigns that type to specific memebers
    class GenericNumber<T> {
      zeroValue: T;
      add: (x: T, y: T) => T;
    }
    let myGenericNumber = new GenericNumber<number>();
    myGenericNumber.zeroValue = 0;
    myGenericNumber.add = function(x, y) { return x + y; };
    let stringNumeric = new GenericNumber<string>();
    stringNumeric.zeroValue = "";
    stringNumeric.add = function(x, y) { return x + y; };
    stringNumeric.add(stringNumeric.zeroValue, "test");
    // factory
    function create<T>(c: {new(): T; }): T { return new c(); }
    // factory advanced
    class BeeKeeper { hasMask: boolean; }
    class ZooKeeper { nametag: string; }
    class Animal { numLegs: number; }
    class Bee extends Animal { keeper: BeeKeeper; }
    class Lion extends Animal { keeper: ZooKeeper; }
    function createInstance<A extends Animal>(c: new () => A): A {
      return new c();
    }
    createInstance(Lion).keeper.nametag;  // typechecks!
    createInstance(Bee).keeper.hasMask;   // typechecks!

    // --- INSTANTIATION EXPRESSIONS
    interface Box<T> {
      value: T;
    }
    function makeBox<T>(value: T) {
      return { value };
    }
    // take functions and constructors and feed them type arguments directly
    const makeHammerBox = makeBox<Hammer>;
    const makeWrenchBox = makeBox<Wrench>;
    // instead of wrapping
    function makeHammerBox(hammer: Hammer) {
      return makeBox(hammer);
    }
    const makeWrenchBox: (wrench: Wrench) => Box<Wrench> = makeBox;

    // --- CONSTRAINTS
    interface Lengthwise { length: number; }
    function loggingIdentity<T extends Lengthwise>(arg: T): T {
      console.log(arg.length);  // we know we have a .length property, no more error
      return arg;
    }
    loggingIdentity({length: 10, value: 3});
    // with type parameters
    function getProperty<T, K extends keyof T>(obj: T, key: K) {
      return obj[key];
    }
    let x = { a: 1, b: 2, c: 3, d: 4 };
    getProperty(x, "a"); // okay
    getProperty(x, "m"); // error: 'm' isn't assignable to 'a' | 'b' | 'c' | 'd'

    // ... it is not possible to create generic enums and namespaces
  

Enums


    enum Direction_nr {
      Up = 1, // default would be 0 if not initialized
      Down,
      Left,
      Right,
    }
    Direction_nr.Up; // 1
    enum Direction_str { // adviced to be all strings definition
      Up = "UP",
      Down = "DOWN",
      Left = "LEFT",
      Right = "RIGHT",
    }
    Direction_str.Up; // "UP"
    enum E {
      A = 10 * 10,  // Numeric literal enum member
      B = "foo",    // String literal enum member
      C = bar(42)   // Opaque computed enum member
    }

    // --- CONSTANTS, evaluated at start time :
    enum E { X } // first member and not initialized (=1)
    enum E1 { X, Y, Z }
    enum E2 { A = 5, B, C } // previous member is a numeric constant
    // 1) string or numeric literal
    // 2) reference to previous constant
    // 3) parenthesized constant enum expression
    // 4) +, -, ~ unary operators applied to constant enum expression
    // 5) +, -, *, /, %, <<, >>, >>>, &, |, ^ binary operators with constant enum expressions as operands
    // 6) evaluated to NaN or Infinity = compile time error
    // in all other cases = computed
    enum FileAccess {
      // constant members
      None,
      Read    = 1 << 1,
      Write   = 1 << 2,
      ReadWrite  = Read | Write,
      // computed member
      G = "123".length
    }
    // enums without initializers either need to be first
    // or have to come after numeric enums initialized
    // with numeric constants or other constant enum members

    // --- LITERAL enum members: "foo","bar",... ; 1,100,... ; -1,-100,...
    // enum members become types:
    enum ShapeKind { Circle, Square }
    interface Circle {
      kind: ShapeKind.Circle;
      radius: number;
    }
    interface Square {
      kind: ShapeKind.Square;
      sideLength: number;
    }
    let c: Circle = {
      kind: ShapeKind.Square, // error
      radius: 100,
    }
    // and union (knowing of all members) of each member:
    enum E { Foo, Bar }
    function f(x: E) {
      if (x !== E.Foo || x !== E.Bar) { // E. is anum, no sense to check both members
        // error! '!==' cannot be applied to types 'E.Foo' and 'E.Bar'.
      }
    }
    // enums are real object at runtime
    function f(obj: { X: number }) { return obj.X; }
    f(E);// ok, E has property X
    // reverse mapping (for numeric only!)
    enum Enum { A }
    let a = Enum.A;
    let nameOfA = Enum[a]; // "A"

    // --- CONST enums, no computed values, removed during compilation
    const enum Directions { Up, Down, Left, Right }
    let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right]

    // --- AMBIENT enums
    declare enum Enum {
      A = 1,
      B,
      C = 2
    }
  

Iterators, Generators


    // Iterator type allows to specify the yielded type, the returned type, and the type that next can accept
    interface Iterator<T, TReturn = any, TNext = undefined> {
      // Takes either 0 or 1 arguments - doesn't accept 'undefined'
      next(...args: [] | [TNext]): IteratorResult<T, TReturn>;
      return?(value?: TReturn): IteratorResult<T, TReturn>;
      throw?(e?: any): IteratorResult<T, TReturn>;
    }

    // ---iterable interface
    function toArray<X>(xs: Iterable<X>): X[] {
      return [...xs]
    }

    // --- for..of - loops over an iterable object
    let someArray = [1, "string", false];
    for (let entry of someArray) {
      console.log(entry); // 1, "string", false
    }

    // --- for..of vs. for..in
    // both iterate over lists
    // for..in returns a list of keys on the object being iterated
    //         operates on any object, serves to inspect properties on this object
    // for..of returns a list of values of the numeric properties of the object being iterated
    //         is mainly interested in values of iterable objects
    let list = [4, 5, 6];
    for (let i in list) { console.log(i) } // "0", "1", "2"
    for (let i of list) { console.log(i) } // 4, 5, 6
    let pets = new Set(["Cat", "Dog", "Hamster"]);
    pets["species"] = "mammals";
    for (let pet in pets) { console.log(pet) } // "species"
    for (let pet of pets) { console.log(pet) } // "Cat", "Dog", "Hamster"


    // Generator type is an Iterator that always has both the return and throw methods present, and is also iterable
    interface Generator<T = unknown, TReturn = any, TNext = unknown>
      extends Iterator<T, TReturn, TNext> {
      next(...args: [] | [TNext]): IteratorResult<T, TReturn>;
      return(value: TReturn): IteratorResult<T, TReturn>;
      throw(e: any): IteratorResult<T, TReturn>;
      [Symbol.iterator](): Generator<T, TReturn, TNext>;
    }

    // yields numbers, returns strings, can be passed in booleans
    function* counter(): Generator<number, string, boolean> {
      let i = 0;
      while (true) {
        if (yield i++) {
          break;
        }
      }
      return "done!";
    }
    var iter = counter();
    var curr = iter.next();
    while (!curr.done) {
      console.log(curr.value);
      curr = iter.next(curr.value === 5);
    }
    console.log(curr.value.toUpperCase()); // prints: 0 1 2 3 4 5 DONE!
  

Type Inference


    let zoo = [new Rhino(), new Elephant(), new Snake()];
    // -> set a super-type for all inner items
    let zoo: Animal[] = [new Rhino(), new Elephant(), new Snake()];
    // when no best common type is found, resulting inference is the union array type
    // (Rhino | Elephant | Snake)[]
    function createZoo(): Animal[] {
      return [new Rhino(), new Elephant(), new Snake()];
    }
    // avoid error because of unfound property of current context
    window.onmousedown = function(mouseEvent: any) {
      console.log(mouseEvent.clickTime);  // now, no error is given
    };

    // --- "const" modifier, no need to write 'as const':
    type HasNames = { names: readonly string[] };
    function getNamesExactly<const T extends HasNames>(arg: T): T["names"] {
      return arg.names;
    }
    const names = getNamesExactly({ names: ["Alice", "Bob", "Eve"] }); // inferred type: : readonly [...]
    const names2 = getNamesExactly({ names: ["Alice", "Bob", "Eve"]} as const); // older solution
    // does not reject mutable values, and does not require immutable constraints:
    declare function fnBad<const T extends string[]>(args: T): void;
    // 'T' will be 'string[]', 'readonly ["a", "b", "c"]' is not assignable to mutable 'string[]',
    // inference falls back to the constraint
    fnBad(["a", "b" ,"c"]);
    // better definition , with readonly string[]:
    declare function fnGood<const T extends readonly string[]>(args: T): void;
    fnGood(["a", "b" ,"c"]); // T is readonly ["a", "b", "c"]
    // modifier only affects inference of object, array and primitive expressions that were written within the call,
    // arguments which wouldnt (or couldnt) be modified with as const wont see any change in behavior:
    const arr = ["a", "b" ,"c"];
    fnGood(arr); // 'T' is still 'string[]'-- the 'const' modifier has no effect here

    // --- "satisfies" operator
    // validate that the type of an expression matches some type, without changing the resulting type of that expression.
    // validate that all the properties of palette are compatible with string | number[] :
    type Colors = "red" | "green" | "blue";
    type RGB = [red: number, green: number, blue: number];
    const palette = {
      red: [255, 0, 0],
      green: "#00ff00",
      bleu: [0, 0, 255] // error, typo !
    } satisfies Record<Colors, string | RGB>;
    // both methods are accessible
    const redComponent = palette.red.at(0);
    const greenNormalized = palette.green.toUpperCase();
    // ensure that an object has all the keys of some type, but no more:
    const favoriteColors = {
      "red": "yes",
      "green": false,
      "blue": "kinda",
      "platypus": false // error - "platypus" was never listed in 'Colors'
    } satisfies Record<Colors, unknown>;
    const g: boolean = favoriteColors.green; // information about the 'red', 'green', and 'blue' properties are retained
    // ensure that all of an object property values conform to some type:
    type RGB = [red: number, green: number, blue: number];
    const palette = {
      red: [255, 0, 0],
      green: "#00ff00",
      blue: [0, 0] // error !
    } satisfies Record<string, string | RGB>;
    const redComponent = palette.red.at(0);
    const greenNormalized = palette.green.toUpperCase();

    // higher order type inference from generic functions
    // compose takes two other functions:
    //    f which takes some argument (of type A) and returns a value of type B
    //    g which takes an argument of type B (the type f returned),
    //    and returns a value of type C
    // compose then returns a function which feeds its argument through f and then g
    function compose<A, B, C>(f: ((x: A) => B, g: (y: B) => C): ((x: A) => C {
      return x => g(f(x));
    }
    function arrayify<T>(x: T): T[] {
      return [x];
    }
    type Box<U> = { value: U };
    function boxify<U>(y: U): Box<U> {
      return { value: y };
    }
    let newFn = compose(arrayify, boxify);
    // inference allows newFn to be generic, its new type is <T>(x: T) => Box<T[]>
    // work on constructor functions as well
    class Box<T> {
      kind: "box";
      value: T;
      constructor(value: T) { this.value = value; }
    }
    class Bag<U> {
      kind: "bag";
      value: U;
      constructor(value: U) { this.value = value; }
    }
    function composeCtor<T, U, V>(
      F: new (x: T) => U,
      G: new (y: U) => V
    ): (x: T) => V {
      return x => new G(new F(x));
    }
    let f = composeCtor(Box, Bag); // type '<T>(x: T) => Bag<Box<T>>'
    let a = f(1024); // type 'Bag<Box<number>>'
    // functions that operate on class components in certain UI libraries like React can more correctly operate on generic class components
    type ComponentClass<P> = new (props: P) => Component<P>;
    declare class Component<P> {
      props: P;
      constructor(props: P);
    }
    declare function myHoc<P>(C: ComponentClass<P>): ComponentClass<P>;
    type NestedProps<T> = { foo: number; stuff: T };
    declare class GenericComponent<T> extends Component<NestedProps<T>> {}
    // type is 'new <T>(props: NestedProps<T>) => Component<NestedProps<T>>'
    const GenericComponent2 = myHoc(GenericComponent);
  

Type Compatibility


    interface Named { name: string; }
    class Person { name: string; }
    let x: Named;
    p = new Person(); // OK, because of structural typing

    let y = { name: "Alice", location: "Seattle" };
    x = y; // OK, y has "name" property

    function greet(n: Named) { console.log("Hello, " + n.name); }
    greet(y); // OK, members of the target type were checked

    // --- FUNCTIONS
    // assignment succeeds if the source parameter is assignable to the target parameter
    // or vice versa
    // when comparing functions for compatibility:
    // - optional and required parameters are interchangeable
    // not an error:
    // - extra optional parameters of the source type
    // - optional parameters of the target type without corresponding parameters in the source type
    let x = (a: number) => 0;
    let y = (b: number, s: string) => 0;
    y = x; // OK
    x = y; // Error
    let x = () => ({name: "Alice"});
    let y = () => ({name: "Alice", location: "Seattle"});
    x = y; // OK
    y = x; // Error, because x() lacks a location property
    // - rest parameter - treated as if it were an infinite series of optional parameters;
    // - overload in the source type must be matched by a compatible signature on the target type;

    // --- ENUMS, compatible with numbers, and numbers are compatible with enums
    // values from different enum types are considered incompatible
    enum Status { Ready, Waiting };
    enum Color { Red, Blue, Green };
    let status = Status.Ready;
    status = Color.Green;  // Error

    // --- CLASSES
    // only members of the instance are compared
    // static members and constructors do not affect compatibility
    class Animal {
      feet: number;
      constructor(name: string, numFeet: number) { }
    }
    class Size {
      feet: number;
      constructor(numFeet: number) { }
    }
    let a: Animal;
    let s: Size;
    a = s;  // OK
    s = a;  // OK
    // private and protected members:
    // if the target type contains a private/protected member,
    // source type must also contain private/protected member originated from the same class
    // this allows a class to be assignment compatible with its super class,
    // but not with classes from a different inheritance hierarchy

    // --- GENERICS
    interface Empty<T> { }
    let x: Empty<number>;
    let y: Empty<string>;
    x = y;  // OK, because y matches structure of x
    interface NotEmpty<T> { data: T; }
    let x: Empty<number>;
    let y: Empty<string>;
    x = y;  // Error, because x and y are not compatible

    let identity = function<T>(x: T): T { ... }
    let reverse = function<U>(y: U): U { ... }
    identity = reverse;  // OK, because (x: any) => any matches (y: any) => any
  

Type Mixins/Union


    // --- MIXIN, intersection types - &
    function extend<T, U>(first: T, second: U): T & U {
      let result = <T & U>{};
      for (let id in first) {
        (<any>result)[id] = (<any>first)[id];
      }
      for (let id in second) {
        if (!result.hasOwnProperty(id)) {
          (<any>result)[id] = (<any>second)[id];
        }
      }
      return result;
    }
    class Person { constructor(public name: string) { } }
    interface Loggable { log(): void; }
    class ConsoleLogger implements Loggable { log() { ... } }
    var jim = extend(new Person("Jim"), new ConsoleLogger());
    var n = jim.name;
    jim.log();

    // --- UNION - |
    function padLeft(value: string, padding: string | number) {
      if (typeof padding === "number") { return Array(padding + 1).join(" ") + value; }
      if (typeof padding === "string") { return padding + value; }
      throw new Error(`Expected string or number, got '${padding}'.`);
    }
    let indentedString = padLeft("Hello world", true); // errors during compilation

    interface Bird { fly(); layEggs(); }
    interface Fish { swim(); layEggs(); }
    function getSmallPet(): Fish | Bird { ... }
    let pet = getSmallPet();
    pet.layEggs(); // okay
    pet.swim();    // errors, not a common member for all union types

    // narrow the union with code
    // narrowing occurs when TypeScript can deduce a more specific type for a value based on the structure of the code
    function printId(id: number | string) {
      if (typeof id === "string") { console.log(id.toUpperCase()) } // only a string value will have a typeof value 'string'
      else { console.log(id) }
    }
    // use a function like Array.isArray
    function welcomePeople(x: string[] | string) {
      if (Array.isArray(x)) { // 'x' is 'string[]'
        console.log("Hello, " + x.join(" and "));
      } else { // 'x' is 'string'
        console.log("Welcome lone traveler " + x);
      }
    }

    // --- DISCRIMINATED UNIONS (also known as tagged unions or algebraic data types)
    // combination of singleton types, union types, type guards, and type aliases
    // 1 - types that have a common, singleton type property — the discriminant
    // 2 - type alias that takes the union of those types — the union
    // 3 - type guards on the common property
    interface Square { kind: "square"; size: number; }
    interface Rectangle { kind: "rectangle"; width: number; height: number; }
    interface Circle { kind: "circle"; radius: number; }
    // kind property - discriminant or tag
    type Shape = Square | Rectangle | Circle;
    function area(s: Shape) {
      switch (s.kind) {
        case "square": return s.size * s.size;
        case "rectangle": return s.height * s.width;
        case "circle": return Math.PI * s.radius ** 2;
        // exhaustiveness checking
        default: return assertNever(s); // error here if there are missing cases
      }
    }
    type Action =
      | { kind: "NumberContents"; payload: number }
      | { kind: "StringContents"; payload: string };
    function processAction(action: Action) {
      if (action.kind === "NumberContents") { // 'action.payload' is a number
        let num = action.payload * 2;
      } else if (action.kind === "StringContents") { // 'action.payload' is a string
        const str = action.payload.trim();
      }
    }
    function assertNever(x: never): never {
      throw new Error("Unexpected object: " + x);
    }
  

Type Guards


    // --- TYPE GUARDS - check that guarantees the type in some scope
    if (pet.swim) { pet.swim(); } // error
    else if (pet.fly) { pet.fly(); } // error
    // using type assertion
    if ((<Fish>pet).swim) { (<Fish>pet).swim(); } // OK
    else { (<Bird>pet).fly(); } // OK
    // just define a function whose return type is a type predicate
    // predicate - takes the form: parameterName is Type,
    // narrowing variable to specific type
    function isFish(pet: Fish | Bird): pet is Fish {
      return (<Fish>pet).swim !== undefined;
    }
    if (isFish(pet)) { pet.swim(); }
    else { pet.fly(); }

    // --- typeof
    // typeof v === "typename" AND typeof v !== "typename"
    // "typename" must be "number", "string", "boolean", or "symbol"
    // as known expression strings
    //
    // instead of:
    // ...
    // function isNumber(x: any): x is number {
    //   return typeof x === "number";
    // }
    // if (isNumber(padding)) {
    //   return Array(padding + 1).join(" ") + value;
    // }
    // ...
    function padLeft(value: string, padding: string | number) {
      if (typeof padding === "number") {
        return Array(padding + 1).join(" ") + value;
      }
      if (typeof padding === "string") {
        return padding + value;
      }
      throw new Error(`Expected string or number, got '${padding}'.`);
    }
    function foo(arg: unknown) {
      const argIsString = typeof arg === "string";
      if (argIsString) {
        console.log(arg.toUpperCase());
      }
    }

    // --- instanceof
    // right side of the "instanceof" needs to be a constructor function,
    // and TypeScript will narrow down to:
    // 1) the type of the function "prototype" property if its type is not "any"
    // 2) the union of types returned by that type construct signatures
    interface Padder {
      getPaddingString(): string
    }
    class SpaceRepeatingPadder implements Padder {
      constructor(private numSpaces: number) { }
      getPaddingString() {
        return Array(this.numSpaces + 1).join(" ");
      }
    }
    class StringPadder implements Padder {
      constructor(private value: string) { }
      getPaddingString() {
        return this.value;
      }
    }
    function getRandomPadder() {
      return Math.random() < 0.5 ?
        new SpaceRepeatingPadder(4) :
        new StringPadder("  ");
    }
    // Type is 'SpaceRepeatingPadder | StringPadder'
    let padder: Padder = getRandomPadder();
    if (padder instanceof SpaceRepeatingPadder) {
      padder; // type narrowed to 'SpaceRepeatingPadder'
    }
    if (padder instanceof StringPadder) {
      padder; // type narrowed to 'StringPadder'
    }
  

null, undefined


    // --- NULLable, null and undefined are seen differently

    // flag --strictNullChecks avoids assigning of null to variables, unless explicitly
    let s = "foo";
    s = null; // error, 'null' is not assignable to 'string'
    let sn: string | null = "bar";
    sn = null; // ok
    sn = undefined; // error, 'undefined' is not assignable to 'string | null'

    // and adds |undefined to optional parameters/properties
    function f(x: number, y?: number) { return x + (y || 0); }
    f(1, 2);
    f(1);
    f(1, undefined);
    f(1, null); // error, 'null' is not assignable to 'number | undefined'

    class C { a: number; b?: number; }
    let c = new C();
    c.a = 12;
    c.a = undefined; // error, 'undefined' is not assignable to 'number'
    c.b = 13;
    c.b = undefined; // ok
    c.b = null; // error, 'null' is not assignable to 'number | undefined'

    // --- type assertion
    function f(sn: string | null): string {
      return sn || "default";
      // instead of:
      // if (sn == null) { return "default"; }
      // else { return sn; }
    }
    // postfix "!" - removes null/undefined from type without doing any explicit checking
    function fixed(name: string | null): string {
      function postfix(epithet: string) {
        return name!.charAt(0) + '.  the ' + epithet; // ok
      }
      name = name || "Bob";
      return postfix("great");
    }
    function liveDangerously(x?: number | null) {
      console.log(x!.toFixed()); // no error
    }
  

Aliases


    // --- ALIASES, new name to refer to a type
    export type BasicPrimitive = number | string | boolean;
    export function doStuff(value: BasicPrimitive) {
      let x = value;
      return x;
    }
    type Name = string;
    type NameResolver = () => string;
    type NameOrResolver = Name | NameResolver;
    function getName(n: NameOrResolver): Name {
      if (typeof n === "string") {
        return n;
      }
      else {
        return n();
      }
    }
    // generic, type parameters defined
    type Container<T> = { value: T };
    // type alias refers to itself in a property
    type Tree<T> = {
      value: T;
      left: Tree<T>;
      right: Tree<T>;
    }
    // with intersection types
    type LinkedList<T> = T & { next: LinkedList<T> };
    interface Person { name: string; }
    var people: LinkedList<Person>;
    var s = people.name;
    var s = people.next.name;
    var s = people.next.next.name;
    var s = people.next.next.next.name;
    // its not possible for a type alias to appear anywhere else on the right side
    type Yikes = Array<Yikes>; // error

    // type aliases as. interfaces:
    // 1) interfaces create a new name that is used everywhere
    //    alias - object literal type
    type Alias = { num: number }
    interface Interface { num: number; }
    declare function aliased(arg: Alias): Alias;
    declare function interfaced(arg: Interface): Interface;
    // 2) cannot be extended/implemented from (nor can extend/implement other types)

    // --- RECURSIVE, reference type aliases
    type Json =
      | string
      | number
      | boolean
      | null
      | { [property: string]: Json }
      | Json[];
    type VirtualNode = string | [string, { [key: string]: any }, ...VirtualNode[]];
    const myNode: VirtualNode = [
      "div",
      { id: "parent" },
      ["div", { id: "first-child" }, "I'm the first child"],
      ["div", { id: "second-child" }, "I'm the second child"],
    ];
  

Template|Literal Types

LITERAL TYPES - specify exact value a string must have


    type Easing = "ease-in" | "ease-out" | "ease-in-out"; // string literal type
    class UIElement {
      animate(dx: number, dy: number, easing: Easing) {
        if (easing === "ease-in") { ... }
        else if (easing === "ease-out") { ... }
        else if (easing === "ease-in-out") { ... }
        else {
          // error! should not pass null or undefined.
        }
      }
    }
    let button = new UIElement();
    button.animate(0, 0, "ease-in");
    button.animate(0, 0, "uneasy"); // error: "uneasy" is not allowed here

    function rollDice(): 1 | 2 | 3 | 4 | 5 | 6 { // numeric literal type
      // ...
    }

    // combine with non-literal types:
    interface Options {
      width: number;
    }
    function configure(x: Options | "auto") {
      // ...
    }
    configure({ width: 100 });
    configure("auto");
    configure("automatic"); // Err: argument of type 'automatic' is not assignable to parameter of type 'Options | "auto"'

    // --- INFERENCE
    declare function handleRequest(url: string, method: "GET" | "POST"): void;
    const req = { url: "https://example.com", method: "GET" };
    handleRequest(req.url, req.method); // Err: argument of type 'string' is not assignable to '"GET" | "POST"'
    // solution 1
    const req = { url: "https://example.com", method: "GET" as "GET" };
    // solution 2
    handleRequest(req.url, req.method as "GET");
    // solution 3
    const req = { url: "https://example.com", method: "GET" } as const;
    handleRequest(req.url, req.method);
  

TEMPLATE LITERAL(STRING) TYPES


    // concrete literal types - new string literal type by concatenating the contents
    type World = "world";
    type Greeting = `hello ${World}`; // type Greeting = "hello world"

    // interpolated position - every possible string literal that could be represented by each union member
    type EmailLocaleIDs = "welcome_email" | "email_heading";
    type FooterLocaleIDs = "footer_title" | "footer_sendoff";
    type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`;
    // type AllLocaleIDs = "welcome_email_id" | "email_heading_id" | "footer_title_id" | "footer_sendoff_id"
    // for each interpolated position unions are cross multiplied:
    type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`;
    type Lang = "en" | "ja" | "pt";
    type LocaleMessageIDs = `${Lang}_${AllLocaleIDs}`;
    // type LocaleMessageIDs = "en_welcome_email_id" | "en_email_heading_id" | ... | "ja_welcome_email_id" | ... | "pt_footer_sendoff_id"Try
    declare let s1: `${number}-${number}-${number}`;
    declare let s2: `1-2-3`;
    declare let s3: `${number}-2-3`;
    declare let s4: `1-${number}-3`;
    declare let s5: `1-2-${number}`;
    declare let s6: `${number}-2-${number}`;
    s1 = s2;
    s1 = s3;
    s1 = s4;
    s1 = s5;
    s1 = s6;
    function bar(s: string): `hello ${string}` {
      return `hello ${s}`;
    }
    declare function foo<V extends string>(arg: `*${V}*`): V;
      function test<T extends string>(s: string, n: number, b: boolean, t: T) {
          let x1 = foo("*hello*");            // "hello"
          let x2 = foo("**hello**");          // "*hello*"
          let x3 = foo(`*${s}*` as const);    // string
          let x4 = foo(`*${n}*` as const);    // `${number}`
          let x5 = foo(`*${b}*` as const);    // "true" | "false"
          let x6 = foo(`*${t}*` as const);    // `${T}`
          let x7 = foo(`**${s}**` as const);  // `*${string}*`
      }

    // --- string unions in types
    type PropEventSource<Type> = {
      on(eventName: `${string & keyof Type}Changed`, callback: (newValue: any) => void): void;
    };
    // create a "watched object" with an 'on' method
    // so that you can watch for changes to properties.
    declare function makeWatchedObject<Type>(obj: Type): Type & PropEventSource<Type>;
    const person = makeWatchedObject({
      firstName: "Saoirse",
      lastName: "Ronan",
      age: 26
    });
    person.on("firstNameChanged", () => {});
    // it is typo-resistent
    person.on("firstName", () => {});
    // Err: argument of type 'firstName' is not assignable to parameter of type firstNameChanged|lastNameChanged|ageChanged
    person.on("frstNameChanged", () => {});
    // Err: argument of type 'frstNameChanged' is not assignable to parameter of type firstNameChanged|lastNameChanged|ageChanged

    // --- inference with template literals
    // make last example generic
    type PropEventSource<Type> = {
      on<Key extends string & keyof Type>
        (eventName: `${Key}Changed`, callback: (newValue: Type[Key]) => void ): void;
    };
    declare function makeWatchedObject<Type>(obj: Type): Type & PropEventSource<Type>;
    const person = makeWatchedObject({
      firstName: "Saoirse",
      lastName: "Ronan",
      age: 26
    });
    person.on("firstNameChanged", newName => { // (parameter) newName: string
      console.log(`new name is ${newName.toUpperCase()}`);
    });
    person.on("ageChanged", newAge => { // (parameter) newAge: number
      if (newAge < 0) {
        console.warn("warning! negative age");
      }
    })
    // inference can be combined in different ways, often to deconstruct|reconstruct strings in different ways
  

Intrinsic String Manipulation Types


    // --- Uppercase<StringType>
    type Greeting = "Hello, world"
    type ShoutyGreeting = Uppercase<Greeting> // type ShoutyGreeting = "HELLO, WORLD"
    type ASCIICacheKey<Str extends string> = `ID-${Uppercase<Str>}`
    type MainID = ASCIICacheKey<"my_app"> // type MainID = "ID-MY_APP"

    // --- Lowercase<StringType>
    type Greeting = "Hello, world"
    type QuietGreeting = Lowercase<Greeting> // type QuietGreeting = "hello, world"
    type ASCIICacheKey<Str extends string> = `id-${Lowercase<Str>}`
    type MainID = ASCIICacheKey<"MY_APP"> // type MainID = "id-my_app"

    // --- Capitalize<StringType>
    type LowercaseGreeting = "hello, world";
    type Greeting = Capitalize<LowercaseGreeting>; // type Greeting = "Hello, world"

    // --- Uncapitalize<StringType>
    type UppercaseGreeting = "HELLO WORLD";
    type UncomfortableGreeting = Uncapitalize<UppercaseGreeting>; // type UncomfortableGreeting = "hELLO WORLD"

    // technical details on the intrinsic string manipulation types
    // not locale aware
    function applyStringMapping(symbol: Symbol, str: string) {
      switch (intrinsicTypeKinds.get(symbol.escapedName as string)) {
        case IntrinsicTypeKind.Uppercase: return str.toUpperCase();
        case IntrinsicTypeKind.Lowercase: return str.toLowerCase();
        case IntrinsicTypeKind.Capitalize: return str.charAt(0).toUpperCase() + str.slice(1);
        case IntrinsicTypeKind.Uncapitalize: return str.charAt(0).toLowerCase() + str.slice(1);
      }
      return str;
    }
  

Index Types


    // --- INDEX TYPES, check code that uses dynamic property name
    function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
      return names.map(n => o[n]); // o[name] is of type T[K]
    }
    // "keyof T" - index type query operator, union of known, public property names of T
    // "T[K]" - indexed access operator
    interface Person { name: string; age: number; }
    let person: Person = { name: 'Jarid', age: 35 };
    let strings: string[] = pluck(person, ['name']); // ok, string[]
    pluck(person, ['age', 'unknown']); // error, 'unknown' is not in 'name' | 'age'
    let personProps: keyof Person; // 'name' | 'age'
    // keyof and T[K] interact with string index signatures
    // if you have a type with a string index signature, keyof T will just be string
    // and T[string] is just the type of the index signature
    interface Map<T> { [key: string]: T; }
    let keys: keyof Map<number>; // string
    let value: Map<number>['foo']; // number
    // index signatures allow more properties on a value than a type explicitly declares
    class Foo {
      hello = "hello";
      world = 1234;
      static prop = true; // Err: boolean not assignable to string|number|undefined
      [propName: string]: string | number | undefined; // index signature
      static [propName: string]: string | number | undefined;
    }
    let instance = new Foo();
    instance["whatever"] = 42;
    let x = instance["something"]; // type 'string | number | undefined'
    // declare a type that can be keyed on arbitrary symbol
    interface Colors {
      [sym: symbol]: number;
    }
    const red = Symbol("red");
    const green = Symbol("green");
    const blue = Symbol("blue");
    let colors: Colors = {};
    // Assignment of a number is allowed
    colors[red] = 255;
    let redVal = colors[red];
    let redVal: number
    colors[blue] = "da ba dee"; // Type 'string' is not assignable to type 'number'
    // index signature with template string pattern type
    interface Options {
      width?: number;
      height?: number;
    }
    let a: Options = {
        width: 100,
        height: 100,
        "data-blah": true,
    };
    interface OptionsWithDataProps extends Options {
        [optName: `data-${string}`]: unknown; // Permit any property starting with 'data-'
    }
    let b: OptionsWithDataProps = {
        width: 100,
        height: 100,
        "data-blah": true,
        // Fails for a property which is not known, nor starts with 'data-'
        "unknown-property": true,
    };
    // index signature whose argument is a union of these types will de-sugar
    interface Data {
      [optName: string | symbol]: any;
    }
    // Equivalent to
    interface Data {
      [optName: string]: any;
      [optName: symbol]: any;
    }
  

Mapped Types


    // new types based on old types
    type Keys = 'option1' | 'option2';
    type Flags = { [K in Keys]: boolean };
    // same as:
    type Flags = {
      option1: boolean;
      option2: boolean;
    }

    // based on some existing type, and transform the properties in some way

    // --- homomorphic
    type Readonly<T> = { readonly [P in keyof T]: T[P]; }
    type A = Readonly<{ a: string, b: number }>; // { readonly a: string, readonly b: number }
    type B = Readonly<number[]>; // readonly number[]
    type C = Readonly<[string, boolean]>; // readonly [string, boolean]
    type Writable<T> = { -readonly [K in keyof T]: T[K] } // strips away readonly
    type A = Writable<{ // { a: string, b: number }
      readonly a: string;
      readonly b: number
    }>;
    type B = Writable<readonly number[]>; // number[]
    type C = Writable<readonly [string, boolean]>; // [string, boolean]
    type Partial<T> = { [P in keyof T]?: T[P]; }
    type Nullable<T> = { [P in keyof T]: T[P] | null }
    type Pick<T, K extends keyof T> = { [P in K]: T[P]; }
    type PersonPartial = Partial<Person>;
    type ReadonlyPerson = Readonly<Person>;
    type NullablePerson = Nullable<Person>;

    // --- not homomorphic - creates new properties, cant copy property modifiers from anywhere
    type Record<K extends string, T> = { [P in K]: T; }
    type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string>
    // T[P] wrapped in a Proxy<T> class
    type Proxy<T> = {
      get(): T;
      set(value: T): void;
    }
    type Proxify<T> = {
      [P in keyof T]: Proxy<T[P]>;
    }
    function proxify<T>(o: T): Proxify<T> {
      // ... wrap proxies ...
    }
    let proxyProps = proxify(props);

    // --- unwrap properties of a type, works on homomorphic mapped types
    function unproxify<T>(t: Proxify<T>): T {
        let result = {} as T;
        for (const k in t) {
          result[k] = t[k].get();
        }
        return result;
    }
    let originalProps = unproxify(proxyProps);
    // if the mapped type is not homomorphic, give an explicit type parameter to unwrapping function

    // --- new object types based on arbitrary keys
    type Options = {
      [K in "noImplicitAny" | "strictNullChecks" | "strictFunctionTypes"]?: boolean;
    };
    // same as
    //   type Options = {
    //       noImplicitAny?: boolean,
    //       strictNullChecks?: boolean,
    //       strictFunctionTypes?: boolean
    //   };
    // or new object types based on other object types
    // 'Partial<T>' is the same as 'T', but with each property marked optional.
    type Partial<T> = {
      [K in keyof T]?: T[K];
    };

    // --- re-map keys in mapped types with a 'as' clause
    type MappedTypeWithNewKeys<T> = {
      [K in keyof T as NewKeyType]: T[K]
    }

    // with template literal types property names based off of old ones
    type Getters<T> = {
      [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
    };
    interface Person {
      name: string;
      age: number;
      location: string;
    }
    type LazyPerson = Getters<Person>;
    // type LazyPerson = {
    //   getName: () => string;
    //   getAge: () => number;
    //   getLocation: () => string;
    // }
  

Conditional Types


    // --- CONDITIONAL TYPES, express non-uniform type mappings
    // based on a condition expressed as a type relationship test, example:
    // when T is assignable to U the type is X, otherwise the type is Y, or deferred
    // T extends U ? X : Y
    declare function f<T extends boolean>(x: T): T extends true ? string : number;
    let x = f(Math.random() < 0.5) // type is 'string | number
    // with nested conditions
    type TypeName<T> =
      T extends string ? "string" :
      T extends number ? "number" :
      T extends boolean ? "boolean" :
      T extends undefined ? "undefined" :
      T extends Function ? "function" :
      "object";
    type T0 = TypeName<string>;  // "string"
    type T1 = TypeName<"a">;  // "string"
    type T2 = TypeName<true>;  // "boolean"
    type T3 = TypeName<() => void>;  // "function"
    type T4 = TypeName<string[]>;  // "object"
    // deffered
    interface Foo { propA: boolean; propB: boolean; }
    declare function f<T>(x: T): T extends Foo ? string : number;
    function foo<U>(x: U) {
      let a = f(x); // has type 'U extends Foo ? string : number' - deffered
      let b: string | number = a; // this assignment is allowed - picked
    }
    // distributive conditional types -
    // automatically distributed over union types during instantiation.
    // "T extends U ? X : Y" with the type argument "A|B|C" for T, is resolved as
    // "(A extends U ? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y)"
    type T10 = TypeName<string | (() => void)>;  // "string"|"function"
    type T12 = TypeName<string | string[] | undefined>;  // "string"|"object"|"undefined"
    type T11 = TypeName<string[] | number[]>;  // "object"
    type BoxedValue<T> = { value: T };
    type BoxedArray<T> = { array: T[] };
    type Boxed<T> = T extends any[] ? BoxedArray<T[number]> : BoxedValue<T>;
    // BoxedValue<string>;
    type T20 = Boxed<string>;
    // BoxedArray<number>;
    type T21 = Boxed<number[]>;
    // BoxedValue<string> | BoxedArray<number>;
    type T22 = Boxed<string | number[]>;
    // distributive property of conditional types can be used to filter union types
    // Remove types from T that are assignable to U
    type Diff<T, U> = T extends U ? never : T;
    // Remove types from T that are not assignable to U
    type Filter<T, U> = T extends U ? T : never;
    type T30 = Diff<"a" | "b" | "c" | "d", "a" | "c" | "f">;  // "b" | "d"
    type T31 = Filter<"a" | "b" | "c" | "d", "a" | "c" | "f">;  // "a" | "c"
    type T32 = Diff<string|number | (() => void), Function>;  // string | number
    type T33 = Filter<string|number | (() => void), Function>;  // () => void
    type NonNullable<T> = Diff<T, null|undefined>;  // Remove null and undefined from T
    type T34 = NonNullable<string|number|undefined>;  // string | number
    type T35 = NonNullable<string|string[]|null|undefined>;  // string | string[]
    function f1<T>(x: T, y: NonNullable<T>) {
      x = y;  // Ok
      y = x;  // Error
    }
    function f2<T extends string | undefined>(x: T, y: NonNullable<T>) {
      x = y;  // Ok
      y = x;  // Error
      let s1: string = x;  // Error
      let s2: string = y;  // Ok
    }
    // combined with mapped types
    type FunctionPropertyNames<T> =
      { [K in keyof T]: T[K] extends Function ? K : never }[keyof T];
    type FunctionProperties<T> = Pick<T, FunctionPropertyNames<T>>;
    type NonFunctionPropertyNames<T> =
      { [K in keyof T]: T[K] extends Function ? never : K }[keyof T];
    type NonFunctionProperties<T> = Pick<T, NonFunctionPropertyNames<T>>;
    interface Part {
      id: number;
      name: string;
      subparts: Part[];
      updatePart(newName: string): void;
    }
    type T40 = FunctionPropertyNames<Part>;  // "updatePart"
    type T41 = NonFunctionPropertyNames<Part>;  // "id" | "name" | "subparts"
    type T42 = FunctionProperties<Part>;  // { updatePart(newName: string): void }
    type T43 = NonFunctionProperties<Part>;  // { id: number, name: string, subparts: Part[]
    // not permitted to reference themselves recursively
    type ElementType<T> = T extends any[] ? ElementType<T[number]> : T;  // Error

    // "infer" declarations introduce a type variable to be inferred, in the true branch
    // is possible to have multiple infer locations for the same type variable.
    // extract the return type of a function type:
    type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
    // nested to form a sequence of pattern matches that are evaluated in order:
    type Unpacked<T> =
      T extends (infer U)[] ? U :
      T extends (...args: any[]) => infer U ? U :
      T extends Promise<infer U> ? U :
      T;
    type T0 = Unpacked<string>;  // string
    type T1 = Unpacked<string[]>;  // string
    type T2 = Unpacked<() => string>;  // string
    type T3 = Unpacked<Promise<string>>;  // string
    type T4 = Unpacked<Promise<string>[]>;  // Promise<string>
    type T5 = Unpacked<Unpacked<Promise<string>[]>>;  // string
    // union type inferred
    // for multiple candidates of same type variable in co-variant positions
    type Foo<T> = T extends { a: infer U, b: infer U } ? U : never;
    type T10 = Foo<{ a: string, b: string }>;  // string
    type T11 = Foo<{ a: string, b: number }>;  // string | number
    // intersection type inferred
    // for multiple candidates for the same type variable in contra-variant positions
    type Bar<T> = T extends { a: (x: infer U) => void, b: (x: infer U) => void } ? U : never;
    type T20 = Bar<{ a: (x: string) => void, b: (x: string) => void }>; // string
    type T21 = Bar<{ a: (x: string) => void, b: (x: number) => void }>; // string & number
    // inferring from a type with multiple call signatures (overload,...)
    // inferences are made from the last signature (catch-all case)
    declare function foo(x: string): number;
    declare function foo(x: number): string;
    declare function foo(x: string | number): string | number;
    type T30 = ReturnType<typeof foo>;  // string | number
    // is not possible
    // to use infer declarations in constraint clauses for regular type parameters
    type ReturnType<T extends (...args: any[]) => infer R> = R;  // Error, not supported
    // use:
    type AnyFunction = (...args: any[]) => any;
    type ReturnType<T extends AnyFunction> = T extends (...args: any[]) => infer R ? R : any;
    // constraint on any infer type
    type FirstIfString<T> =
      T extends [infer S extends string, ...unknown[]]
        ? S
        : never;
    type A = FirstIfString<[string, number, number]>;// string
    type C = FirstIfString<["hello" | "world", boolean]>; // "hello" | "world"
    type D = FirstIfString<[boolean, number, string]>; // never
    type TryGetNumberIfFirst<T> =
      T extends [infer U extends number, ...unknown[]] ? U : never;
    type SomeNum = "100" extends `${infer U extends number}` ? U : never; // 100
    type SomeBigInt = "100" extends `${infer U extends bigint}` ? U : never; // 100n
    type SomeBool = "true" extends `${infer U extends boolean}` ? U : never; // true

    // --- recursive conditional types
    type ElementType<T> = T extends ReadonlyArray<infer U> ? ElementType<U> : T;
    function deepFlatten<T extends readonly unknown[]>(x: T): ElementType<T>[] {
      throw "not implemented";
    }
    // All of these return the type 'number[]':
    deepFlatten([1, 2, 3]);
    deepFlatten([[1], [2, 3]]);
    deepFlatten([[1], [[2]], [[[3]]]]);
    // Awaited type to deeply unwrap Promises
    type Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T;
    /// Like `promise.then(...)`, but more accurate in types.
    declare function customThen<T, U>(
      p: Promise<T>,
      onFulfilled: (value: Awaited<T>) => U
    ): Promise<Awaited<U>>;

    // --- predefined conditional types (lib.d.ts)
    // Exclude<T, U> - Exclude from T those types that are assignable to U
    // Extract<T, U> - Extract from T those types that are assignable to U
    // NonNullable<T> - Exclude null and undefined from T
    // ReturnType<T> - Obtain the return type of a function type
    // InstanceType<T> - Obtain the instance type of a constructor function type
    type T00 = Exclude<"a" | "b" | "c" | "d", "a" | "c" | "f">;  // "b" | "d"
    type T01 = Extract<"a" | "b" | "c" | "d", "a" | "c" | "f">;  // "a" | "c"
    type T02 = Exclude<string | number | (() => void), Function>;  // string | number
    type T03 = Extract<string | number | (() => void), Function>;  // () => void
    type T04 = NonNullable<string | number | undefined>;  // string | number
    type T05 = NonNullable<(() => string) | string[] | null | undefined>;  // (() => string) | string[]
    function f1(s: string) { return { a: 1, b: s }; }
    class C { x = 0; y = 0; }
    type T10 = ReturnType<() => string>;  // string
    type T11 = ReturnType<(s: string) => void>;  // void
    type T12 = ReturnType<(<T>() => T)>;  // {}
    type T13 = ReturnType<(<T extends U, U extends number[]>() => T)>;  // number[]
    type T14 = ReturnType<typeof f1>;  // { a: number, b: string }
    type T15 = ReturnType<any>;  // any
    type T16 = ReturnType<never>;  // any
    type T17 = ReturnType<string>;  // Error
    type T18 = ReturnType<Function>;  // Error
    type T20 = InstanceType<typeof C>;  // C
    type T21 = InstanceType<any>;  // any
    type T22 = InstanceType<never>;  // any
    type T23 = InstanceType<string>;  // Error
    type T24 = InstanceType<Function>;  // Error
    // Omit<T, K> type because it is trivially written as Pick<T, Exclude<keyof T, K>>
  

Utility Types


    // --- Partial<Type> - all properties set to optional
    // returns a type that represents all subsets of a given type
    interface Todo {
      title: string;
      description: string;
    }
    function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
      return { ...todo, ...fieldsToUpdate };
    }
    const todo1 = {
      title: "organize desk",
      description: "clear clutter",
    };
    const todo2 = updateTodo(todo1, {
      description: "throw out trash",
    });

    // --- Required<Type> - all properties set to required
    // opposite of Partial
    interface Props {
      a?: number;
      b?: string;
    }
    const obj: Props = { a: 5 };
    const obj2: Required<Props> = { a: 5 }; // Err: roperty 'b' is missing ...

    // --- Readonly<Type> - all properties set to readonly
    // meaning the properties of the constructed type cannot be reassigned
    interface Todo {
      title: string;
    }
    const todo: Readonly<Todo> = {
      title: "Delete inactive users",
    };
    todo.title = "Hello"; // Err: cannot assign to 'title' because it is a read-only
    // useful for representing assignment expressions that will fail at runtime
    // (i.e. when attempting to reassign properties of a frozen object)
    Object.freeze
    function freeze<Type>(obj: Type): Readonly<Type>;

    // --- Record<Keys,Type>
    // constructs an object type whose property keys are Keys and whose property values are Type
    // can be used to map the properties of a type to another type
    interface CatInfo {
      age: number;
      breed: string;
    }
    type CatName = "miffy" | "boris" | "mordred";
    const cats: Record<CatName, CatInfo> = {
      miffy: { age: 10, breed: "Persian" },
      boris: { age: 5, breed: "Maine Coon" },
      mordred: { age: 16, breed: "British Shorthair" },
    };
    cats.boris; // const cats: Record<CatName, CatInfo>

    // --- Pick<Type, Keys>
    // picking the set of properties Keys (string literal or union of string literals) from Type
    interface Todo {
      title: string;
      description: string;
      completed: boolean;
    }
    type TodoPreview = Pick<Todo, "title" | "completed">;
    const todo: TodoPreview = {
      title: "Clean room",
      completed: false,
    };
    todo; // const todo: TodoPreview

    // --- Omit<Type, Keys>
    // picking all properties from Type and then removing Keys (string literal or union of string literals)
    interface Todo {
      title: string;
      description: string;
      completed: boolean;
      createdAt: number;
    }
    type TodoPreview = Omit<Todo, "description">;
    const todo: TodoPreview = {
      title: "Clean room",
      completed: false,
      createdAt: 1615544252770,
    };
    todo; // const todo: TodoPreview
    type TodoInfo = Omit<Todo, "completed" | "createdAt">;
    const todoInfo: TodoInfo = {
      title: "Pick up kids",
      description: "Kindergarten closes at 5pm",
    };
    todoInfo; // const todoInfo: TodoInfo

    // --- Exclude<Type, ExcludedUnion>
    // excluding from Type all union members that are assignable to ExcludedUnion
    type T0 = Exclude<"a" | "b" | "c", "a">; // type T0 = "b" | "c"
    type T1 = Exclude<"a" | "b" | "c", "a" | "b">; // type T1 = "c"
    type T2 = Exclude<string | number | (() => void), Function>; // type T2 = string | number

    // --- Extract<Type, Union>
    // extracting from Type all union members that are assignable to Union
    type T0 = Extract<"a" | "b" | "c", "a" | "f">; // type T0 = "a"
    type T1 = Extract<string | number | (() => void), Function>; // type T1 = () => void

    // --- NonNullable<Type> - excluding null and undefined from Type
    type T0 = NonNullable<string | number | undefined>; // type T0 = string | number
    type T1 = NonNullable<string[] | null | undefined>; // type T1 = string[]

    // --- Parameters<Type>
    // constructs a tuple type from the types used in the parameters of a function type Type
    declare function f1(arg: { a: number; b: string }): void;
    type T0 = Parameters<() => string>; // type T0 = []
    type T1 = Parameters<(s: string) => void>; // type T1 = [s: string]
    type T2 = Parameters<<T>(arg: T) => T>; // type T2 = [arg: unknown]
    type T3 = Parameters<typeof f1>; // type T3 = [arg: { a: number; b: string; }]
    type T4 = Parameters<any>; // type T4 = unknown[]
    type T5 = Parameters<never>; // type T5 = never
    type T6 = Parameters<string>; // type T6 = never // Err: Type 'string' does not satisfy the constraint
    type T7 = Parameters<Function>; // type T7 = never
    // Err: Type 'Function' does not satisfy the constraint
    // Err: Type 'Function' provides no match for the signature '(...args: any): any'

    // --- ConstructorParameters<Type>
    // constructs a tuple or array type from the types of a constructor function type
    // produces a tuple type with all the parameter types (or the type 'never' if Type is not a function)
    type T0 = ConstructorParameters<ErrorConstructor>; // type T0 = [message?: string]
    type T1 = ConstructorParameters<FunctionConstructor>; // type T1 = string[]
    type T2 = ConstructorParameters<RegExpConstructor>; // type T2 = [pattern: string | RegExp, flags?: string]
    type T3 = ConstructorParameters<any>; // type T3 = unknown[]
    type T4 = ConstructorParameters<Function>; //  type T4 = never
    // Err: Type 'Function' does not satisfy the constraint 'abstract new (...args: any) => any'
    // Err: Type 'Function' provides no match for the signature 'new (...args: any): any'
    // works on abstract classes
    abstract class C {
      constructor(a: string, b: number) { }
    }
    type CParams = ConstructorParameters<typeof C>; // type '[a: string, b: number]'

    // --- ReturnType<Type> - type consisting of the return type of function Type
    declare function f1(): { a: number; b: string };
    type T0 = ReturnType<() => string>; // type T0 = string
    type T1 = ReturnType<(s: string) => void>; // type T1 = void
    type T2 = ReturnType<<T>() => T>; // type T2 = unknown
    type T3 = ReturnType<<T extends U, U extends number[]>() => T>; // type T3 = number[]
    type T4 = ReturnType<typeof f1>; // type T4 = { a: number; b: string; }
    type T5 = ReturnType<any>; // type T5 = any
    type T6 = ReturnType<never>; // type T6 = never
    type T7 = ReturnType<string>; // type T7 = any
    // Err: Type 'string' does not satisfy the constraint '(...args: any) => any'
    type T8 = ReturnType<Function>; // type T8 = any
    // Err: Type 'Function' does not satisfy the constraint '(...args: any) => any'
    // Err: Type 'Function' provides no match for the signature '(...args: any): any'

    // --- InstanceType<Type>
    // constructs a type consisting of the instance type of a constructor function in Type
    class C {
      x = 0;
      y = 0;
    }
    type T0 = InstanceType<typeof C>; // type T0 = C
    type T1 = InstanceType<any>; // type T1 = any
    type T2 = InstanceType<never>; // type T2 = never
    type T3 = InstanceType<string>; // type T3 = any
    // Err: Type 'string' does not satisfy the constraint 'abstract new (...args: any) => any'
    type T4 = InstanceType<Function>; // type T4 = any
    // Err: Type 'Function' does not satisfy the constraint 'abstract new (...args: any) => any'
    // Err: Type 'Function' provides no match for the signature 'new (...args: any): any'

    // --- NoInfer<Type> - blocks inferences to the contained type
    // other than blocking inferences, is identical to "Type".
    // not to dig in and match against the inner types to find candidates for type inference.
    function createStreetLight<C extends string>(
      colors: C[],
      defaultColor?: NoInfer<C>,
    ) { ... }
    function doSomethingr<T>(arg: T) { ... }
    //explicitly say that 'T' should be 'string'.
    doSomethingr<string>("hello!");
    // just let the type of 'T' get inferred
    doSomething("hello!");

    createStreetLight(["red", "yellow", "green"], "red");  // OK
    createStreetLight(["red", "yellow", "green"], "blue");  // Error






    // --- ThisParameterType<Type>
    // extracts the type of the 'this' parameter for a function type,
    // or 'unknown' if the function type has no 'this' parameter
    function toHex(this: Number) {
      return this.toString(16);
    }
    function numberToString(n: ThisParameterType<typeof toHex>) {
      return toHex.apply(n);
    }

    // --- OmitThisParameter<Type> - removes the 'this' parameter from Type
    // if Type has no explicitly declared this parameter, the result is simply Type
    // otherwise, a new function type with no this parameter is created from Type
    // generics are erased and only the last overload signature is propagated into the new function type
    function toHex(this: Number) {
      return this.toString(16);
    }
    const fiveToHex: OmitThisParameter<typeof toHex> = toHex.bind(5);

    // --- ThisType<Type>
    // does not return a transformed type, serves as a marker for a contextual 'this' type
    // the --noImplicitThis flag must be enabled to use this utility
    type ObjectDescriptor<D, M> = {
      data?: D;
      methods?: M & ThisType<D & M>; // Type of 'this' in methods is D & M
    };
    function makeObject<D, M>(desc: ObjectDescriptor<D, M>): D & M {
      let data: object = desc.data || {};
      let methods: object = desc.methods || {};
      return { ...data, ...methods } as D & M;
    }
    let obj = makeObject({
      data: { x: 0, y: 0 },
      methods: {
        moveBy(dx: number, dy: number) {
          this.x += dx; // Strongly typed this
          this.y += dy; // Strongly typed this
        },
      },
    });
    obj.x = 10;
    obj.y = 20;
    obj.moveBy(5, 5);
    // 'methods' object in the argument to makeObject has a contextual type that includes ThisType<D & M>
    // therefore the type of 'this' in methods within the 'methods' object is
    // { x: number, y: number } & { moveBy(dx: number, dy: number): number }
    // Notice how the type of the 'methods property' simultaneously is an inference target
    // and a source for the 'this' type in methods.
    // ThisType<T> marker interface is simply an empty interface declared in lib.d.ts
    // Beyond being recognized in the contextual type of an object literal,
    // the interface acts like any empty interface


    // --- Awaited<Type>
    // model operations like await in async functions, or the .then() method on Promises
    // specifically, the way that they recursively unwrap Promise
    // A = string
    type A = Awaited<Promise<string>>;
    // B = number
    type B = Awaited<Promise<Promise<number>>>;
    // C = boolean | number
    type C = Awaited<boolean | Promise<number>>;
    declare function MaybePromise<T>(value: T): T | Promise<T> | PromiseLike<T>;
    async function doSomething(): Promise<[number, number]> {
      const result = await Promise.all([MaybePromise(100), MaybePromise(200)]);
      return result;
    }
  

Polymorphic "this"


    // --- POLYMORPHIC "this" TYPES, subtype of the containing class or interface
    // usage of parent class "this" in sub-class - fluent interface
    class BasicCalculator {
      public constructor(protected value: number = 0) { }
      public currentValue(): number { return this.value; }
      public add(operand: number): this {
        this.value += operand;
        return this;
      }
      // ... other operations go here ...
    }
    let v = new BasicCalculator(2)
      .add(1)
      .currentValue();
    class ScientificCalculator extends BasicCalculator {
      public sin() {
          this.value = Math.sin(this.value);
          return this;
      }
      // ... other operations go here ...
    }
    let v = new ScientificCalculator(2)
      .sin()
      .add(1)
      .currentValue();
  

globalThis


    // variable that refers to the global scope
    // provides a standard way for accessing the global scope
    // can be used across different environments
    var abc = 100; // in a global file
    globalThis.abc = 200; // Refers to 'abc' from above
    // global variables declared with let and const dont show up on globalThis
    let answer = 42;
    globalThis.answer = 333333; // 'answer' does not exist on 'typeof globalThis'
  

Modules/Namespaces

Modules

    // TypeScript supports "export=" to model CommonJS and AMD workflow
    // import module = require("module") must be used to import the module
    // - ZipCodeValidator.ts
    let numberRegexp = /^[0-9]+$/;
    class ZipCodeValidator {
        isAcceptable(s: string) {
            return s.length === 5 && numberRegexp.test(s);
        }
    }
    export = ZipCodeValidator;
    // - Test.ts
    import zip = require("./ZipCodeValidator");
    let strings = ["Hello", "98052", "101"]; // Some samples to try
    let validator = new zip(); // Validators to use
    strings.forEach(s => { // Show whether each string passed each validator
      console.log(`"${ s }" - ${ validator.isAcceptable(s) ? "matches" : "does not match" }`);
    });
    // depending on the module target specified, compiler will generate appropriate code
    // for Node.js:     tsc --module commonjs Test.ts
    // for require.js:  tsc --module amd Test.ts

    // --- TYPE-ONLY IMPORTS AND EXPORTS
    import type { SomeThing } from "./some-module.js";
    export type { SomeThing };
    import { someFunc, type BaseType } from "./some-module.js";
    // - export type *
    // models/vehicles.ts
    export class Spaceship { ... }
    // models/index.ts
    export type * as vehicles from "./vehicles";
    // main.ts
    import { vehicles } from "./models";
    // "vehicles" can only be used in a type position
    function takeASpaceship(s: vehicles.Spaceship) { ... }
    function makeASpaceship() {
      return new vehicles.Spaceship(); // cannot be used as a value !
    }

    // --- DYNAMIC module loading in node.js
    // module loader is invoked (through "require") dynamically
    declare function require(moduleName: string): any;
    import { ZipCodeValidator as Zip } from "./ZipCodeValidator";
    if (needZipValidation) {
      // typeof - produces in this case the type of the module
      let ZipCodeValidator: typeof Zip = require("./ZipCodeValidator");
      let validator = new ZipCodeValidator();
      if (validator.isAcceptable("...")) { /* ... */ }
    }
    // to reference a type from another module, you can instead directly qualify the import
    import { someValue } from "some-module";
    /**
     * @type {import("some-module").SomeType}
     */
    export const myValue = someValue;
    // export a type - use a /** @typedef */ comment in JSDoc
    // @typedef comments already automatically export types from their containing modules
    /**
      * @typedef {string | number} MyType
      */
    /**
      * @typedef {MyType} MyExportedType
      */

    // --- AMBIENT modules (declarations that dont define an implementation) - .d.ts files
    // use "module" keyword and quoted name of the module imported later
    // - node.d.ts (simplified excerpt)
    declare module "url" {
      export interface Url {
        protocol?: string;
        hostname?: string;
        pathname?: string;
      }
      export function parse(urlStr: string, parseQueryString?, slashesDenoteHost?): Url;
    }
    declare module "path" {
      export function normalize(p: string): string;
      export function join(...paths: any[]): string;
      export var sep: string;
    }
    // using:
    /// <reference path="node.d.ts"/>
    import * as URL from "url";
    let myUrl = URL.parse("http://www.typescriptlang.org");
    // short version:
    // declarations.d.ts
    declare module "hot-new-module";
    // using (all imports from a shorthand module will have the "any" type):
    import x, {y} from "hot-new-module";
    x(y);

    // --- WILDCARD module declarations
    // SystemJS and AMD allow non-JavaScript content to be imported
    declare module "*!text" {
      const content: string;
      export default content;
    }
    declare module "json!*" { // some do it the other way around
      const value: any;
      export default value;
    }
    // then:
    import fileContent from "./xyz.txt!text";
    import data from "json!http://example.com/data.json";
    console.log(data, fileContent);

    // --- UMD modules
    // used in many module loaders, or with no module loading (global variables)
    // - math-lib.d.ts
    export function isPrime(x: number): boolean;
    export as namespace mathLib;
    // import
    import { isPrime } from "math-lib";
    isPrime(2);
    mathLib.isPrime(2); // ERROR: can't use the global definition from inside a module
    // as a global variable, only inside of a script(no imports or exports)
    mathLib.isPrime(2);

    // --- Import Assertions
    // make sure that an import has an expected format
    import obj from "./something.json" assert { type: "json" };
    const obj = await import("./something.json", {
      assert: { type: "json" },
    });

    // --- Import Attributes, evolution of an earlier "import assertions" proposal.
    // runtime are now free to use attributes to guide the resolution
    // and interpretation of import paths,
    // import assertions could only assert some characteristics after loading a module
    const obj = await import("./something.json", {
      with: { type: "json" } // with vs. assert
    });
  
Structuring modules

    // 1 - functions, rather then classes or namespaces
    // - if you are only exporting a single class or function, use export default
    // MyClass.ts
    export default class SomeType { constructor() { ... } }
    // MyFunc.ts
    export default function getThing() { return "thing"; }
    // Consumer.ts
    import t from "./MyClass";
    import f from "./MyFunc";
    let x = new t();
    console.log(f());
    // - if you are exporting multiple objects, put them all at top-level
    // MyThings.ts
    export class SomeType { /* ... */ }
    export function someFunc() { /* ... */ }
    // explicitly list imported names
    // Consumer.ts
    import { SomeType, someFunc } from "./MyThings";
    let x = new SomeType();
    let y = someFunc();
    // - use the namespace import pattern if you are importing a large number of things
    // MyLargeModule.ts
    export class Dog { ... }
    export class Cat { ... }
    export class Tree { ... }
    export class Flower { ... }
    // Consumer.ts
    import * as myLargeModule from "./MyLargeModule.ts";
    let x = new myLargeModule.Dog();

    // 2 - re-export to extend
    // dont mutate the original object, export a new entity that provides the new functionality
    // Calculator.ts
    export class Calculator { ... }
    export function test(c: Calculator, input: string) { ... }
    // TestCalculator.ts - running tests apart:
    import { Calculator, test } from "./Calculator";
    let c = new Calculator();
    test(c, "1+2*33/11="); // prints 9
    // ProgrammerCalculator.ts - extending functionality, and re-export:
    import { Calculator } from "./Calculator";
    class ProgrammerCalculator extends Calculator { ... }
    export { ProgrammerCalculator as Calculator };
    export { test } from "./Calculator";

    // 3 - do not use namespaces in module

    // 4 - check that you are not trying to namespace external modules:
    // - file whose only top-level declaration is "export namespace Foo { ... }"
    // remove Foo and move everything 'up' a level
    // - file that has a single "export class" or "export function"
    // consider using "export default"
    // - multiple files that have the same "export namespace Foo {" at top-level
    // dont think that these are going to combine into one Foo!
  
Namespaces

    // instead of putting lots of different names into the global namespace

    // --- one file example

    namespace Validation {
      // variables, unexported implementation details
      const lettersRegexp = /^[A-Za-z]+$/;
      const numberRegexp = /^[0-9]+$/;
      // exported are visble outside
      export interface StringValidator { isAcceptable(s: string): boolean; }
      export class LettersOnlyValidator implements StringValidator {
        isAcceptable(s: string) { return lettersRegexp.test(s); }
      }
      export class ZipCodeValidator implements StringValidator {
        isAcceptable(s: string) {
          return s.length === 5 && numberRegexp.test(s);
        }
      }
    }
    let strings = ["Hello", "98052", "101"]; // samples
    // validators to use
    let validators: { [s: string]: Validation.StringValidator; } = {};
    validators["ZIP code"] = new Validation.ZipCodeValidator();
    validators["Letters only"] = new Validation.LettersOnlyValidator();
    // whether each string passed each validator
    for (let s of strings) {
      for (let name in validators) {
        console.log(`"${ s }" - ${ validators[name].isAcceptable(s) ? "OK" : "NO" } ${ name }`);
      }
    }

    // --- splitting across files

    // - Validation.ts
    namespace Validation {
        export interface StringValidator {
            isAcceptable(s: string): boolean;
        }
    }
    // - LettersOnlyValidator.ts
    /// <reference path="Validation.ts" />
    namespace Validation {
      const lettersRegexp = /^[A-Za-z]+$/;
      export class LettersOnlyValidator implements StringValidator {
        isAcceptable(s: string) {
          return lettersRegexp.test(s);
        }
      }
    }
    // - ZipCodeValidator.ts
    /// <reference path="Validation.ts" />
    namespace Validation {
      const numberRegexp = /^[0-9]+$/;
      export class ZipCodeValidator implements StringValidator {
        isAcceptable(s: string) {
          return s.length === 5 && numberRegexp.test(s);
        }
      }
    }
    // - Test.ts
    /// <reference path="Validation.ts" />
    /// <reference path="LettersOnlyValidator.ts" />
    /// <reference path="ZipCodeValidator.ts" />
    let strings = ["Hello", "98052", "101"]; // samples to try
    // validators ...

    // concatenated output using the --outFile flag - compile into a single file
    tsc --outFile sample.js Test.ts
    tsc --outFile sample.js Validation.ts LettersOnlyValidator.ts ZipCodeValidator.ts Test.ts

    // per-file compilation (the default) to emit one JavaScript file for each input file
    <script src="Validation.js" type="text/javascript" />
    <script src="LettersOnlyValidator.js" type="text/javascript" />
    <script src="ZipCodeValidator.js" type="text/javascript" />
    <script src="Test.js" type="text/javascript" />

    // aliases
    namespace Shapes {
      export namespace Polygons {
        export class Triangle { }
        export class Square { }
      }
    }
    import polygons = Shapes.Polygons; // import q = x.y.z
    let sq = new polygons.Square(); // Same as 'new Shapes.Polygons.Square()'
    // not to be confused with the import x = require("name") syntax used to load modules !

    // ambient namespace (declarations that dont define an implementation) - .d.ts files

    // D3 library defines its functionality in a global object called d3
    // loaded through a script-tag (instead of a module loader)
    // its declaration uses namespaces to define its shape
    // we use an ambient namespace declaration TypeScript compiler to see this shape:
    declare namespace D3 {
      export interface Selectors {
        select: {
          (selector: string): Selection;
          (element: EventTarget): Selection;
        };
      }
      export interface Event { x: number; y: number; }
      export interface Base extends Selectors { event: Event; }
    }
    declare var d3: D3.Base;
  
Pitfalls

    // compiler looks for .ts, .tsx, and then a .d.ts with the appropriate path
    // if a specific file not found - compiler looks for ambient module declaration
    // declared in a .d.ts file
    // - myModules.d.ts (.d.ts file or .ts file that is not a module)
    declare module "SomeModule" { export function fn(): string; }
    // - myOtherModule.ts
    /// <reference path="myModules.d.ts" />
    import * as m from "SomeModule";

    // dont add extra layers !
    // export namespace Shapes {
    //   export class Triangle { /* ... */ }
    //   export class Square { /* ... */ }
    // }
    // VS.
    export class Triangle { /* ... */ }
    export class Square { /* ... */ }
    // using:
    import * as shapes from "./shapes";
    let t = new shapes.Triangle();
  

Module Resolution


    // lookup for moduleA is in .ts/.tsx files, or in .d.ts that code depends on
    import { a } from "moduleA"

    // RELATIVE module imports - starts with /, ./ or ../
    // resolved relative to the importing file
    // cannot resolve to an ambient module declaration
    // use for own modules that are guaranteed on relative location at runtime
    import Entry from "./components/Entry";
    import { DefaultHeaders } from "../constants/http";
    import "/mod";

    // NON-RELATIVE module imports
    // can be resolved relative to baseUrl, or through path mapping
    // can also resolve to ambient module declarations
    // use when importing any of external dependencies
    import * as $ from "jquery";
    import { Component } from "@angular/core";
  
CLASSIC resolution strategy

    // for relative import:
    import { b } from "./moduleB"
    // in source file
    /root/src/folder/A.ts
    // would result in the following lookups:
    /root/src/folder/moduleB.ts
    /root/src/folder/moduleB.d.ts

    // for non-relative module imports
    // compiler walks up the directory tree
    // starting with the directory containing the importing file
    // trying to locate a matching definition file:
    import { b } from "moduleB"
    // in a source file
    /root/src/folder/A.ts
    // would result in attempting the following locations for locating "moduleB":
    /root/src/folder/moduleB.ts
    /root/src/folder/moduleB.d.ts
    /root/src/moduleB.ts
    /root/src/moduleB.d.ts
    /root/moduleB.ts
    /root/moduleB.d.ts
    /moduleB.ts
    /moduleB.d.ts
  
NODE resolution strategy

    // how NODEJS solves:
    // relative path:
    // file located at
    /root/src/moduleA.js
    // contains
    import var x = require("./moduleB");
    // Node.js resolves that import in the following order:
    // 1 - ask the file named /root/src/moduleB.js, if it exists
    // 2 - ask the folder /root/src/moduleB if it contains a file named package.json
    // that specifies a "main" module
    // if /root/src/moduleB/package.json containing { "main": "lib/mainModule.js" }
    // then Node.js will refer to /root/src/moduleB/lib/mainModule.js.
    // 3 - ask the folder /root/src/moduleB if it contains a file named index.js
    // implicitly considered that folder "main" module
    // non-relative path:
    // node_modules folder on the same level or higher up in the directory chain
    /root/src/node_modules/moduleB.js
    /root/src/node_modules/moduleB/package.json (if it specifies a "main" property)
    /root/src/node_modules/moduleB/index.js
    /root/node_modules/moduleB.js
    /root/node_modules/moduleB/package.json (if it specifies a "main" property)
    /root/node_modules/moduleB/index.js
    /node_modules/moduleB.js
    /node_modules/moduleB/package.json (if it specifies a "main" property)
    /node_modules/moduleB/index.js

    // how TS solves:
    // overlays the TypeScript source file extensions (.ts, .tsx, and .d.ts)
    // over the Node resolution logic
    // will also use a field in package.json named "types" to mirror the purpose of "main"
    // the compiler will use it to find the “main” definition file to consult
    import { b } from "./moduleB"
    // in
    /root/src/moduleA.ts
    // would result in attempting the following locations for locating "./moduleB"
    /root/src/moduleB.ts
    /root/src/moduleB.tsx
    /root/src/moduleB.d.ts
    /root/src/moduleB/package.json (if it specifies a "types" property)
    /root/src/moduleB/index.ts
    /root/src/moduleB/index.tsx
    /root/src/moduleB/index.d.ts
    // non-relative import:
    import { b } from "moduleB"
    // in source file
    /root/src/moduleA.ts
    // would result in the following lookups:
    /root/src/node_modules/moduleB.ts
    /root/src/node_modules/moduleB.tsx
    /root/src/node_modules/moduleB.d.ts
    /root/src/node_modules/moduleB/package.json (if it specifies a "types" property)
    /root/src/node_modules/@types/moduleB.d.ts
    /root/src/node_modules/moduleB/index.ts
    /root/src/node_modules/moduleB/index.tsx
    /root/src/node_modules/moduleB/index.d.ts

    /root/node_modules/moduleB.ts
    /root/node_modules/moduleB.tsx
    /root/node_modules/moduleB.d.ts
    /root/node_modules/moduleB/package.json (if it specifies a "types" property)
    /root/node_modules/@types/moduleB.d.ts
    /root/node_modules/moduleB/index.ts
    /root/node_modules/moduleB/index.tsx
    /root/node_modules/moduleB/index.d.ts

    /node_modules/moduleB.ts
    /node_modules/moduleB.tsx
    /node_modules/moduleB.d.ts
    /node_modules/moduleB/package.json (if it specifies a "types" property)
    /node_modules/@types/moduleB.d.ts
    /node_modules/moduleB/index.ts
    /node_modules/moduleB/index.tsx
    /node_modules/moduleB/index.d.ts
  

additional module resolution flags


    // --- baseUrl - informs the compiler where to find modules
    // taken from value of baseUrl command line argument OR property in "tsconfig.json"

    // --- paths - path mapping, property in tsconfig.json
    { "compilerOptions": {
      "baseUrl": ".", // This must be specified if "paths" is.
      "paths": {
        "jquery": ["node_modules/jquery/dist/jquery"] // mapping is relative to "baseUrl"
    }}}
    // allows more sophisticated mappings like multiple fall back locations
    { "compilerOptions": {
      "baseUrl": ".",
      "paths": {
        "*": [
          // same name unchanged, so map moduleName => baseUrl/moduleName
          "*",
          // module name with an appended prefix "generated" baseUrl/generated/moduleName
          "generated/*"
    ]}}}
    // works for this project structure
    projectRoot
    ├── folder1
    │   ├── file1.ts (imports 'folder1/file2' and 'folder2/file3')
    │   └── file2.ts
    ├── generated
    │   ├── folder1
    │   └── folder2
    │       └── file3.ts
    └── tsconfig.json

    // --- rootDirs - virtual directories
    // roots whose contents are expected to merge at run-time like from one directory
    { "compilerOptions": {
      "rootDirs": [
        "src/views",
        "generated/templates/views"
    ]}}
    // for:
    src
    └── views
        └── view1.ts (imports './template1')
        └── view2.ts
    generated
    └── templates
        └── views
            └── template1.ts (imports './view2')
  
module resolution settings
classic node node16 bundler
node_modules packages
extensionless CJS only
directory index CJS only
*.ts imports
package.json exports
exports conditions always node, types;
import from ESM,
require from CJS;
custom additions
always types, import;
custom additions

Declaration Merging


Declaration Type Namespace Type Value
Namespace X   X
Class   X X
Enum   X X
Interface   X  
Type Alias   X  
Function     X
Variable     X

    // --- MERGING INTERFACES
    // joins the members of both declarations into a single interface with the same name
    // non-function members of the interfaces should be unique OR of the same type
    interface Box { height: number; width: number; }
    interface Box { scale: number; }
    let box: Box = {height: 5, width: 6, scale: 10};
    // function member of the same name is treated as describing an overload
    interface Cloner { clone(animal: Animal): Animal; }
    interface Cloner { clone(animal: Sheep): Sheep; }
    interface Cloner { clone(animal: Dog): Dog; clone(animal: Cat): Cat; }
    // ->
    interface Cloner {
      clone(animal: Dog): Dog; // later overload sets ordered first
      clone(animal: Cat): Cat;
      clone(animal: Sheep): Sheep;
      clone(animal: Animal): Animal;
    }
    // single string literal type (e.g. not a union of string literals)
    // will be bubbled toward the top of its merged overload list
    interface Document {
      createElement(tagName: "canvas"): HTMLCanvasElement;
      createElement(tagName: "div"): HTMLDivElement;
      createElement(tagName: "span"): HTMLSpanElement;
      createElement(tagName: string): HTMLElement;
      createElement(tagName: any): Element;
    }

    // --- MERGING NAMESPACES
    // merged forming a single namespace with merged interface definitions inside
    // non-exported members are only visible in the original (un-merged) namespace

    // --- MERGING NAMESPACES WITH CLASSES, FUNCTIONS, AND ENUMS
    // creates class inside other class, or function inside function in JS
    class Album { label: Album.AlbumLabel; }
    namespace Album { export class AlbumLabel { } }
    // extend function with properties
    function buildLabel(name: string): string {
      return buildLabel.prefix + name + buildLabel.suffix;
    }
    namespace buildLabel {
      export let suffix = "";
      export let prefix = "Hello, ";
    }
    console.log(buildLabel("Sam Smith"));
    // extend enums with static members
    enum C { red = 1, green = 2, blue = 4 }
    namespace C {
      export function mixColor(colorName: string) {
        if (colorName == "yellow") { return C.red + C.green; }
        else if (colorName == "white") { return C.red + C.green + C.blue; }
        else if (colorName == "magenta") { return C.red + C.blue; }
        else if (colorName == "cyan") { return C.green + C.blue; }
    }}
    // classes can not merge with other classes or with variables, only in ambient context
    export declare function Point2D(x: number, y: number): Point2D;
    export declare class Point2D {
      x: number;
      y: number;
      constructor(x: number, y: number);
    }

    // --- AUGUMENT MODULE - patch to existing declarations
    // tell compiler about Observable.prototype.map
    // observable.js
    export class Observable<T> { /*...*/ }
    // map.ts
    import { Observable } from "./observable";
    declare module "./observable" {
      interface Observable<T> {
        map<U>(f: (x: T) => U): Observable<U>;
    }}
    Observable.prototype.map = function (f) { /*...*/ }
    // consumer.ts
    import { Observable } from "./observable";
    import "./map";
    let o: Observable<number>;
    o.map(x => x.toFixed());

    // --- GLOBAL AUGMENTATION
    // observable.ts
    export class Observable<T> { /*...*/ }
    declare global {
      interface Array<T> {
        toObservable(): Observable<T>;
    }}
    Array.prototype.toObservable = function () { /*...*/ }
  

JSX


Mode Input Output Output File Extension
preserve <div /> <div /> .jsx
react <div /> React.createElement("div") .js
react-native <div /> <div /> .js

    // --- as (operator)
    var foo = <foo>bar;
    // ->
    var foo = bar as foo; // available in both .ts and .tsx files

    // --- TYPE CHECKING

    // --- INTRINSIC ELEMENT (environmental: DOM element,...) - begins with a lowercase letter
    // looked up on the special interface JSX.IntrinsicElements,
    // if interface is not specified, then anything goes and elements will not be type checked
    // if interface is present, then the name of the intrinsic element is looked up
    // as a property on the JSX.IntrinsicElements interface
    declare namespace JSX {
      interface IntrinsicElements {
        foo: any
      }
    }
    <foo />; // ok
    <bar />; // error
    // catch-all string indexer on JSX.IntrinsicElements
    declare namespace JSX {
      interface IntrinsicElements {
        [elemName: string]: any;
      }
    }
    // namespaced tag names
    namespace JSX { // in some library code or in an augmentation of that library
        interface IntrinsicElements {
            ["a:b"]: { prop: string };
        }
    } // ...
    let x = <a:b prop="hello!" />;

    // --- VALUE-BASED ELEMENT (component) - begins with an uppercase letter
    // looked up by identifiers that are in scope
    import MyComponent from "./myComponent";
    <MyComponent />; // ok
    <SomeOtherComponent />; // error
    // ways to define a value-based element:

    // 1 - stateless functional component (SFC)
    // component is defined as JavaScript function where its first argument is a props object
    // TS enforces that its return type must be assignable to JSX.Element
    interface FooProp { name: string; X: number; Y: number; }
    declare function AnotherComponent(prop: {name: string});
    function ComponentFoo(prop: FooProp) { return <AnotherComponent name={prop.name} />; }
    const Button = (prop: {value: string}, context: { color: string }) => <button>
    // function overloads may be used here as well:
    interface ClickableProps { children: JSX.Element[] | JSX.Element }
    interface HomeProps extends ClickableProps { home: JSX.Element; }
    interface SideProps extends ClickableProps { side: JSX.Element | string; }
    function MainButton(prop: HomeProps): JSX.Element;
    function MainButton(prop: SideProps): JSX.Element { /*...*/ }

    // 2 - class component
    // element class type
    // element instance type (must be assignable to JSX.ElementClass, {} - is its default)
    class MyComponent { render() {} }
    var myComponent = new MyComponent(); // use a construct signature
    // element class type => MyComponent
    // element instance type => { render: () => void }
    function MyFactoryFunction() { return { render: () => { } } }
    var myComponent = MyFactoryFunction(); // use a call signature
    // element class type => FactoryFunction
    // element instance type => { render: () => void }

    // augmenting JSX.ElementClass
    // to limit the use of JSX to types that conform to the proper interface
    declare namespace JSX { interface ElementClass { render: any; } }
    <MyComponent />; // ok
    <MyFactoryFunction />; // ok
    class NotAValidComponent {}
    function NotAValidFactoryFunction() { return {}; }
    <NotAValidComponent />; // error
    <NotAValidFactoryFunction />; // error

    // --- ATTRIBUTE TYPE CHECKING
    // first step is to determine the element attributes type

    // for intrinsic elements - type of the property on JSX.IntrinsicElements
    declare namespace JSX {
      interface IntrinsicElements {
        foo: { bar?: boolean }
      }
    }
    <foo bar />; // element attributes type for 'foo' is '{bar?: boolean}'
    // namespaced attribute names
    const x = <Foo a:b="hello" />; // equivalents
    const y = <Foo a : b="hello" />;
    interface FooProps {
      "a:b": string;
    }
    function Foo(props: FooProps) {
      return <div>{props["a:b"]}</div>;
    }

    // for value-based elements
    // determined by the type of a property on the element instance type
    // that was previously determined.
    // which property to use is determined by JSX.ElementAttributesProperty
    // it should be declared with a single property
    // the name of that property is then used.
    // if JSX.ElementAttributesProperty is not provided
    // the type of first parameter of the class element constructor
    // or SFC call will be used instead
    declare namespace JSX {
      interface ElementAttributesProperty {
        props; // specify the property name to use
      }
    }
    class MyComponent {
      // specify the property on the element instance type
      props: { foo?: string; }
    }
    // element attributes type for 'MyComponent' is '{foo?: string}'
    <MyComponent foo="bar" />
    // element attribute type is used to type check the attributes in the JSX
    declare namespace JSX {
      interface IntrinsicElements {
        foo: { requiredProp: string; optionalProp?: number }
      }
    }
    <foo requiredProp="bar" />; // ok
    <foo requiredProp="bar" optionalProp={0} />; // ok
    <foo />; // error, requiredProp is missing
    <foo requiredProp={0} />; // error, requiredProp should be a string
    <foo requiredProp="bar" unknownProp />; // error, unknownProp does not exist
    // ok, because 'some-unknown-prop' is not a valid identifier
    <foo requiredProp="bar" some-unknown-prop />;
    // spread operator also works:
    var props = { requiredProp: "bar" };
    <foo {...props} />; // ok
    var badProps = {};
    <foo {...badProps} />; // error
    // if an attribute name is not a valid JS identifier (like a data-* attribute)
    // it is not considered to be an error if it is not found in the element attributes type

    // --- CHILDREN TYPE CHECKING
    // children - special property in an element attributes type,
    // where child JSXExpressions are taken to be inserted into the attributes.
    // JSX.ElementChildrenAttribute (declared with a single property) -
    // determines the name of children within those props
    declare namespace JSX {
      interface ElementChildrenAttribute {
        children: {};  // specify children name to use
      }
    }
    // --->
    // <div>
    //   <h1>Hello</h1>
    // </div>;
    // <div>
    //   <h1>Hello</h1>
    //   World
    // </div>;
    // const CustomComp = (props) => <div>props.children</div>
    // <CustomComp>
    //   <div>Hello World</div>
    //   {"This is just a JS expression..." + 1000}
    // </CustomComp>

    // specify the type of children like any other attribute
    // will override the default type from, eg the "React typings" if you use them
    interface PropsType { children: JSX.Element, name: string }
    class Component extends React.Component<PropsType, {}> {
      render() {
        return (
          <h2> {this.props.children} </h2>
    )}}
    // --->
    // // OK
    // <Component>
    //   <h1>Hello World</h1>
    // </Component>
    // // Error: children is of type JSX.Element not array of JSX.Element
    // <Component>
    //   <h1>Hello World</h1>
    //   <h2>Hello World</h2>
    // </Component>
    // // Error: children is of type JSX.Element not array of JSX.Element or string.
    // <Component>
    //   <h1>Hello</h1>
    //   World
    // </Component>

    // --- EMBEDDING EXPRESSIONS
    // surrounding the expressions with curly braces - {}
    // tsc -- jsx preserve file.tsx
    var a = <div>
      {[5, 9, 15].map(i => <span>{i / 2}</span>)}
    </div>

    // --- REACT INTEGRATION, use with the "React typings"
    // these typings define the JSX namespace appropriately for use with React
  

@decorators

experimental stage 2 decorators

    // --- ENABLING , Command Line:
    tsc --target ES5 --experimentalDecorators
    // OR, tsconfig.json:
    {
      "compilerOptions": {
        "target": "ES5",
        "experimentalDecorators": true
      }
    }

    // --- DECORATOR FACTORIES
    // function that returns expression called by the decorator at runtime
    function color(value: string) { // this is the decorator factory
      return function (target) { // this is the decorator
        // do something with 'target' and 'value'...
      }
    }

    // --- DECORATOR COMPOSITION
    // multiple decorators can be applied to a declaration, f(g(x)):
    @f @g x // on a single line, or multiple lines:
    @f
    @g
    x
    // expressions for each decorator are evaluated top-to-bottom
    // results are then called as functions from bottom-to-top
    function f() {
      console.log("f(): evaluated");
      return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
        console.log("f(): called");
      }
    }
    function g() {
      console.log("g(): evaluated");
      return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
        console.log("g(): called");
      }
    }
    class C {
      @f()
      @g()
      method() {}
    }
    // --- >
    f(): evaluated
    g(): evaluated
    g(): called
    f(): called

    // --- CLASS DECORATORS
    // declared just before a class declaration, will be called as a function at runtime
    // applied to the constructor of the class
    // can be used to observe, modify, or replace a class definition
    // cannot be used in a declaration file,
    // or in any other ambient context (such as on a declare class).
    // if the class decorator returns a value,
    // it will replace the class declaration with the provided constructor function.
    // you should return a new constructor function,
    // maintain the original prototype,
    // logic that applies decorators at runtime will not do this for you.
    // class decorator (@sealed) applied to the Greeter:
    @sealed
    class Greeter {
      greeting: string;
      constructor(message: string) { this.greeting = message; }
      greet() { return "Hello, " + this.greeting; }
    }
    function sealed(constructor: Function) { // define the @sealed decorator
      Object.seal(constructor);
      Object.seal(constructor.prototype);
    }
    // how to override the constructor:
    function classDecorator<T extends {new(...args:any[]):{}}>(constructor:T) {
      return class extends constructor {
        newProperty = "new property";
        hello = "override";
      }
    }
    @classDecorator
    class Greeter {
      property = "property";
      hello: string;
      constructor(m: string) { this.hello = m; }
    }
    console.log(new Greeter("world"));

    // --- METHOD DECORATORS
    // declared just before a method declaration, will be called as a function at runtime,
    // with following argument:
    // 1 - either the constructor function of the class for a static member,
    // or the prototype of the class for an instance member
    // 2 - the name of the member
    // 3 - the Property Descriptor for the member (undefined, when less than ES5)
    // applied to the Property Descriptor for the method
    // can be used to observe, modify, or replace a method definition
    // cannot be used in a declaration file, on an overload,
    // or in any other ambient context (such as in a declare class).
    // if the method decorator returns a value,
    // it will be used as the Property Descriptor for the method (ignored, when less than ES5).
    // method decorator (@enumerable) applied to a method on the Greeter class:
    class Greeter {
      greeting: string;
      constructor(message: string) { this.greeting = message; }

      // here, is a decorator factory
      // when called modifies the enumerable property of the property descriptor
      @enumerable(false)

      greet() { return "Hello, " + this.greeting; }
    }
    function enumerable(value: boolean) { // define the @enumerable decorator
      return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        descriptor.enumerable = value;
      };
    }

    // --- ACCESSOR DECORATORS
    // declared just before an accessor declaration, called as a function at runtime
    // with the following three arguments:
    // 1 - either the constructor function of the class for a static member,
    // or the prototype of the class for an instance member
    // 2 - the name of the member
    // 3 - the Property Descriptor for the member (undefined if less than ES5)
    // applied to the Property Descriptor for the accessor
    // can be used to observe, modify, or replace an accessor definitions
    // cannot be used in a declaration file
    // or in any other ambient context (such as in a declare class).
    // if the method decorator returns a value,
    // it will be used as the Property Descriptor for the method (ignored, when less than ES5).
    // accessor decorator (@configurable) applied to a member of the Point class:
    class Point {
      private _x: number;
      private _y: number;
      constructor(x: number, y: number) { this._x = x; this._y = y; }
      @configurable(false)
      get x() { return this._x; }
      @configurable(false)
      get y() { return this._y; }
    }
    function configurable(value: boolean) { // define the @configurable decorator
      return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        descriptor.configurable = value;
      };
    }
    // TS disallows decorating both the get and set accessor for a single member
    // instead, all decorators for the member must be applied
    // to the first accessor specified in document order.
    // because decorators apply to a Property Descriptor,
    // which combines both the get and set accessor, not each declaration separately

    // --- PROPERTY DECORATORS
    // declared just before a property declaration, called as a function at runtime
    // with the following two arguments:
    // 1 - either the constructor function of the class for a static member,
    // or the prototype of the class for an instance member.
    // 2 - the name of the member
    // can ONLY be used to observe that a property of a specific name has been declared for a class
    // cannot be used in a declaration file,
    // or in any other ambient context (such as in a declare class)
    // record metadata about the property (requires the reflect-metadata library):
    class Greeter {

      // here, is a decorator factory
      // adds a metadata entry for the property using the Reflect.metadata function
      // from the reflect-metadata library
      @format("Hello, %s")

      greeting: string;
      constructor(message: string) { this.greeting = message; }
      greet() {
        // read the metadata value for the format
        let formatString = getFormat(this, "greeting");
        return formatString.replace("%s", this.greeting);
      }
    }
    // define the @format decorator and getFormat functions
    import "reflect-metadata";
    const formatMetadataKey = Symbol("format");
    function format(formatString: string) {
      return Reflect.metadata(formatMetadataKey, formatString);
    }
    function getFormat(target: any, propertyKey: string) {
      return Reflect.getMetadata(formatMetadataKey, target, propertyKey);
    }

    // --- PARAMETER DECORATORS
    // declared just before a parameter declaration
    // applied to the function for a class constructor or method declaration
    // called as a function at runtime, with the following three arguments:
    // 1 - either the constructor function of the class for a static member,
    // or the prototype of the class for an instance member
    // 2 - the name of the member
    // 3 - the ordinal index of the parameter in the function parameter list
    // can only be used to observe that a parameter has been declared on a method
    // return value of the parameter decorator is ignored
    // cannot be used in a declaration file, an overload,
    // or in any other ambient context (such as in a declare class).
    // parameter decorator (@required) applied to parameter of a member of the Greeter class
    // (requires the reflect-metadata library):
    class Greeter {
      greeting: string;
      constructor(message: string) { this.greeting = message; }
      // wrap existing greet method in a function
      // that validates the arguments before invoking the original method
      @validate
      // add a metadata entry that marks the parameter as required
      greet(@required name: string) { return "Hello " + name + ", " + this.greeting; }
    }
    // define the @required and @validate decorators
    import "reflect-metadata";
    const requiredMetadataKey = Symbol("required");
    function required(
      target: Object,
      propertyKey: string | symbol,
      parameterIndex: number
    ) {
      let existingRequiredParameters: number[] =
        Reflect.getOwnMetadata(requiredMetadataKey, target, propertyKey) || [];
      existingRequiredParameters.push(parameterIndex);
      Reflect.defineMetadata(
        requiredMetadataKey,
        existingRequiredParameters,
        target,
        propertyKey
      );
    }
    function validate(
      target: any,
      propertyName: string,
      descriptor: TypedPropertyDescriptor<Function>
    ) {
      let method = descriptor.value;
      descriptor.value = function () {
        let requiredParameters: number[] =
          Reflect.getOwnMetadata(requiredMetadataKey, target, propertyName);
        if (requiredParameters) {
          for (let parameterIndex of requiredParameters) {
            if (
              parameterIndex >= arguments.length ||
              arguments[parameterIndex] === undefined
            ) {
              throw new Error("Missing required argument.");
            }
          }
        }
        return method.apply(this, arguments);
      }
    }

    // --- METADATA
    // polyfill for an experimental metadata API: npm i reflect-metadata --save
    // enabling:
    // Command Line:
    tsc --target ES5 --experimentalDecorators --emitDecoratorMetadata
    // tsconfig.json:
    {
      "compilerOptions": {
        "target": "ES5",
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true
      }
    }
    // additional design-time type information will be exposed at runtime.
    import "reflect-metadata";
    class Point { x: number; y: number; }
    class Line {
        private _p0: Point;
        private _p1: Point;

        @validate
        set p0(value: Point) { this._p0 = value; }
        get p0() { return this._p0; }

        @validate
        set p1(value: Point) { this._p1 = value; }
        get p1() { return this._p1; }
    }
    function validate<T>(
      target: any,
      propertyKey: string,
      descriptor: TypedPropertyDescriptor<T>
    ) {
      let set = descriptor.set;
      descriptor.set = function (value: T) {
        let type = Reflect.getMetadata("design:type", target, propertyKey);
        if (!(value instanceof type)) { throw new TypeError("Invalid type.");
        set(value);
      }
    }
    // TS compiler will inject design-time type information
    // using the @Reflect.metadata decorator
    // you could consider it the equivalent of the following TypeScript:
    class Line {
      private _p0: Point;
      private _p1: Point;

      @validate
      @Reflect.metadata("design:type", Point)
      set p0(value: Point) { this._p0 = value; }
      get p0() { return this._p0; }

      @validate
      @Reflect.metadata("design:type", Point)
      set p1(value: Point) { this._p1 = value; }
      get p1() { return this._p1; }
    }
  
stage 3 decorators (as in TC39 proposal)
abilities

    // --- replacing the decorated entity
    // @replaceMethod replaces method .hello() with a function that it returns
    function replaceMethod() {
      return function () {
        return `How are you, ${this.name}?`;
      }
    }
    class Person {
      constructor(name) {
        this.name = name;
      }
      @replaceMethod
      hello() {
        return `Hi ${this.name}!`;
      }
    }
    const robin = new Person('Robin');
    assert.equal(
      robin.hello(), 'How are you, Robin?'
    );

    // --- exposing access to the decorated entity to others
    // @exposeAccess stores an object in the variable acc that give access to property .green of the instances of Color
    let acc;
    function exposeAccess(_value, {access}) {
      acc = access;
    }
    class Color {
      @exposeAccess
      name = 'green'
    }
    const green = new Color();
    assert.equal(
      green.name, 'green'
    );
    // Using `acc` to get and set `green.name`
    assert.equal(
      acc.get.call(green), 'green'
    );
    acc.set.call(green, 'red');
    assert.equal(
      green.name, 'red'
    );

    // --- processing the decorated entity and its container
    function collect(_value, {name, addInitializer}) {
      addInitializer(function () {
        if (!this.collectedMethodKeys) {
          this.collectedMethodKeys = new Set();
        }
        this.collectedMethodKeys.add(name);
      });
    }
    class C {
      @collect
      toString() {}
      @collect
      [Symbol.iterator]() {}
    }
    const inst = new C();
    assert.deepEqual(
      inst.collectedMethodKeys,
      new Set(['toString', Symbol.iterator])
    );

    // --- placed before export and after export or export default
    @register export default class Foo { ... }
    export default @register class Bar { ... }
    // not allowed, mixing the two styles
    @before export @after class Bar { ... }
  
Class method

    // signature:
    type ClassMethodDecorator = (
      value: Function,
      context: {
        kind: 'method';
        name: string | symbol;
        static: boolean;
        private: boolean;
        access: { get: () => unknown };
        addInitializer(initializer: () => void): void;
      }
    ) => Function | void;
    // abilities:
    // change the decorated method by changing "value"
    // replace the decorated method by returning a function
    // register initializers
    // context.access only supports getting the value of its property, not setting it

    // --- @trace wraps methods, invocations and results are logged to the console:
    function trace(value, {kind, name}) {
      if (kind === 'method') {
        return function (...args) {
          console.log(`CALL ${name}: ${JSON.stringify(args)}`);
          const result = value.apply(this, args);
          console.log('=> ' + JSON.stringify(result));
          return result;
        };
      }
    }
    class StringBuilder {
      #str = '';
      @trace
      add(str) {
        this.#str += str;
      }
      @trace
      toString() {
        return this.#str;
      }
    }
    const sb = new StringBuilder();
    sb.add('Home');
    sb.add('page');
    assert.equal(
      sb.toString(), 'Homepage'
    );
    // Output:
    // CALL add: ["Home"]
    // => undefined
    // CALL add: ["page"]
    // => undefined
    // CALL toString: []
    // => "Homepage"

    // --- @bind - function-call methods with fixed "this":
    function bind(value, {kind, name, addInitializer}) {
      if (kind === 'method') {
        // initializer registration
        addInitializer(function () {
          this[name] = value.bind(this);
        });
      }
    }
    class Color2 {
      #name;
      constructor(name) {
        this.#name = name;
      }
      @bind
      toString() {
        return `Color(${this.#name})`;
      }
    }
    const green2 = new Color2('green');
    const toString2 = green2.toString;
    assert.equal(
      toString2(), 'Color(green)'
    );
    // The own property green2.toString is different
    // from Color2.prototype.toString
    assert.ok(Object.hasOwn(green2, 'toString'));
    assert.notEqual(
      green2.toString,
      Color2.prototype.toString
    );

    // --- multiple
    function bound(originalMethod: any, context: ClassMethodDecoratorContext) {
      const methodName = context.name;
      if (context.private) {
        throw new Error(`'bound' cannot decorate private properties like ${methodName as string}.`);
      }
      context.addInitializer(function () {
        this[methodName] = this[methodName].bind(this);
      });
    }
    // function loggedMethod(headMessage = "LOG:") {
    //   return function actualDecorator(originalMethod: any, context: ClassMethodDecoratorContext) {
    //     const methodName = String(context.name);
    //     function replacementMethod(this: any, ...args: any[]) {
    //       console.log(`${headMessage} Entering method '${methodName}'.`)
    //       const result = originalMethod.call(this, ...args);
    //       console.log(`${headMessage} Exiting method '${methodName}'.`)
    //       return result;
    //     }
    //     return replacementMethod;
    //   }
    // }
    function loggedMethod(headMessage: string = "LOG:") {
      return function loggedMethod<This, Args extends any[], Return>(
        target: (this: This, ...args: Args) => Return,
        context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Return>
      ) {
        const methodName = String(context.name);
        function replacementMethod(this: This, ...args: Args): Return {
          console.log(`${headMessage} Entering method '${methodName}'.`)
          const result = target.call(this, ...args);
          console.log(`${headMessage} Exiting method '${methodName}'.`)
          return result;
        }
        return replacementMethod;
      }
    }
    class User {
      name: string;
      constructor(name: string) {
        this.name = name;
      }
      // decorations run in "reverse order"
      @bound // decorate the result of @loggedMethod, add logic before any other fields are initialized
      @loggedMethod // decorate the original method greet
      // @bound @loggedMethod(">>>") greet() { ...
      greet() {
        console.log(`Hi, my name is ${this.name}.`);
      }
    }
    const p = new User('Andrei');
    const greet = p.greet;
    greet();
    // Output:
    // >>> Entering method 'greet'.
    // Hi, my name is Andrei.
    // >>> Exiting method 'greet'.
  
Class Accessors

    // signature:
    type ClassGetterDecorator = (
      value: Function,
      context: {
        kind: "getter";
        name: string | symbol;
        access: { get(): unknown };
        static: boolean;
        private: boolean;
        addInitializer(initializer: () => void): void;
      }
    ) => Function | void;
    type ClassSetterDecorator = (
      value: Function,
      context: {
        kind: "setter";
        name: string | symbol;
        access: { set(value: unknown): void };
        static: boolean;
        private: boolean;
        addInitializer(initializer: () => void): void;
      }
    ) => Function | void;
    // abilities are similar to method decorators

    function logged(value, { kind, name }) {
      if (kind === "method" || kind === "getter" || kind === "setter") {
        return function (...args) {
          console.log(`starting ${name} with arguments ${args.join(", ")}`);
          const ret = value.call(this, ...args);
          console.log(`ending ${name}`);
          return ret;
        };
      }
    }
    class C {
      @logged
      set x(arg) {}
    }
    new C().x = 1
    // starting x with arguments 1
    // ending x

    // --- computing values lazily
    class C {
      @lazy
      get value() {
        console.log('COMPUTING');
        return 'Result of computation';
      }
    }
    // @lazy wraps the original getter
    // when invoked for the first time, it invokes the getter
    // and creates an own data property whose value is the result.
    // own property overrides the inherited getter whenever someone reads the property.
    function lazy(value, {kind, name, addInitializer}) {
      // implement the property via a getter
      // code that computes its value, is only executed if the property is read
      if (kind === 'getter') {
        return function () {
          const result = value.call(this);
          // property .[name] is immutable (because there is only a getter)
          // we have to define the property and cant use assignment
          Object.defineProperty(
            this, name,
            {
              value: result,
              writable: false,
            }
          );
          return result;
        };
      }
    }
    console.log('1 new C()');
    const inst = new C();
    console.log('2 inst.value');
    assert.equal(inst.value, 'Result of computation');
    console.log('3 inst.value');
    assert.equal(inst.value, 'Result of computation');
    console.log('4 end');
    // Output:
    // 1 new C()
    // 2 inst.value
    // COMPUTING
    // 3 inst.value
    // 4 end
  
Class Fields

    // signature:
    type ClassFieldDecorator = (
      value: undefined,
        context: {
        kind: "field";
        name: string | symbol;
        access: { get(): unknown, set(value: unknown): void };
        static: boolean;
        private: boolean;
      }
    ) => (initialValue: unknown) => unknown | void;
    // abilities:
    // - cannot change or replace its field (use an auto-accessor)
    // - change the value with which "its" field is initialized,
    // by returning a function that receives the original initialization value and returns a new initialization value
    // inside that function, "this" refers to the current instance
    // - register initializers
    // - expose access to its field (even if it is private) via context.access

    function logged(value, { kind, name }) {
      if (kind === "field") {
        return function (initialValue) {
          console.log(`initializing ${name} with value ${initialValue}`);
          return initialValue;
        };
      }
      // ...
    }
    class C {
      @logged x = 1;
    }
    new C(); // initializing x with value 1

    // --- @twice doubles the original initialization value of a field
    function twice() {
      return initialValue => initialValue * 2;
    }
    class C {
      @twice
      field = 3;
    }
    const inst = new C();
    assert.equal(
      inst.field, 6
    );

    // ---  @readOnly - immutable, read-only fields (instance public fields)
    // waits until the field was completely set up (either via an assignment or via the constructor)
    // (!) breaks instanceof, see workaround in classes decorators
    // auto-accessors @readOnly version does not require the class to be decorated
    const readOnlyFieldKeys = Symbol('readOnlyFieldKeys');
    function readOnly(value, {kind, name}) {
      // collect all keys of read-only fields
      if (kind === 'field') {
        return function () {
          if (!this[readOnlyFieldKeys]) {
            this[readOnlyFieldKeys] = [];
          }
          this[readOnlyFieldKeys].push(name);
        };
      }
      // wait until the instance was completely set up
      // make the fields, whose keys we collected, non-writable
      if (kind === 'class') {
        return function (...args) {
          const inst = new value(...args);
          for (const key of inst[readOnlyFieldKeys]) {
            Object.defineProperty(inst, key, {writable: false});
          }
          return inst;
        }
      }
    }

    @readOnly // wrap the class because decorator initializers are executed too early
    class Color {
      @readOnly
      name;
      constructor(name) {
        this.name = name;
      }
    }
    const blue = new Color('blue');
    assert.equal(blue.name, 'blue');
    assert.throws(
      () => blue.name = 'brown', // TypeError: Cannot assign to read only property 'name'
    );

    // --- dependency injection (instance public fields)
    // easier to adapt the dependencies to different environments, including testing.
    // inversion of control: constructor does not do its own setup, we do it for it.
    // approaches for doing dependency injection:
    // - manually, by creating dependencies and passing them to the constructor.
    // - via "contexts" in frontend frameworks such as React.
    // - via decorators and a dependency injection registry (a minor variation of dependency injection containers):
    function createRegistry() {
      const nameToClass = new Map();
      const nameToInstance = new Map();
      const registry = {
        register(name, componentClass) {
          nameToClass.set(name, componentClass);
        },
        getInstance(name) {
          if (nameToInstance.has(name)) {
            return nameToInstance.get(name);
          }
          const componentClass = nameToClass.get(name);
          if (componentClass === undefined) {
            throw new Error('Unknown component name: ' + name);
          }
          const inst = new componentClass();
          nameToInstance.set(name, inst);
          return inst;
        },
      };
      function inject (_value, {kind, name}) {
        if (kind === 'field') {
          return () => registry.getInstance(name);
        }
      }
      return {registry, inject};
    }
    const {registry, inject} = createRegistry();
    class Logger {
      log(str) { console.log(str) }
    }
    class Main {
      @inject logger;
      run() { this.logger.log('Hello!') }
    }
    registry.register('logger', Logger);
    new Main().run();
    // Output:
    // Hello!

    // --- "friend" visibility (instance private fields)
    // change the visibility of some class members by making them private
    // prevents them from being accessed publicly
    // lets a group of "friends" (functions, other classes, etc.) access the member.
    // everyone who has access to friendName, is a "friend" of classWithSecret.#name
    // module contains classes and functions that collaborate and that there is some instance data that only the collaborators should be able see
    const friendName = new Friend();
    class ClassWithSecret {
      @friendName.install #name = 'Rumpelstiltskin';
      getName() {
        return this.#name;
      }
    }
    // everyone who has access to "secret", can access inst.#name
    const inst = new ClassWithSecret();
    assert.equal(
      friendName.get(inst), 'Rumpelstiltskin'
    );
    friendName.set(inst, 'Joe');
    assert.equal(
      inst.getName(), 'Joe'
    );
    class Friend {
      #access = undefined;
      #getAccessOrThrow() {
        if (this.#access === undefined) { throw new Error('The friend decorator wasn not used yet') }
        return this.#access;
      }
      // an instance property whose value is a function whose "this"
      // is fixed (bound to the instance)
      install = (_value, {kind, access}) => {
        if (kind === 'field') {
          if (this.#access) { throw new Error('This decorator can only be used once') }
          this.#access = access;
        }
      }
      get(inst) {
        return this.#getAccessOrThrow().get.call(inst);
      }
      set(inst, value) {
        return this.#getAccessOrThrow().set.call(inst, value);
      }
    }

    // --- enums (static public fields)
    function enumEntry(value, {kind, name}) {
      if (kind === 'field') {
        return function (initialValue) {
          // a Map from "enum keys" (the names of their fields) to enum values
          if (!Object.hasOwn(this, 'enumFields')) {
            this.enumFields = new Map();
          }
          this.enumFields.set(name, initialValue);
          // add enum keys to enum values - without having to pass them to the constructor
          initialValue.enumKey = name;
          return initialValue;
        };
      }
    }
    class Color {
      @enumEntry static red = new Color();
      @enumEntry static green = new Color();
      @enumEntry static blue = new Color();
      toString() {
        return `Color(${this.enumKey})`;
      }
    }
    assert.equal(
      Color.green.toString(),
      'Color(green)'
    );
    assert.deepEqual(
      Color.enumFields,
      new Map([
        ['red', Color.red],
        ['green', Color.green],
        ['blue', Color.blue],
      ])
    );

    // --- register children on a parent class:
    const CHILDREN = new WeakMap();
    function registerChild(parent, child) {
      let children = CHILDREN.get(parent);
      if (children === undefined) {
        children = [];
        CHILDREN.set(parent, children);
      }
      children.push(child);
    }
    function getChildren(parent) {
      return CHILDREN.get(parent);
    }
    function register() {
      return function(value) {
        registerChild(this, value);
        return value;
      }
    }
    class Child {}
    class OtherChild {}
    class Parent {
      @register child1 = new Child();
      @register child2 = new OtherChild();
    }
    let parent = new Parent();
    getChildren(parent); // [Child, OtherChild]
  
Classes

    // signature:
    type ClassDecorator = (value: Function, context: {
      kind: "class";
      name: string | undefined;
      addInitializer(initializer: () => void): void;
    }) => Function | void;
    // abilities:
    // change the decorated class by changing "value"
    // replace the decorated class by returning a callable value
    // register initializers, which are called after the decorated class is fully set up
    // does not get context.access because classes are not members of other language constructs (like methods, etc.)

    function logged(value, { kind, name }) {
      if (kind === "class") {
        return class extends value {
          constructor(...args) {
            super(...args);
            console.log(`constructing an instance of ${name} with arguments ${args.join(", ")}`);
          }
        }
      }
      // ...
    }
    @logged
    class C {}
    new C(1);
    // constructing an instance of C with arguments 1

    // --- collecting instances, "instanceof" wont work (!)
    class InstanceCollector {
      instances = new Set();
      install = (value, {kind}) => {
        if (kind === 'class') {
          const _this = this;
          return function (...args) { // (A)
            const inst = new value(...args); // (B)
            _this.instances.add(inst);
            return inst;
          };
        }
      };
    }
    const collector = new InstanceCollector();
    @collector.install
    class MyClass {}
    const inst1 = new MyClass();
    const inst2 = new MyClass();
    const inst3 = new MyClass();
    assert.deepEqual(
      collector.instances, new Set([inst1, inst2, inst3])
    );
    assert.equal(
      inst1 instanceof MyClass,
      false
    );

    // enabling "instanceof":
    function countInstances(value) {
      const _this = this;
      let instanceCount = 0;

      // --- --- ---
      const wrapper = function (...args) { // new-callable
        instanceCount++;
        const instance = new value(...args);
        instance.count = instanceCount;
        return instance;
      };

      // 1 - set the .prototype of the wrapper function to the .prototype of the wrapped value
      wrapper.prototype = value.prototype;
      // 2 - give the wrapper function a method whose key is Symbol.hasInstance
      Object.defineProperty(
        wrapper, Symbol.hasInstance,
        {
          value: function (x) {
            return x instanceof value;
          }
        }
      );

      return wrapper;

      // --- --- ---
      // 3 - by returning a subclass of value
      return class extends value { // (A)
        constructor(...args) {
          super(...args);
          instanceCount++;
          this.count = instanceCount;
        }
      };

    }

    @countInstances
    class MyClass {}

    const inst1 = new MyClass();
    assert.ok(inst1 instanceof MyClass);
    assert.equal(inst1.count, 1);
    const inst2 = new MyClass();
    assert.ok(inst2 instanceof MyClass);
    assert.equal(inst2.count, 2);

    // --- making classes function-callable:
    function functionCallable(value, {kind}) {
      if (kind === 'class') {
        return function (...args) {
          if (new.target !== undefined) {
            throw new TypeError('This function cant be new-invoked');
          }
          return new value(...args);
        }
      }
    }
    @functionCallable
    class Person {
      constructor(name) {
        this.name = name;
      }
    }
    const robin = Person('Robin');
    assert.equal(
      robin.name, 'Robin'
    );
  
Class Auto-Accessors

    // define a getter and setter on the class prototype
    // that defaults to getting and setting a value on a private slot.
    // "accessor" keyword before a class field.
    // receive a value, which is an object containing the get and set accessors
    // defined on the prototype of the class (or the class itself in the case of static auto-accessors).
    type ClassAutoAccessorDecorator = (
      value: {
        get: () => unknown;
        set(value: unknown) => void;
      },
      context: {
        kind: "accessor";
        name: string | symbol;
        access: { get(): unknown, set(value: unknown): void };
        static: boolean;
        private: boolean;
        addInitializer(initializer: () => void): void;
      }
    ) => {
      get?: () => unknown;
      set?: (value: unknown) => void;
      init?: (initialValue: unknown) => unknown;
    } | void;
    // abilities:
    // - receives the getter and the setter of the auto-accessor via its parameter value
    // context.access provides the same functionality
    // - replace the decorated auto-accessor by returning an object with the methods .get() and/or .set()
    // - influence the initial value of the auto-accessor by returning an object with the method .init()
    // - register initializers

    function logged(value, { kind, name }) {
      if (kind === "accessor") {
        let { get, set } = value;
        return {
          get() {
            console.log(`getting ${name}`);
            return get.call(this);
          },
          set(val) {
            console.log(`setting ${name} to ${val}`);
            return set.call(this, val);
          },
          init(initialValue) {
            console.log(`initializing ${name} with value ${initialValue}`);
            return initialValue;
          }
        };
      }
      // ...
    }
    class C {
      @logged accessor x = 1;
    }
    let c = new C(); // initializing x with value 1
    c.x; // getting x
    c.x = 123; // setting x to 123

    // --- read-only auto-accessors
    // Compared to the field version, has one considerable advantage:
    // idoes not need to wrap the class to ensure that the decorated constructs become read-only
    const UNINITIALIZED = Symbol('UNINITIALIZED');
    function readOnly({get,set}, {name, kind}) {
      if (kind === 'accessor') {
        return {
          init() {
            return UNINITIALIZED;
          },
          get() {
            const value = get.call(this);
            if (value === UNINITIALIZED) {
              throw new TypeError(
                `Accessor ${name} hasn not been initialized yet`
              );
            }
            return value;
          },
          set(newValue) {
            const oldValue = get.call(this);
            if (oldValue !== UNINITIALIZED) {
              throw new TypeError(
                `Accessor ${name} can only be set once`
              );
            }
            set.call(this, newValue);
          },
        };
      }
    }
    class Color {
      @readOnly
      accessor name;
      constructor(name) {
        this.name = name;
      }
    }
    const blue = new Color('blue');
    assert.equal(blue.name, 'blue');
    assert.throws(
      () => blue.name = 'yellow', // TypeError: Accessor name can only be set once
    );
    const orange = new Color('orange');
    assert.equal(orange.name, 'orange');
  
Decorator Metadata

    // make it easy for decorators to create and consume metadata on any class they are used on or within.
    // providing a "metadata" object, which can be used either to directly store metadata, or as a WeakMap key.
    // object is provided via the decorator "context" argument,
    // and is then accessible via the Symbol.metadata property on the class definition after decoration
    type Decorator = (value: Input, context: {
      kind: string;
      name: string | symbol;
      access: {
        get?(): unknown;
        set?(value: unknown): void;
      };
      isPrivate?: boolean;
      isStatic?: boolean;
      addInitializer?(initializer: () => void): void;
    + metadata?: Record<string | number | symbol, unknown>;
    }) => Output | void;

    // usage:
    function meta(key, value) {
      return (_, context) => {
        context.metadata[key] = value;
      };
    }4

    @meta('a', 'x')
    class C {
      @meta('b', 'y')
      m() {}
    }
    C[Symbol.metadata].a; // 'x'
    C[Symbol.metadata].b; // 'y'

    interface Context {
        name: string;
        metadata: Record<PropertyKey, unknown>;
    }
    function setMetadata(_target: any, context: Context) {
        context.metadata[context.name] = true;
    }
    class SomeClass {
        @setMetadata
        foo = 123;
        @setMetadata
        accessor bar = "hello!";
        @setMetadata
        baz() { }
    }
    const ourMetadata = SomeClass[Symbol.metadata];
    console.log(JSON.stringify(ourMetadata));
    // { "bar": true, "baz": true, "foo": true }
    // metadata could possibly be attached for lots of uses like debugging, serialization,
    // or performing dependency injection with decorators.
    // metadata objects are created per decorated class,
    // frameworks can either privately use them as keys into a Map or WeakMap, or tack properties on as necessary.
    // keep track of which properties and accessors are serializable when using JSON.stringify:
    import { serialize, jsonify } from "./serializer";
    class Person {
      firstName: string;
      lastName: string;
      @serialize
      age: number
      @serialize
      get fullName() {
        return `${this.firstName} ${this.lastName}`;
      }
      toJSON() {
        return jsonify(this)
      }
      constructor(firstName: string, lastName: string, age: number) {
        // ...
      }
    }
    //  example of how the module ./serialize.ts might be defined:
    const serializables = new WeakMap<object, string[]>();
    type Context =
        | ClassAccessorDecoratorContext
        | ClassGetterDecoratorContext
        | ClassFieldDecoratorContext
        ;
    export function serialize(_target: any, context: Context): void {
        if (context.static || context.private) {
            throw new Error("Can only serialize public instance members.")
        }
        if (typeof context.name !== "string") {
            throw new Error("Can only serialize string properties.");
        }
        let propNames = serializables.get(context.metadata);
        if (propNames === undefined) {
            serializables.set(context.metadata, propNames = []);
        }
        propNames.push(context.name);
    }
    export function jsonify(instance: object): string {
        const metadata = instance.constructor[Symbol.metadata];
        const propNames = metadata && serializables.get(metadata);
        if (!propNames) {
            throw new Error("No members marked with @serialize.");
        }
        const pairStrings = propNames.map(key => {
            const strKey = JSON.stringify(key);
            const strValue = JSON.stringify((instance as any)[key]);
            return `${strKey}: ${strValue}`;
        });
        return `{ ${pairStrings.join(", ")} }`;
    }

    // --- inheritance
    // with parent class metadata object is set to the metadata object of the superclass
    // allows taking advantage of shadowing by default, mirroring class inheritance:
    function meta(key, value) {
      return (_, context) => {
        context.metadata[key] = value;
      };
    }

    @meta('a', 'x')
    class C {
      @meta('b', 'y')
      m() {}
    }

    C[Symbol.metadata].a; // 'x'
    C[Symbol.metadata].b; // 'y'

    class D extends C {
      @meta('b', 'z')
      m() {}
    }

    D[Symbol.metadata].a; // 'x'
    D[Symbol.metadata].b; // 'z'

    // read metadata during decoration, so it can be modified or extended by children rather than overriding it:
    function appendMeta(key, value) {
      return (_, context) => {
        // NOTE: be sure to copy, not mutate
        const existing = context.metadata[key] ?? [];
        context.metadata[key] = [...existing, value];
      };
    }

    @appendMeta('a', 'x')
    class C {}

    @appendMeta('a', 'z')
    class D extends C {}

    C[Symbol.metadata].a; // ['x']
    D[Symbol.metadata].a; // ['x', 'z']

    // --- private metadata
    //  object can be used as a key in a WeakMap if the decorator author does not want to share their metadata:
    const PRIVATE_METADATA = new WeakMap();
    function meta(key, value) {
      return (_, context) => {
        let metadata = PRIVATE_METADATA.get(context.metadata);
        if (!metadata) {
          metadata = {};
          PRIVATE_METADATA.set(context.metadata, metadata);
        }
        metadata[key] = value;
      };
    }

    @meta('a', 'x')
    class C {
      @meta('b', 'y')
      m() {}
    }

    PRIVATE_METADATA.get(C[Symbol.metadata]).a; // 'x'
    PRIVATE_METADATA.get(C[Symbol.metadata]).b; // 'y'
  
addInitializer examples

    // --- @customElement
    // create a decorator which registers a web component in the browser
    function customElement(name) {
      return (value, { addInitializer }) => {
        addInitializer(function() {
          customElements.define(name, this);
        });
      }
    }

    @customElement('my-element')
    class MyElement extends HTMLElement {
      static get observedAttributes() {
        return ['some', 'attrs'];
      }
    }

    // ---  @bound
    // bind the method to the instance of the class:
    function bound(value, { name, addInitializer }) {
      addInitializer(function () {
        this[name] = this[name].bind(this);
      });
    }

    class C {
      message = "hello!";
      @bound
      m() {
        console.log(this.message);
      }
    }
    let { m } = new C();
    m(); // hello!
  
access object and metadata sidechanneling

    // --- METADATA SIDECHANNELING
    // dependency injection decorator:
    const INJECTIONS = new WeakMap();
    function createInjections() {
      const injections = [];
      function injectable(Class) {
        INJECTIONS.set(Class, injections);
      }
      function inject(injectionKey) {
        return function applyInjection(v, context) {
          // use access object
          injections.push({ injectionKey, set: context.access.set });
        };
      }
      return { injectable, inject };
    }
    class Container {
      registry = new Map();
      register(injectionKey, value) {
        this.registry.set(injectionKey, value);
      }
      lookup(injectionKey) {
        this.registry.get(injectionKey);
      }
      create(Class) {
        let instance = new Class();
        for (const { injectionKey, set } of INJECTIONS.get(Class) || []) {
          set.call(instance, this.lookup(injectionKey));
        }
        return instance;
      }
    }
    class Store {}
    const { injectable, inject } = createInjections();
    @injectable
    class C {
      // inject values on an instance
      @inject('store') store;
    }
    let container = new Container();
    let store = new Store();
    container.register('store', store);
    let c = container.create(C);
    c.store === store; // true
  
exposing data from decorators

    // in a surrounding scope (doesnt work if a decorator comes from another module):
    const classes = new Set(); // (A)
    function collect(value, {kind, addInitializer}) {
      if (kind === 'class') {
        classes.add(value);
      }
    }
    @collect
    class A {}
    @collect
    class B {}
    @collect
    class C {}
    assert.deepEqual(
      classes, new Set([A, B, C])
    );

    // via a factory function:
    function createClassCollector() {
      const classes = new Set();
      function collect(value, {kind, addInitializer}) {
        if (kind === 'class') {
          classes.add(value);
        }
      }
      return {
        classes,
        collect,
      };
    }
    const {classes, collect} = createClassCollector();
    @collect
    class A {}
    @collect
    class B {}
    @collect
    class C {}
    assert.deepEqual(
      classes, new Set([A, B, C])
    );

    // via a class:
    // it has two members: .classes - Set with the collected classes , .install - a class decorator
    class ClassCollector {
      classes = new Set();
      // implement .install by assigning an arrow function to a public instance field
      install = (value, {kind}) => { // scopes of the current instance
        // also the outer scope of the arrow function
        if (kind === 'class') {
          this.classes.add(value);
        }
      };
    }
    const collector = new ClassCollector();
    @collector.install
    class A {}
    @collector.install
    class B {}
    @collector.install
    class C {}
    assert.deepEqual(
      collector.classes, new Set([A, B, C])
    );
  
Type signature:
Kind of decorator: (input) => output .access
Class (func) => func2 -
Method (func) => func2 {get}
Getter (func) => func2 {get}
Setter (func) => func2 {set}
Auto-accessor ({get,set}) => {get,set,init} {get,set}
Field () => (initValue)=>initValue2 {get,set}
Value of this in functions
this is: undefined Class Instance
Decorator function
Static initializer
Non-static initializer
Static field decorator result
Non-static field decorator result

Mixins


    // Disposable Mixin
    class Disposable {
      isDisposed: boolean;
      dispose() { this.isDisposed = true; }
    }
    // Activatable Mixin
    class Activatable {
      isActive: boolean;
      activate() { this.isActive = true; }
      deactivate() { this.isActive = false; }
    }
    // treat the classes as interfaces - only use the types behind Disposable and Activatable
    class SmartObject implements Disposable, Activatable {
      constructor() {
        setInterval(() => console.log(this.isActive + " : " + this.isDisposed), 500);
      }
      interact() { this.activate(); }
      // Disposable
      isDisposed: boolean = false;
      dispose: () => void;
      // Activatable
      isActive: boolean = false;
      activate: () => void;
      deactivate: () => void;
    }
    applyMixins(SmartObject, [Disposable, Activatable]);
    let smartObj = new SmartObject();
    setTimeout(() => smartObj.interact(), 1000);
    ////////////////////////////////////////
    // In runtime library somewhere
    ////////////////////////////////////////
    function applyMixins(derivedCtor: any, baseCtors: any[]) {
      // run through the properties of each of the mixins
      // and copy them over to the target of the mixins
      // filling out the stand-in properties with their implementations
      baseCtors.forEach(baseCtor => {
        Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
          derivedCtor.prototype[name] = baseCtor.prototype[name];
        });
      });
    }
  

/// <directive />


    // amdModule.ts
    ///<amd-module name="NamedModule"/>
    export class C { }
    // result in assigning the name NamedModule to the module as part of calling the AMD define:
    // amdModule.js
    define("NamedModule", ["require", "exports"], function (require, exports) {
      var C = (function () {
        function C() { }
        return C;
      })();
      exports.C = C;
    });
  

    // --- import type
    // Resolve `pkg` as if we were importing with a `require()`
    import type { TypeFromRequire } from "pkg" assert {
        "resolution-mode": "require"
    };
    // Resolve `pkg` as if we were importing with an `import`
    import type { TypeFromImport } from "pkg" assert {
        "resolution-mode": "import"
    };
    export interface MergedType extends TypeFromRequire, TypeFromImport {}

    // --- import()
    export type TypeFromRequire =
        import("pkg", { assert: { "resolution-mode": "require" } }).TypeFromRequire;
    export type TypeFromImport =
        import("pkg", { assert: { "resolution-mode": "import" } }).TypeFromImport;
    export interface MergedType extends TypeFromRequire, TypeFromImport {}
  

Back to Main Page