TypeScript


Creative Commons License
This -TypeScript- tutorial is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License
Preamble
Headlines
TypeScript installation
Rule(s)
TypeScript compilation and execution
Rule(s)

tsconfig.json file

Rule(s)
Example (source files in the ts directory, recursively)
"include": [
    "ts/**/*"
],
Example
"files": [
    "Abstract_optional.ts",
    "Assertion_function.ts",
    "Infer.ts",
    "Main.ts",
    "The_case_of_this.ts"
  ],
Rule(s)
Example (tsconfig.json file)
"exclude": [
    "node_modules"
],
Rule(s)
Example (tsconfig.json file)
"moduleResolution": "node", // Alternative is "classic", but it is not recommended...
Rule(s)
Example (tsconfig.json file and later on TypeScript code)
"noImplicitAny": true,
declare const dat: any; // 'any' is imposed by '"noImplicitAny": true'
Rule(s)
Example (JavaScript)
// window.splash-screen.enable('circular'); // Dash in 'splash-screen' creates a bug...
window['splash-screen'].enable('circular'); // Right way in JavaScript
Example (tsconfig.json file and later on TypeScript code)
"suppressImplicitAnyIndexErrors": true,
// '"suppressImplicitAnyIndexErrors": true,' is required (otherwise compilation error):
window['splash-screen'].enable('circular');
Rule(s)
Example (tsconfig.json file)
"sourceMap": true,
Example (tsconfig.json complete file)
{
  "compilerOptions": {
    "baseUrl": ".",
    // https://www.typescriptlang.org/docs/handbook/module-resolution.html#additional-module-resolution-flags
    // https://stackoverflow.com/questions/43281741/how-to-use-paths-in-tsconfig-json:
    "paths": {
      "GLTFLoader": ["three.js-r115/examples/jsm/loaders/GLTFLoader"], // From "baseUrl"
      "Three": ["three.js-r115/src/Three"] // From "baseUrl"
    },
    "module": "ES6", // Code generation..
    "moduleResolution": "node", // Required by Three.js (https://www.typescriptlang.org/docs/handbook/module-resolution.html#how-typescript-resolves-modules)
    //    "noImplicitAny": true,
    "outDir": "js", // Where to generate JavaScript files...
    "sourceMap": true, // For debugging...
    "target": "ES6", // Required by Three.js...
    "traceResolution": true
  },
  //  "exclude": [ // No need if 'npm' not used...
  //    "node_modules"
  //  ],
  //  "files": [ // Source files relative to the base URL (added to "include"):
  //    "./ts/Covid_19.ts"
  //  ],
  "include": [ // Source files relative to the base URL:
    // '.ts', '.tsx', and '.d.ts' by default:
    "ts/**/*" // '*' matches zero or more characters (excluding directory separators) AND '**/' recursively matches any subdirectory...
  ]
}
TypeScript reuses JavaScript primitive types boolean, number, string… plus “special types” outside the scope of JavaScript for type checking.

var, let, and const

Rule(s)
Example
const F = "Franck"; // 'F' has the type "Franck"
let B = "Barbier";   // 'B' has the type 'string'
let FB = "FranckBarbier" as const; // 'FB' has type "FranckBarbier"
// FB = "?"; // Compilation error because of 'as const'... -> somehow stupid as this stage?

const lasagna = {country: "Italy"};
lasagna.country = "France"; // 'const' has low scope...
const foie_gras = {country: "France"} as const;
// foie_gras.country = "Italy"; // Compilation error!
// 'const' cast only applies to references to enum members, string, number, boolean, array, or object literals:
// let my_favorite_dish = lasagna as const // Compilation error!

Numbers… (and their representation)

Rule(s)
Example
/** JavaScript 'number' primitive type is 64-bit long based on the IEEE 754 standard */
// See also: http://speakingjs.com/es5/ch11.html#number_representation
let one: number = Number.parseInt('00000001', 2);
if (Number.isNaN(one)) window.alert('Number.isNaN(one)');
else window.alert('one: ' + one); // '1'
let two: Number = new Number(1 << 1);
window.alert('two(2): ' + two.toString(2)); // '10'
let zero: Number = new Number(1 >> 1);
window.alert('zero: ' + zero); // '0'
Rule(s)
Example
/** JavaScript Bitwise operators apply on the first significant 32 bits only! */
// How negative numbers are represented:
let minus_nine: Number = new Number((~9) | 1); // '00000000000000000000000000001001' -> '11111111111111111111111111110110' -> '11111111111111111111111111110111'
window.alert('minus_nine: ' + minus_nine); // '-9'

// 32 significant bits representing the number are viewed as an *unsigned* 32-bit integer:
let x: Number = new Number(-1 >>> 0); // Unsigned right shift that inserts '0' on the left (BTW: unsigned left shift does not exist!)
window.alert('-1 >>> 0: ' + x.toString(2)); // '11111111111111111111111111111111' -> (2^32 - 1) -> 4294967295

window.alert(Number.MAX_SAFE_INTEGER.toString(2).length); // '53' since 'Number.MAX_SAFE_INTEGER' is equal to (2^53 - 1)
window.console.assert(Number.MAX_SAFE_INTEGER === -Number.MIN_SAFE_INTEGER);

window.alert(Number.MAX_VALUE * 2 === Number.POSITIVE_INFINITY);

window.alert(isNaN(Math.sqrt(-1))); // 'true'
See also

Special types

Rule(s)
Example
let my_var: any;
window.console.assert(my_var === undefined, "Note that 'undefined' is both the type and its singleton value!");
my_var = {given_name: "Franck"};
window.console.assert(my_var.given_name === "Franck", "No compilation error...");
Rule(s)
Example
let my_other_var: unknown;
my_other_var = {given_name: "Franck"};
// window.console.assert(my_other_var.given_name === "Franck", "Compilation error: 'given_name' does not exist on type 'unknown'");
// if ('given_name' in my_other_var) ... // Compilation error!

function owns_given_name(o: any): o is { given_name: string } { // Type guard is introduced...
    return 'given_name' in o;
}
if (owns_given_name(my_other_var))
    window.alert(my_other_var.given_name); // No compilation error...
Rule(s)
Example
my_WebSocket.onmessage = (event: MessageEvent) => {
    // Weak type checking, compiler does not raise any error:
    let m: My_type = JSON.parse(event.data); // What's inside data?
Rule(s)
Example (tsconfig.json file)
"lib": [ // '"target": "ES6"' is enough
    "DOM",
    …
// 'HTMLElement' and 'HTMLCanvasElement' come from the 'DOM' library
// Type cast is imposed by the fact that 'createElement' returns a 'HTMLElement' object
let canvas: HTMLCanvasElement = window.document.createElement('canvas') as HTMLCanvasElement;

// Alternatively:
let canvas: HTMLCanvasElement = <HTMLCanvasElement>window.document.createElement('canvas');
See also
Rule(s)
Example (tsconfig.json file)
"strictNullChecks": true,
…
let canvas_context: CanvasRenderingContext2D | null = canvas.getContext('2d');
if (canvas_context !== null) // Required by '"strictNullChecks": true'
    canvas_context.drawImage(image, 0, 0, image.width, image.height);
Rule(s)
Example Miscellaneous.ts.zip 
window.console.assert(this._scene.getObjectByName('dlh')!.matrixAutoUpdate === false);
See also

TypeScript has a native support for enumerated types

Example
enum Medal {
    Gold = "Gold",
    Silver = "Silver",
    Bronze = "Bronze"
}
let award: Medal = Medal.Gold;
window.alert(award); // 'Gold'
See also
The move from “legacy” JavaScript to TypeScript is a tricky task.
Rule(s)
Example
import * as THREE from "../three.js-r115/src/Three" // Look for 'Three.ts' or 'Three.tsx' or 'Three.d.ts'...
Rule(s)
Example
declare const dat; // Type is 'any'...
window.console.assert(typeof dat !== "undefined", "Problem: it won't work because 'dat.gui.min.js' library has not been loaded...");
Rule(s)
Example
// @ts-ignore (no TypeScript support for 'THREE.GLTFLoader') -> compilation error disappears!
(new THREE.GLTFLoader()).load('./models/Coronavirus-sars-co-v2.gltf', this._post_process_Covid_19.bind(this, ready)); // External lib.
Resource(s)
TypeScript fundamentally promotes strong object/variable typing including type inference.
Example
let size: number; // Explicit typing...
let j = 0; // Type inference -> 'number'...
const my_array = []; // Implicit 'any' elements in 'my_array' -> bad idea...
const my_other_array = new Array<number>(size); // Type inference...
my_other_array.push(j);
Example
window.onscroll = function (event) {
    window.console.log(event.button); // No compilation error...
}
// Versus:
window.onscroll = function (event: UIEvent) {
    // window.console.log(event.button); // Compilation error: 'UIEvent' type has no 'button' property...
}
TypeScript relies on an advanced typing system from the idea of conditional type. The type keyword is in particular introduced for managing some “type interoperability”.
Example
export abstract class Individual { …
export class Kid extends Individual { …

type Kid_bis = Kid extends Individual ? Kid : never; // 'Kid_bis' is constructed as a conditional type...
const Maternity = function (): Kid_bis { // 'Kid' and 'Kid_bis' are equivalent types...
    return new Kid();
}

type Individual_bis = Individual extends Kid ? Individual : never;
const Maternity_ = function (): Individual_bis { // 'Individual_bis' and 'never' are equivalent types...
    // One doesn't return a 'Individual' object since 'Individual' is abstract:
    // return new Kid(); // Compilation error since 'Maternity_' *never* returns something...
    // 'throw' creates compatibility with 'never':
    throw "TS2534: A function returning 'never' cannot have a reachable end point.";
}
As a strongly typed programming language, TypeScript promotes generic types along with predefined generic collections (Array, Set, Map…) that are wrappers of JavaScript non-generic collections.
Example
export enum NoLanguageCharacter_texture_type {
    Blurred = "Blurred",
    Grayed = "Grayed",
    None = "None",
    Normal = "Normal",
    Sobel = "Sobel"
}
…
protected readonly _textures: Map<NoLanguageCharacter_texture_type, THREE.CanvasTexture> = new Map<NoLanguageCharacter_texture_type,THREE.CanvasTexture>();
…
this._textures.set(NoLanguageCharacter_texture_type.Normal, new THREE.CanvasTexture(canvas));
this._textures.get(NoLanguageCharacter_texture_type.Normal).minFilter = THREE.LinearFilter;
…
for (let texture of this._textures.values())
    texture.dispose();
Beyond, the JavaScript Array type, TypeScript offers a native support for tuples.
Example
let matrix: Array<Array<number>> = [[1, 2], [1, 2]];

const chromosomes: Array<string> = ["Genetic female - XX", "Genetic male - XY"];
window.alert(chromosomes instanceof Array); // 'true' is displayed
// window.alert(typeof chromosomes); // 'object' is displayed
chromosomes[-1] = "N'importe quoi !";
// window.alert(chromosomes[-1]); // "N'importe quoi !" is displayed
window.alert("chromosomes.length: " + chromosomes.length); // "2" is displayed!

const diseases = new Array(Symbol("Turner's syndrome - X"), Symbol("Klinefelter syndrome - XXY")); // Type inference...
window.console.assert(diseases.length === 2);

const genetics = [...chromosomes, ...diseases]; // Array of 'string | symbol'...
// Symbols do not duplicate (they're unique!), so 'null':
window.alert("'JSON.stringify(genetics)': " + JSON.stringify(genetics)); // '["Genetic female - XX","Genetic male - XY",null,null]' is displayed!
const mapping: [string | symbol, string | symbol] = [chromosomes[0], diseases[0]]; // Tuple...
Resource(s)
See also
TypeScript offers the notions of union and intersection types (here…).
Example (union)
let my_var: number | string | null = null;
Example (intersection)
type FemaleAndManager = Female & Manager;
let my_var: FemaleAndManager;
Like JavaScript 6, TypeScript uses the class keyword for user-defined types. Contrary to JavaScript 6, TypeScript natively supports the notion of interface.
Rule(s)

Classes

Example
class N_INSEE {

    constructor(private readonly _n_insee: number, private readonly _clef: number) { // 'public' by default
        // 'this._n_insee' is automatically inserted as private field
        // 'this._clef' is automatically inserted as private field
    }

    calcul_clef(): boolean { // 'public' by default
        if ((97 - (this._n_insee % 97)) === this._clef)
            return true;
        else
            return false;
    }
}

let main = function () { // 'window.onload = main;'
    let n_insee: N_INSEE = new N_INSEE(1630125388055, 29);
    if (n_insee.calcul_clef()) window.alert("OK");
    else window.alert("Non OK");
}
Example (JavaScript 6 code generated by the TypeScript compiler)
class N_INSEE {
    constructor(_n_insee, _clef) {
        this._n_insee = _n_insee;
        this._clef = _clef;
    }
    calcul_clef() {
        if ((97 - (this._n_insee % 97)) === this._clef)
            return true;
        else
            return false;
    }
}
let main = function () {
    let n_insee = new N_INSEE(1630125388055, 29);
    if (n_insee.calcul_clef())
        window.alert("OK");
    else
        window.alert("Non OK");
};
Resource(s)

Getter and setter

Rule(s)
Example (use of getter)
private _focus: boolean = false;
get focus(): boolean {
    return this._focus;
}
set focus(focus: boolean) /* : void */ { // TypeScript setter rejects returned type...
    this._focus = focus;
}
…
if (object_with_focus.focus) … // Getter call is without braces...
Example (use of setter)
private _camera = Camera.General;
get camera(): Camera {
    return this._camera;
}
set camera(camera: Camera) {
    this._camera = camera;
}
…
this.camera = Camera.Embedded; // Setter call is without braces...

static property

Rule(s)
Example
abstract class Compte_epargne extends Compte_bancaire {
    protected static _Taux_interet: number;
    …
}

class Livret_A extends Compte_epargne {
    public static Information() {
        return "Classe des livrets A - rémunération fixée par l'Etat !";
    }
    public static Initialize() {
        Compte_epargne._Taux_interet = 0.5; // 13/02/2020
    }
}
Rule(s)
Example
/** Simulation of Java static initializer */
function Initialize(target: any) { // Decorator...
    target.Initialize();
}
…
@Initialize // '"experimentalDecorators": true,'
class Compte_epargne_logement extends Compte_epargne {
    public static Information() {
        return "Classe des comptes épargne logement - rémunération ratio du Livret A !";
    }
    public static Initialize() { // Launched by decorator...
        Compte_epargne._Taux_interet = Livret_A._Taux_interet * 2. / 3.; // 2/3 du taux du Livret A...
        // Compte_epargne._Taux_interet = ...; // arrondi au 1/4 point le plus proche (0.5 * 2. / 3. ==== 0.33) -> (0.25)...
    }
}
Resource(s)

Optional property

Rule(s)
Example (attribute)
protected _eyes?: Array<any>;
protected _face: Array<any> | undefined; // <=> 'protected _face?: Array<any>;'
protected _mouth?: Array<any>;
…
if (this._eyes !== undefined) … // Required by the compiler!
Example (method)
protected _compute_morphing?(geometry: any): void;
…
if (this._compute_morphing)
    this._compute_morphing(front_geometry); // Required by the compiler!

Constructor overloading

Rule(s)
Example
class Human_being {
    protected readonly _birth_date: number;

    constructor(readonly birth_date: string) {
        this._birth_date = Date.parse(birth_date);
    }

    // private constructor(readonly birth_date: number) { // No other constructor is accepted, even 'private'!
    //     this._birth_date = birth_date;
    // }

    // cloning(): this { // This differs from 'Current' polymorphic type in Eiffel!
    //     return this; // Only 'this' object complies with 'this' *TYPE*!
    // }

    cloning(): Human_being {
        console.log("cloning in Human_being...\n");
        // return new Human_being(this._birth_date); // No way because multiple constructors aren't accepted!
        return new Human_being(new Date(this._birth_date).toDateString());
    }
}

Class inheritance

Rule(s)
Example
abstract class Animal {…}

abstract class Mammal extends Animal {…}

abstract class Oviparous extends Animal {…}

// class Ornithorhynchus extends Mammal, Oviparous { // Not supported as in C++!
// }

class Giraffe extends Mammal {
    constructor(private readonly _name: string, private readonly _height: number) {
        super(); // Mandatory
    }
}
Rule(s)
Example
export default abstract class NoLanguageCharacter {
    protected abstract _hex_color(): number; // No code...
    …
Example
export default class Troublemaker extends NoLanguageCharacter { // Not abstract...
    protected _hex_color(): number { // Cannot "become" 'private' while it may become public... 
        return …;
    }
    …
Rule(s)
Example
abstract class Friend {
    protected constructor(private readonly _nickname: string) {
    }
}

class Close_friend extends Friend {
    public constructor(/* 'private readonly' tries the creation of another '_nickname' private attribute! */ _nickname: string) {
        super(_nickname);
    }
}
Rule(s)
Example
interface Responsibility { /* ... */ }

abstract class Individual {
    abstract responsibilities?(): Set<Responsibility>; // Abstract and optional...
    abstract isResponsible(responsibility: Responsibility): boolean;
}

class Kid extends Individual {
    responsibilities: undefined; // Implementation allows 'Kid' to no longer be abstract...
    isResponsible(responsibility: Responsibility): boolean {
        return false; // 'false' in all cases: 'responsibility' is ignored...
    };
}

Polymorphism

Rule(s)
Example
class Female extends Human_being {

    cloning(): Female {
        window.console.log("cloning in Female...\n");
        return new Female(new Date(this._birth_date).toDateString());
    }
}
…
// Later on:
let Eve: Human_being = new Female("0, 0, 0");
let Eve_clone: Human_being = Eve.cloning(); // 'cloning in Female...' is displayed => covariance applies in TypeScript!

Using this as a type

Example
class Human_being {
    …
    that_s_me(): this { // This differs from 'Current' polymorphic type in Eiffel!
        return this; // Only 'this' object complies with 'this' *TYPE*!
    }
}
…
// Later on:
window.console.assert(Eve.that_s_me().constructor === Female); // 'true' is displayed!

Using this in conjunction with bind

Example
class My_class {
    private readonly _resolve_2020: Promise<number> = Promise.resolve(2020);
    …
        this._resolve_2020.then(function (this: My_class, value: number) { // Differently to JavaScript, TypeScript makes explicit the type of 'this' in the function...
            window.console.assert(value === 2020);
            // Use 'this' as effective instance of 'My_class'...
        }.bind(this));
Example
interface Person {
    identities: Array<string>
    display?: Function
}

const person: Person = {
    identities: ["Barbier", "Franck", "Bab"],
    display: function (this: Person) { // Differently to JavaScript, TypeScript makes explicit the type of 'this' in the function...
        window.console.assert(person === this);
        this.identities.forEach(function (element) {
            window.alert(element);
        }.bind(this)); // This guarantees 'window.console.assert(person === this)'
    }
};

person.display();

Generic class

Rule(s)
Example
class Sort_illustration<T extends Comparable<T>> {
    private readonly _implementation: Array<T> = new Array/*<T>*/(); // Don't forget to fill in somewhere else!

    sort(left: number, right: number): void { // Naive (slow) sort
        console.assert(left < right);
        for (let i: number = left; i < right; i++) {
            for (let j: number = i + 1; j <= right; j++) {
                if (this._implementation[i].compareTo(this._implementation[j]) === Comparable_.GreaterThan) {
                    let temp: T = this._implementation[i];
                    this._implementation[i] = this._implementation[j];
                    this._implementation[j] = temp;
                }
            }
        }
    }
}

Interfaces

Rule(s)
Example
export enum Access {  // 'export' allows external usage
    Failure,
    Success,
}

export interface Image_access {
    access: Access;
    image_URL: string;
}

export interface Open_Food_Facts_item {
    images: Array<Promise<Image_access>>;
    image_URLs: Array<string>;
    product_name: string;
}
…
// Somewhere else:
private readonly _item_data: Promise<Open_Food_Facts_item>;

Interface concretization

Rule(s)
Example
interface Currency { // Example of USA dollar:
    common_symbol: string; // $
    common_name: string; // Dollar
    description?: string;
    iso_code: number; // 840
    iso_symbol: string; // USD
    substitute?: Currency; // 'undefined'
    substitution_date: number | null;
}

 class Currencies {
    public static readonly Currencies: Array<Currency> = [
        {
            common_symbol: "$",
            common_name: "Dollar",
            description: "First leading currency in the world...",
            iso_code: 840,
            iso_symbol: "USD",
            substitution_date: null
        },
        {
            common_symbol: "€",
            common_name: "Euro",
            description: "Second leading currency in the world...",
            iso_code: 978,
            iso_symbol: "EUR",
            substitution_date: null
        }
    ];
}

interface Iso_code_checking { // Interface as contract...
    already_exists: (iso_code: number) => Promise<boolean>; // Attribute as lambda expression...
}

class Iso_code_checking_service implements Iso_code_checking { // Class as service...
    already_exists(iso_code: number): Promise<boolean> { // Concretization...
        const exists = Currencies.Currencies.find(currency => currency.iso_code === iso_code);
        // Check on the Web as well....
        return Promise.resolve(exists !== undefined ? true : false);
    }
}
Rule(s)
Example
interface AsyncValidator {
    validate(ac: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null>;
}
…
class My_asynchronous_validator implements AsyncValidator {
    validate(ac: AbstractControl): Promise<ValidationErrors | null> { /* Concrete code here... */ }
}

Generic interface

Rule(s)
Example
interface Pair<T> {
    readonly _first: T; // Cannot be 'private'...
    readonly _second: T;
    description?: string;
}

class Palindrome_authorized_interval implements Pair<string> {
    // Caution: attributes from interface *MUST* be repeated here...
    // This is done through constructor:
    constructor(readonly _first: string, readonly _second: string) {
    }

    /* 'private' -> error because 'public' in 'Pair<T>' */
    description: string; // May be omitted because optional in 'Pair<T>'

    set_description(/* 'readonly' -> constructor only! */ description: string): void { // 'public' by default
        this.description = description;
    }
}

Interface inheritance

Rule(s)
Example
interface My_event_type extends CustomEvent { // 'CustomEvent' comes from the 'DOM' library (https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent)
    additional_attribute?: symbol;
}
Rule(s)
Example
enum Comparable_ {LessThan, Equal, GreaterThan}

interface Comparable<T> { // Similar to Java "functional interface"
    compareTo(t: T): Comparable_; // Method signature
}

interface Pair<T> extends Comparable<Pair<T>> {
    readonly _first: T; // Cannot be 'private'...
    readonly _second: T;
    description?: string;
}
Example (abstract nature of Palindrome_authorized_interval class is removed…)
class Palindrome_authorized_interval implements Pair<string> {
    …

    compareTo(t: Pair<string>): Comparable_ { // 'public' by default
        if (this._first.localeCompare(t._first) === -1 &&
            this._second.localeCompare(t._second) === -1) return Comparable_.LessThan;
        if (this._first.localeCompare(t._first) === 0 &&
            this._second.localeCompare(t._second) === 0) return Comparable_.Equal;
        if (this._first.localeCompare(t._first) === 1 &&
            this._second.localeCompare(t._second) === 1) return Comparable_.GreaterThan;
    }

    …
}
Example (abstract nature of Complex class is kept…)
abstract class Complex implements Pair<number> {
    constructor(readonly _first: number, readonly _second: number) {
    }

    abstract compareTo(t: Pair<number>): Comparable_;

    …
}
Resource(s)
Lookup types allow the access to variable types from a compilation viewpoint. The keyof keyword complements this facility.
Example (base)
// Lookup type extracts type of property:
type EventTarget_or_null = Event["currentTarget"];  // Extracted type is 'EventTarget | null'...
const et: EventTarget_or_null = new EventTarget(); // No compilation error!
Example (keyof)
type Keys_of_Event = keyof Event; // "data" | "type" | ... as properties of 'Event' DOM type...
const me: Event = new Event("Franck's event");
let type_property: Keys_of_Event = "type";
window.alert(me[type_property]); // 'Event' objects effectively have a 'type' property... -> "Franck's event" is displayed...
// let property_does_not_exist: Keys_of_Event = "property_does_not_exist"; // Bingo! -> compilation error...
Example
function get_property<Indexed_type, Property_type extends keyof Indexed_type>(o: Indexed_type, p: Property_type): Indexed_type[Property_type] {
    return o[p];  // Inferred type (as return) is 'Indexed_type[Property_type]'
}
Resource(s)
Conditional types allow enhanced type checking based on, possibly, the infer keyword for type inference.
Example
type Never_never<Any_type> = Any_type extends Object ? Any_type : never;
const b: Never_never<boolean> = true; // No compilation error!
Example
type Primitive = boolean | number | string | symbol; // Bug: this does not include *ALL* primitive types from the TypeScript perspective...
// let wm: WeakMap<Primitive, any>; // Compilation error: by construction, 'WeakMap' does not accept primitive types...
// type AdaptiveMap<Key, Value> = Key extends Primitive ? Map<Key, Value> : WeakMap<Key, Value>; // Compilation error: Type 'Key' does not satisfy the constraint 'object'...
type AdaptiveMap<Key, Value> = Key extends object ? WeakMap<Key, Value> : Map<Key, Value>;
let am: AdaptiveMap<Event, any>; // 'WeakMap'
am.set(new Event(""), null);
let am_: AdaptiveMap<number, any>; // 'Map' -> This does not work with 'boolean' while *it works with other primitive types*, why?
am_.set(0, null);

infer

Rule(s)
  • infer applies within the extends clause of a conditional type. It “extracts” a type from generics, tuples…
Example (infer)
type Array_element_type<T> = T extends (infer Element_type)[] ? Element_type : T;
let b: Array_element_type<boolean>; // 'boolean' is not an array type...
b = true;
let n: Array_element_type<Array<number>>; // 'Array<number>' is an array type...
n = 0;
// Fairly similar:
type What_is_inside_type<I> = I extends Array<infer Element_type> ? Element_type : never;
let s: What_is_inside_type<string>; // 'string' is not an array type...
// s.constructor; // Compilation error: Property 'constructor' does not exist on type 'never'...
let s_: What_is_inside_type<Array<HTMLElement>>; // 'Array<HTMLElement>' is an array type...
s_.constructor; // 'HTMLElement' constructor function...
Example (tuple type)
type Second<T> = T extends [infer F, infer S, ...unknown[]] ? S : never;
type Forname_Surname_IsFemale = [string, string, boolean];
type Surname = Second<Forname_Surname_IsFemale>; // 'string' type...
const Barbier: Surname = "Barbier";
Resource(s)
See also
Utility types (here…) allow some kind of “type adaptation”.

ReturnType<T>

Rule(s)
  • ReturnType<T> computes the returned type of a function.
Example
class Kid extends Individual {
    …
    isResponsible(responsibility: Responsibility): boolean {
        return …;
    };
// 'typeof this.isResponsible' does not work:
    isNotResponsible(responsibility: Responsibility): ReturnType<typeof Kid.prototype.isResponsible> {
        return !this.isResponsible(responsibility);
    };
}
…
let Jules: Individual = new Kid();
let is_Jules_responsible_: ReturnType<typeof Jules.isResponsible> = Jules.isResponsible(some_responsibility);

ThisParameterType<T>

Rule(s)
  • ThisParameterType<T> extracts the type of this from a function.
Example
type Kid_bis_repetita = ThisParameterType<typeof Jules.isResponsible>;
let Julie: Kid_bis_repetita;
Resource(s)
As in JavaScript, functions play a first-class role in TypeScript. Functions both support optional and default parameters.
Rule(s)
Example
private _sobel_post_processing(canvas: HTMLCanvasElement, threshold: number = 100) { …

Overloading

Rule(s)
Example (problem)
get_texture(): any { // Compilation error resulting from conflict...
    return this._textures.get(NoLanguageCharacter_texture_type.Normal);
}
get_texture(type: NoLanguageCharacter_texture_type): any { // Compilation error resulting from conflict...
    return this._textures.get(type);
}
Example (solution)
public get_texture(type: NoLanguageCharacter_texture_type = NoLanguageCharacter_texture_type.Normal): any {
    return this._textures.get(type);
}

Variable number of arguments

Rule(s)
Example
class Lottery {
    public static Draw(bonus_number: number, ...numbers: Array<number>) /* Return type is inferred... */ {
    …
}
…
let result = Lottery.Draw(6, 43, 12, 3, 32, 34, 22);
window.console.assert(result.draw[0] === 43);

<const> cast

Rule(s)
Example
class Lottery {
    public static Draw(bonus_number: number, ...numbers: Array<number>) /* Return type is inferred... */ {
        return {
            bonus_number: bonus_number,
            date: Date.now(), // For simplicity
            draw: Array.from(numbers)
        }
    }
    public static Draw_(bonus_number: number, ...numbers: Array<number>) /* Return type is inferred... */ {
        return <const>{
            bonus_number: bonus_number,
            date: Date.now(), // For simplicity
            draw: Array.from(numbers)
        }
    }
}
…
// 'result' inferred type is: '{ bonus_number: number, date: Date, draw: Array<number> }'
let result = Lottery.Draw(6, 43, 12, 3, 32, 34, 22);
window.console.assert(result.draw[0] === 43);
result.date = Date.parse('10 Feb 2020 15:50:00 GMT');
result = Lottery.Draw_(6, 43, 12, 3, 32, 34, 22);
result.date = Date.parse('10 Feb 2020 15:50:00 GMT');
const result_ = Lottery.Draw_(6, 43, 12, 3, 32, 34, 22);
// result_.date = Date.parse('10 Feb 2020 15:50:00 GMT'); // Compilation error!
As in JavaScript, functions play a first-class role in TypeScript. Function types (here…) are the way of controlling how functions are safely signed, and therefore checked at calling places.
Rule(s)
Example
interface Main { // Similar to Java "functional interface" -> "function type" in TypeScript
    (): void; // Call signature
}
…
// Somewhere else:
let main: Main = () => { … }
Rule(s)
Example (call signature)
enum Comparable_ {LessThan, Equal, GreaterThan}

interface Comparable<T> { // Similar to Java "functional interface"
    (t: T): Comparable_; // Call signature
}
// Compiler error: "Type 'Giraffe' provides no match for the signature '(t: Giraffe): Comparable_'" (no solution exists!)
class Giraffe extends Mammal implements Comparable<Giraffe> {
    constructor(readonly _name: string, readonly _height: number) {
    }
}
Example (method signature)
enum Comparable_ {LessThan, Equal, GreaterThan}

interface Comparable<T> { // Similar to Java "functional interface"
    compareTo(t: T): Comparable_;
}

class Elephant extends Mammal implements Comparable<Elephant> {
    constructor(readonly _name: string, readonly _weight: number) {
        super();
    }

    compareTo(e: Elephant): Comparable_ { // 'public' by default
        if (this._weight < e._weight) return Comparable_.LessThan;
        if (this._weight === e._weight) return Comparable_.Equal;
        if (this._weight > e._weight) return Comparable_.GreaterThan;
    }
}
Rule(s)
Example
interface Comparator<T> {
    compare: (t1: T, t2, T) => Comparable_; // 'compare' has type '(t1: T, t2, T) => Comparable_'
}

class Zoo implements Comparator<Giraffe> {
    compare = function (g1: Giraffe, g2: Giraffe): Comparable_ {
        if (g1._height < g2._height) return Comparable_.LessThan;
        if (g1._height === g2._height) return Comparable_.Equal;
        if (g1._height > g2._height) return Comparable_.GreaterThan;
    };
}
Example
type Ready = (item: Open_Food_Facts_item) => void;
…
// Later on:
this._item_data = new Promise/*<Open_Food_Facts_item>*/((ready: Ready, problem) => { // 'problem' aims at being called when 'Promise' object has difficulty in achieving its job...
    …
    // 'ready' must be called with only one argument whose type is 'Open_Food_Facts_item'
    let offi: Open_Food_Facts_item = {} as Open_Food_Facts_item;
    …
    ready(offi);
});
Rule(s)
Example (type of a constructor function)
 // 'new' is the way for TypeScript to define the type signature of a constructor function:
type Constructor_function_type = new (bye: string, hello: string) => Greetings;

Generic function type

Example Inheritance_polymorphism.ts.zip 
let t_toString = <T>(t: T) => t.toString();

let identity = <T>(t: T): T => { // <=> 'function identity<T>(t: T): T {'
    return t;
}

let identity_alias: <U>(u: U) => U = identity; // Type of 'identity' and 'identity_alias' is '<T>(t: T) => T' <=> '<U>(u: U) => U'
…
// Calls later on:
window.alert("Zero: " + t_toString(0));
let result: string = identity("Franck Barbier"); // <=> 'identity<string>("Franck Barbier")'
result = identity_alias("Franck Barbier");
The notion of assertion function exists from TypeScript 3.7. Copying the assert spirit in Node.js for example, but beyond, TypeScript can check assertions at compilation time.
Example Miscellaneous.ts.zip 
class Assertion_function_example {
    handle(e: MouseEvent | TouchEvent) { // Ideally...
        if (e instanceof MouseEvent) /* Do something */ return;
        e.touches; // By construction, 'e' complies with 'TouchEvent'
    }

    handle_(e: UIEvent /* MouseEvent | TouchEvent */) { // Legacy signature?
        if (e instanceof MouseEvent) /* Do something */ return;
        window.console.assert(e instanceof TouchEvent); // For test...
        if (e instanceof TouchEvent === false) throw TypeError("'TouchEvent' expected..."); // For run-time...
        (e as TouchEvent).touches; // Cast probably succeeds due to immediate prior checking...
    }

    _e_is_TouchEvent(e: UIEvent): asserts e is TouchEvent { // The notion of "assertion function" from TypeScript 3.7...
        if (e instanceof TouchEvent === false) throw TypeError("'TouchEvent' expected...");
    }

    handle__(e: UIEvent /* MouseEvent | TouchEvent */) { // Use of TypeScript 3.7 'asserts'
        if (e instanceof MouseEvent) /* Do something */ return;
        window.console.assert(e instanceof TouchEvent); // For test...
        this._e_is_TouchEvent(e);
        // This may occur if and only if immediate prior call succeeds:
        e.touches; // TypeScript deduces that 'e instanceof TouchEvent' from 'asserts e is TouchEvent'
    }
}
TypeScript favors modules by means of import and export, but namespaces may increase the way of organizing code as well (see also here…).
Example Miscellaneous.ts.zip 
// 'Namespace_Forname.ts' file
namespace Forname {
    export const my_forname = "Joseph";
}
namespace Nickname {
    export const my_nickname = "Jojo";
}
// 'Namespace_Surname.ts' file
/// <reference path="./Namespace_Forname.ts" /> // 'Nickname' namespace is referenced...
namespace Surname {
    // 'export' required to access namespace's properties in 'window.alert':
    export const my_surname = "Barbier--Darnal";
}
namespace Nickname { // This namespace crosses 2 files...
    // 'export' required to access namespace's properties in 'window.alert':
    export const my_nickname_ = "Jo"; // Conflict with 'my_nickname'...
}
window.alert(Forname.my_forname + " " + Surname.my_surname + " as " + Nickname.my_nickname + " or " + Nickname.my_nickname_);
Rule(s)
<script src="./Namespace_Forname.js"></script>
<script src="./Namespace_Surname.js"></script>
In TypeScript, decorators (here…) look like Java annotations.
Rule(s)
Example (method) Miscellaneous.ts.zip 
const is_configurable = (value: boolean) => { // Decorator for method...
    return (target: any, method_name: string, descriptor: PropertyDescriptor) => {
        window.console.assert(method_name === "my_method"); // See usage below...
        descriptor.configurable = value;
    };
}

class My_class {
    common_public_instance_method() {
        window.console.log("Common public instance method...");
    }

    @is_configurable(false) // Remove default configurable nature...
    my_method() {
        window.console.log("'my_method' cannot be suppressed from class' prototype...");
    }
}

const mc = new My_class();
window.console.assert(delete My_class.prototype.common_public_instance_method); // 'true' since public instance methods are configurable...
try {
    mc.common_public_instance_method(); // No longer exists so error...
} catch (error/*: TypeError*/) { // Type annotation not (yet?) allowed in TypeScript...
    window.console.log((error as TypeError).message);
}
window.console.assert(delete My_class.prototype.my_method === false); // Great, 'my_method' cannot be deleted...
mc.my_method(); // It works!
Example (class) Miscellaneous.ts.zip 
type Constructor_function_type = new (bye: string, hello: string) => Greetings; // 'new' is the way for TypeScript to define the type signature of a constructor function...

function Italian(constructor: Constructor_function_type) {
    // ECMAScript 2015 class expression (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/class)
    // const MyClass = class [className] [extends otherClassName] {
    //     // class body
    // };
    return class /* [className] */ extends constructor {
        constructor(bye = "Ciao", hello = "Pronto") {
            super(bye, hello); // No change...
        }
    }
}

@Italian
class Greetings {
    constructor(bye = "Bye", hello = "Hello") {
        window.console.log(hello + "... " + bye);
    }
}

new Greetings(); // Decorator is called here...
Like JavaScript, TypeScript supports asynchronicity. Nonetheless, TypeScript typing system provides a graceful way asynchronicity may be put into practice.
Rule(s)
Example Carrefour_Items.Java.zip  (App. has to be launched from Java server side)
private readonly _item_data: Promise<Open_Food_Facts_item>;

private constructor(private readonly _gtin: string) { // '_gtin' *field* automatic inclusion
    // => this._gtin = _gtin; -> no need!
    this._item_data = new Promise((ready: Ready, problem) => { // 'problem' aims at being called when 'Promise' object has difficulty in achieving its job...
        this._get_Open_Food_Facts_item(ready, problem);
    });
};

public async get_Open_Food_Facts_item() { // So returned type is inferred from 'async'...
    return await this._item_data; // Wait for '_get_item_data' (i.e., 'ready(offi);')
}

private _get_Open_Food_Facts_item(ready: Ready, problem, time_out?: number): void { // Call may omit 'time_out' optional parameter
…