TypeScript


Creative Commons License
This -TypeScript- tutorial is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License
Preamble

This tutorial is about basic and advanced features of TypeScript. As a superset of JavaScript in general and JavaScript 6 in particular, any JavaScript code complies with TypeScript subject to type checking rules. Tutorial on JavaScript may be found here

Headlines
TypeScript compilation and execution

Rule(s)

Note that npm run construction runs the scripts.construction field in package.json while npm run install runs scripts.install (if any). Execution: npm run execution.

The tsc --init compilation directive creates a tsconfig.json file.

Otherwise, simple compilation is as follows: tsc Palindrome.ts <compilation options>. For example, tsc Palindrome.ts --watch (-w) compilation option installs a daemon, which re-launches compilation at source file modification time.

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)

See also here

Example (tsconfig.json file)

"moduleResolution": "node", // Alternative is "classic", but it is not recommended...
"target": "ES6", // The most reasonable choice...

Rule(s)

Example (tsconfig.json file and later on TypeScript code)

"noImplicitAny": true,
declare const dat: any; // 'any' is imposed by '"noImplicitAny": true'
Note that dat.gui is a Google library, which has no TypeScript support.

Rule(s)

Example (PURE 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", // Based on ES6 'import', 'export'...
    "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": [ // Default...
  //    "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...
  ]
}

Rule(s)

JavaScript reminder

Rule(s)

Example

if(expression) {} // This evaluates to 'true' if 'expression' is not:

        null

        undefined

        NaN

        "" // Empty string

        0

        false

Rule(s)

Example

// 'A || B' returns the value 'A' if 'A' can be coerced into 'true'; it returns 'B' otherwise...
const port = process.env['PORT'] || 8080;
// Alternative:
const port = process.env['PORT'] ? process.env['PORT'] : 8080;
TypeScript fundamentally performs static type checking as does any compiler.

Rule(s)

Example Base.ts.zip 

const re: RegExp = /^[A-Z]{3}$/; // 'RegExp' native type...
let bye: string = "BYE"; // 'string' primitive type...
console.assert(bye.match(re) !== null); // Exactly 3 capital letters...
bye = "CIAO";
console.assert(bye.match(re) === null);
const a: Array<boolean> = new Array();
a.push(false);
// a.push(bye); // Compilation error... but JavaScript execution works!
var versus let versus const

Rule(s)

Example (PURE JavaScript)

"use strict";
var well_declared_variable = "Strict mode is activated from all statement lines in the rest of this source file!";
ill_declared_variable = 2016; // The absence of 'var' raises an error at execution time thanks to the strict mode

Rule(s)

Example (PURE JavaScript)

const my_const = "It cannot change!";
my_const = "One says it cannot change!"; // Execution error
if(something) {
    let this_year = 2017; 
} // <- 'this_year' dies right there while 'var' (contrary to 'let') makes the variable living throughout its declaration function

Rule(s)

Example

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

const lasagna = {country: "Italy"}; // 'lasagna' has the type '{country: string}' (inferred)
lasagna.country = "France"; // 'const' has precise scope...
const foie_gras = {country: "France"} as const; // 'const' cast...
// 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!
TypeScript reuses JavaScript primitive types (boolean, number, bigint, string, undefined, and symbol).

Example

console.log(typeof true); // 'boolean'
console.log(typeof 2016); // 'number'
console.log(typeof "2016"); // 'string'
const Franck: string = "Franck";
console.log(`Hello ${Franck}`); // JavaScript 6 backquotes!

Rule(s)

Example

const my_bigint = 1234567890123456789012345678901234567890n; // Note 'n' suffix!

See also

null and undefined

Rule(s)

Example Base.ts.zip 

console.assert(null == undefined);
console.assert(null !== undefined);
let undefined_variable;
console.log(typeof undefined_variable); // 'undefined' is displayed...
console.log(typeof null); // 'object' is displayed...

See also

Primitive versus native constructed types

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
console.log(Number.MAX_VALUE);
const one: number = Number.parseInt("00000001", 2); // ES2015
if (Number.isNaN(one)) console.log('Number.isNaN(one)');
else console.log('one: ' + one); // '1'
const two: Number = new Number(1 << 1);
console.log('two(2): ' + two.toString(2)); // '10'
const zero: Number = new Number(1 >> 1);
console.log('zero: ' + zero); // '0'

Rule(s)

Example

/** JavaScript bitwise operators apply on the first significant 32 bits only! */
// How negative numbers are represented:
const minus_nine: Number = new Number((~9) | 1); // '00000000000000000000000000001001' -> '11111111111111111111111111110110' -> '11111111111111111111111111110111'
console.log('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!)
console.log('-1 >>> 0: ' + x_.toString(2)); // '11111111111111111111111111111111' -> (2^32 - 1) -> 4294967295

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

console.log(Number.MAX_VALUE * 2 === Number.POSITIVE_INFINITY); // 'true'

console.log(isNaN(Math.sqrt(-1))); // 'true'

See also

Object type

Rule(s)

Example Base.ts.zip 

let o = {a: "a"};
console.assert(o.constructor === Object); // 'Object' function...
console.assert(Object.getPrototypeOf(o) === Object.prototype); // "Prototype inheritance"...

Rule(s)

Example

let o_ = Object.create(Object.prototype); // <=> 'var o_ = {};'
console.log(typeof o_); // 'object'

JavaScript objects may grow and slim!

Example (PURE JavaScript)

let Franck = {};
Object.defineProperty(Franck, "surname", {value: "Barbier", enumerable: true, configurable: true, writable: true});
Object.defineProperty(Franck, "nickname", {value: "Bab", enumerable: true, configurable: false, writable: false});
try {
    Franck.nickname = "Other nickname";
} catch (error) { // JavaScript, bug:
    console.log(error.message); // '"nickname" is read-only'
}
try {
    delete Franck.nickname;
} catch (error) { // JavaScript, bug:
    console.log(error.message); // 'property "nickname" is non-configurable and can't be deleted'
}
Franck.skill = "JavaScript"; // Dynamic extension
console.log(JSON.stringify(Franck)); // '{"surname":"Barbier","nickname":"Bab","skill":"JavaScript"}'
delete Franck.skill; // Dynamic suppression
if ("skill" in Franck === false)
    console.log("'Franck.skill' no longer exists...");

PropertyDescriptor

Example Base.ts.zip 

let non_configurable = {boring: "C'est la MAAF que je préfère..."};
Object.seal(non_configurable);
/* Bug in JavaScript ('property "boring" is non-configurable and can't be deleted')
and compilation error in TypeScript ('The operand of a 'delete' operator must be optional.'): */
// delete non_configurable.boring;

Example Base.ts.zip 

const UK = {};
Object.defineProperty(UK, "prime minister", {
    value: "Boris Johnson",
    enumerable: true,
    configurable: false,
    writable: false
});
Object.defineProperty(UK, "secret intelligence service head", {
    value: "Richard Moore",
    enumerable: true,
    configurable: false,
    writable: false
});
Object.defineProperty(UK, "secret intelligence service 007", {
    value: "James Bond",
    enumerable: false,
    configurable: false,
    writable: false
});
console.assert(UK.propertyIsEnumerable("constructor") === false);
console.assert(UK.propertyIsEnumerable("secret intelligence service 007") === false);

for (const property in UK) {
  // '"suppressImplicitAnyIndexErrors": true':
  console.log(`${property}: ${UK[property]}`); // "prime minister" and "secret intelligence service head" only...
}

Predefined objects (Document Object Model)

Rule(s)

  • JavaScript offers predefined objects with (possible) predefined properties: attributes and/or functions.
  • window with alert, confirm… base functions in the browser.
  • console, window.document, window.JSON, window.Math, window.navigator (here…), window.onload (function), window.screen (here…)… in the browser.

Example

window.onload = we_start;
…
// Somewhere else:
function we_start() {
    console.log("All resources are available including downloaded images…");
    …

Example (window.screen)

this._map = window.document.getElementById('map'); // Get the display area (DOM must be ready)...
this._map.style.height = window.screen.height + "px";

Example (window.JSON)

// From 'Object' to 'String':
let my_string = window.JSON.stringify(my_object);
// From 'String' to 'Object':
let my_object = window.JSON.parse(my_string);

Example (window.Math)

window.alert(window.JSON.stringify(window.location.hostname)); // '"localhost"' may be displayed
window.alert(window.Math.E); // '2.718281828459045' is displayed
window.alert(window.Math.ceil(Math.E)); // '3' is displayed
Native versus user-defined constructed types (reminder on JavaScript constructed types)

Rule(s)

Example (PURE JavaScript, constructor function)

const Dish = function (main_ingredient) {
    this.ingredients = [];
    this.main_ingredient = main_ingredient;
    this.add_ingredient = function (ingredient) {
        this.ingredients.push(ingredient);
    };
};
const lasagna = new Dish(Symbol("Pasta"));
console.log(lasagna.main_ingredient); // 'Symbol(Pasta)' is displayed
lasagna.add_ingredient(Symbol("Cheese"));
console.assert(lasagna.constructor === Dish);
console.assert(Object.getPrototypeOf(lasagna) === Dish.prototype); // "Prototype inheritance"...
console.log(Dish.prototype.constructor); // Constructor function
console.assert(Dish.prototype.constructor.name === "Dish");

Example (PURE JavaScript, class)

class Dish_ { // From JavaScript 6...
    constructor(main_ingredient) {
        this.ingredients = [];
        this.main_ingredient = main_ingredient;
    }
    add_ingredient(ingredient) {
        this.ingredients.push(ingredient);
    };
}
const lasagna_ = new Dish_(Symbol("Pasta"));

Example

class Dish {
    private ingredients: Array<symbol> = new Array();
    private main_ingredient: symbol;
    public constructor(main_ingredient: symbol) {
        this.main_ingredient = main_ingredient;
    }
    public add_ingredient(ingredient: symbol) {
        this.ingredients.push(ingredient);
    };
}
const lasagna = new Dish(Symbol("Pasta"));
The case of this

Rule(s)

Example (PURE JavaScript)

const o = {};
Object.defineProperty(o, "simple_array", {
    value: [null, 0, "ABC"],
    enumerable: true,
    configurable: true,
    writable: true
});

o.simple_array_processing = function () {
    for (let i = 0; i < this.simple_array.length; i++)
        this.simple_array[i] = null; // Great, 'this' is set to the appropriate "current" object, i.e., 'o'!
};
o.simple_array_processing(); // Call...
console.log(JSON.stringify(o.simple_array)); // '[null,null,null]'

o.simple_array_processing = function () { // Erase prior version...
    this.simple_array.forEach(function (element) {
        console.assert(this !== o);
        console.log("What is 'this'? " + Object.getOwnPropertyNames(this).join(" - "));
    } /* ,this */); // Uncommenting leads to 'this === o'
};
o.simple_array_processing(); // Call...

o.simple_array_processing = function () { // Erase prior version...
    this.simple_array.forEach(element => { // 'this' in lambda expression is set *IN A STATIC WAY*
        console.assert(this === o);
        console.log("What is 'this'? " + Object.getOwnPropertyNames(this).join(" - "));
    } /* ,this */); // No need of injecting 'this' as second argument of 'forEach'
};
o.simple_array_processing(); // Call...

Rule(s)

Example (PURE JavaScript)

const my_iteration = function (element) {
    console.assert(this === o);
    console.log("What is 'this'? " + Object.getOwnPropertyNames(this).join(" - "));
};
o.simple_array_processing = function () { // Erase prior version...
    this.simple_array.forEach(my_iteration.bind(this));
};
o.simple_array_processing(); // Call...
TypeScript introduces compilation-based types outside the scope of JavaScript for static type checking.

Rule(s)

Example (null, undefined, and void)

let x: undefined = undefined;
let y: null = null;
function f(): void {
    return undefined;
}

Example (any)

let my_var: any;
console.assert(my_var === undefined, "Note that 'undefined' is both the type and its singleton value!");
my_var = {given_name: "Franck"};
console.assert(my_var.given_name === "Franck", "No compilation error...");

Rule(s)

Example

let my_other_var: unknown;
my_other_var = {given_name: "Franck"};
// 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 possess_given_name(o: any): o is { given_name: string } { // Type guard: type predicate is 'o is { given_name: string }'
    return 'given_name' in o;
}
if (possess_given_name(my_other_var))
    console.log(my_other_var.given_name); // No compilation error...

Rule(s)

Example (weak) Base.ts.zip 

const ws = new WebSocket("ws:127.0.0.1:1963", "FranckBarbier");
ws.onmessage = (event: MessageEvent) => {
    const d: Data = JSON.parse(event.data); // What's inside 'event.data'?
};

Example (strong) Base.ts.zip 

interface _Data {
    high_quality: boolean;
}
type Data = _Data; // For pedagogy only
function isData(d: any): d is Data {
    return 'high_quality' in d;
}
…
const ws = new WebSocket("ws:127.0.0.1:1963", "FranckBarbier");
ws.onmessage = (event: MessageEvent) => {
    const d: any = JSON.parse(event.data);
    if (isData(d)) { // Allow access to 'high_quality' at *COMPILATION TIME*:
        const high_quality_: boolean = d.high_quality;
    }
}

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 

console.assert(this._scene.getObjectByName('dlh')!.matrixAutoUpdate === false);

Rule(s)

Example

"strictPropertyInitialization": true,
…
class Student {
// Initialization must occur in constructor if 'strictPropertyInitialization' is set to 'true' else compilation errors:
    private _citizen_id: string;
    private _student_id: string;
    …
}

Example (alternative)

class Student {
    private _citizen_id!: string; // No initialization required by the compiler while 'strictPropertyInitialization' is set to 'true'...
    private _student_id: string;
    …
}

See also

Contrary to JavaScript, TypeScript has a native support for enumerated types.

Example (PURE JavaScript)

const Medal = Object.freeze({ // No new field, no removable field, no writability, etc.
    Gold: Symbol("Gold"),
    Silver: Symbol("Silver"),
    Bronze: Symbol("Bronze")
});
console.assert(Object.isFrozen(Medal));
let award = Medal.Gold;
console.log(award !== Symbol("Gold")); // 'true' because 'Symbol' objects are unique...

Example

enum Medal {
    Gold = "Gold",
    Silver = "Silver",
    Bronze = "Bronze"
}
let award: Medal = Medal.Gold;
console.log(award); // 'Gold'

Example

enum Gateway_type {
    BPMN_complexGateway = 'bpmn:complexGateway',
    BPMN_eventBasedGateway = 'bpmn:eventBasedGateway',
    BPMN_exclusiveGateway = 'bpmn:exclusiveGateway',
    BPMN_inclusiveGateway = 'bpmn:inclusiveGateway',
    BPMN_parallelGateway = 'bpmn:parallelGateway'
}
…
if (!Object.values(Gateway_type).includes(gateway.$type as Gateway_type)) // 'values' => ES2017!
    throw new Error('Invalid gateway type among ' + JSON.stringify(Gateway_type));
The way export and import work in TypeScript is similar to JavaScript 6.

Rule(s)

Example

<!DOCTYPE html>
<html>
    <head>
        …
        <script src="js/chai.js"></script>
        <script src="js/dat.gui.min.js"></script>
        <!-- Loading based on Content Delivery Network (CDN): -->
        <script src="https://code.createjs.com/createjs-2015.11.26.min.js"></script>
        <!-- My stuff: -->
        <script src="js/Homemade_code.js"></script>
        <script>
            if (typeof chai === "undefined")
                window.alert("STOP: it won't work because 'chai.js' has not been loaded…");
            if (typeof dat === "undefined")
                window.alert("STOP: it won't work because 'dat.gui.min.js' has not been loaded…");
            if (typeof createjs === "undefined")
                window.alert("STOP: it won't work because 'createjs-2015.11.26.min.js' has not been loaded…");
            window.onload = go; // 'go' function must exist in 'js/Homemade_code.js' file...
        </script>
    </head>
    <body >
        <canvas id="my_canvas" width="600" height="400"></canvas>
    </body>
</html>

Rule(s)

Example (PURE JavaScript, using RequireJS)

<!-- 'data-main' attribute tells 'require.js' to load 'UV_mapping_three.js' after 'require.js' loads... -->
<!-- *By construction*, 'UV_mapping_three.js' is loaded in an asynchronous way by 'require.js' -->
<!-- Here, 'async' just embodies the asynchronous loading of 'require.js' itself! -->
<script async data-main="./js/UV_mapping_three" src="./js/require.js"></script>
requirejs(['three'], function (library) {
    window.THREE = library;
    // While 'three.js' may be used from here, one has however to check that the DOM and/or the full window (images, sounds, videos…) are/is loaded.  
});

Rule(s)

Example (PURE JavaScript) UV_mapping_three_js.zip 

<script src="./js/UV_mapping_three.js" type="module"></script>
/** FB_ei.js */
// 'use strict'; // No need when imported...
export var author = {value: "Franck Barbier"};
export default getAuthor; // Only one default export, i.e., import does not require braces!
function getAuthor() {
    return author.value;
};
/** Authoring_ei.js */
// 'use strict'; // No need when imported...
import {author as developer} from "./FB_ei.js"; // '.js' is mandatory!
import getAuthor from "./FB_ei.js";
export {Authoring}; // <=> {Authoring as Authoring};
var Authoring = function () {
    this._author = developer.value ? developer.value : getAuthor();
    …
};

Example (PURE JavaScript)

export class C {…} // 'export' must be synch with class declaration!

Rule(s)

Example (PURE JavaScript) Export_import.js.zip 

<script src="./js/C.js" type="module"></script>
// 'A.js' file
export let A = "A";
// 'B.js' file
import {A as proxy} from "./A.js";

export let A = proxy;
// 'C.js' file
import {A} from "./B.js";

window.alert("'A' in 'C.js': " + A); // "A" is displayed...

Rule(s)

Example

// 'index.ts' file is imported and contained code is executed:
import './persistence'; // Make database accessible and running...

Example (Node.js)

import * as HTTP from "http"; // Pure TypeScript style...
From “legacy” JavaScript to TypeScript

Rule(s)

Example (package.json and next TypeScript)

"dependencies": {…, "lodash": "^4.17.20", …},
"devDependencies": {…, "@types/lodash": "^4.14.168", …},
 import {pick} from "lodash";

Rule(s)

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'...
console.assert(typeof dat !== "undefined", "Problem: it won't work because 'dat.gui.min.js' library has not been loaded...");

Rule(s)

Example Covid-19_three_js.ts.zip 

// @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.

Rule(s)

Example (package.json)

"typings": "dist/API.d.ts",
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) {
    console.log(event.button); // No compilation error...
}
// Versus:
window.onscroll = function (event: UIEvent) {
    // console.log(event.button); // Compilation error: 'UIEvent' type has no 'button' property...
}

Example (weird!)

class C {
    a: boolean;
    b: C['a']; // Type of 'b' is that of 'a', i.e., 'boolean'!
}
As a strongly typed programming language, TypeScript promotes generic types along with predefined generic collections (Array<T>, Set<E>, Map<K,V>…) that are wrappers of JavaScript non-generic collections.

Rule(s)

Example (PURE JavaScript) Miscellaneous.js.zip 

let matrix = [[1, 2], [1, 2]];
let norm = 0;
matrix.forEach(function (element) {
    norm += element.reduce(function (sum, element) {
        return sum + element ** 2; // JavaScript 7 only! -> 'return sum + Math.pow(element, 2);'
    });
});
window.alert("Norm: ('forEach')" + Math.sqrt(norm));

norm = 0;
for (let element of matrix) {
    norm += element.reduce(function (sum, element) {
        return sum + element ** 2; // JavaScript 7 only! -> 'return sum + Math.pow(element, 2);'
    });
}
window.alert("Norm ('let'): " + Math.sqrt(norm));

const chromosomes = ["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); // '3' is displayed!

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

const genetics = [...chromosomes, ...diseases];
window.alert(JSON.stringify(genetics)); // '["Genetic female - XX","Genetic male - XY","Turner's syndrome - X","Klinefelter syndrome - XXY"]' is displayed!

const [given_name, surname] = ['Franck', 'Barbier'];
window.alert(typeof given_name + ": " + given_name); // 'string: Franck' is displayed...

const fb = ['Fr', 'Ba'];
const [gv = '?', sn = '?', nn = '?'] = fb;
window.alert(gv); // 'Fr' is displayed...

Rule(s)

Example (PURE JavaScript, inheriting from JavaScript Set) Set.js.zip 

class Elephant {
    constructor(name) {
        this._name = name;
    }
    toString() {
        return window.JSON.stringify(this);
    }
}

class My_set extends Set {
    constructor(ms) {
        super();
        if (ms === undefined)
            return;
        console.assert(ms instanceof My_set);
        ms.forEach(element => {
            this.add(element);
        });
    }
    strict_subset(ms) { // The "current" set is strictly included in 'ms'...
        console.assert(ms instanceof My_set);
        for (const element of this)
            if (!ms.has(element))
                return false;
        return ms !== this && ms.size !== this.size;
    }
    symmetrical_difference(ms) { // All stuff that is neither in the one nor the other...
        console.assert(ms instanceof My_set);
        const symmetrical_difference = new My_set(this);
        ms.forEach(element => {
            symmetrical_difference.add(element); // Union
        });
        const intersection = new My_set();
        ms.forEach(element => {
            if (this.has(element))
                intersection.add(element);
        });
        // Formally, symmetrical difference is "union - intersection':
        intersection.forEach(element => {
            symmetrical_difference.delete(element);
        });
        return symmetrical_difference;
    }
}

window.onload = function () {
    let Babar = null, Dumbo = null, Jumbo = null;
    const zoo = new My_set();
    zoo.add(Babar);
    zoo.add(Dumbo);
    zoo.add(Jumbo);
    console.log("'zoo.size' ** 1: " + zoo.size);
    Babar = new Elephant("Babar");
    console.log("'zoo.size' ** 2: " + zoo.size);
    zoo.add(Babar);
    zoo.add(Babar);
    const Babar_bis = new Elephant("Babar");
    zoo.add(Babar_bis);
    console.log("'zoo.size' ** 3: " + zoo.size);
    Dumbo = new Elephant("Dumbo");
    Jumbo = new Elephant("Jumbo");
    const zoo_annex = new My_set();
    zoo_annex.add(Babar);
    zoo_annex.add(Dumbo);
    zoo_annex.add(Jumbo);
    const symmetrical_difference = zoo.symmetrical_difference(zoo_annex);
    console.log("'zoo.symmetrical_difference(zoo_annex)': ");
    symmetrical_difference.forEach(element => {
        console.log(element + " ");
    });
    console.log("\n'zoo.strict_subset(zoo_annex)' ** 1: " + zoo.strict_subset(zoo_annex));
    console.log("'zoo_annex.strict_subset(zoo)' ** 1: " + zoo_annex.strict_subset(zoo));
    zoo.delete(null);
    zoo.delete(Babar_bis);
    console.log("\n'zoo.strict_subset(zoo_annex)' ** 2: " + zoo.strict_subset(zoo_annex));
    console.log("'zoo_annex.strict_subset(zoo)' ** 2: " + zoo_annex.strict_subset(zoo));
};

Example (inheriting from TypeScript Set<E>) Set.ts.zip 

class Elephant {
    constructor(private readonly _name: string) {
    }
    toString() {
        return JSON.stringify(this);
    }
}

class My_set<E> extends Set<E> {
    constructor(s?: Set<E>) {
        super();
        if (s === undefined)
            return;
        s.forEach((element: E) => {
            this.add(element);
        });
    }
    strict_subset(ms: My_set<E>) { // The "current" set is strictly included in 'ms'...
        for (const element of this)
            if (!ms.has(element))
                return false;
        return ms !== this && ms.size !== this.size;
    }
    symmetrical_difference(ms: My_set<E>) { // All stuff that is neither in the one nor the other...
        const symmetrical_difference: My_set<E> = new My_set(this);
        ms.forEach((element: E) => {
            symmetrical_difference.add(element); // Union
        });
        const intersection: My_set<E> = new My_set();
        ms.forEach((element: E) => {
            if (this.has(element))
                intersection.add(element);
        });
        // Formally, symmetrical difference is "union - intersection':
        intersection.forEach((element: E) => {
            symmetrical_difference.delete(element);
        });
        return symmetrical_difference;
    }
}

(function() {
    let Babar = null, Dumbo = null, Jumbo = null;
    const zoo: My_set<Elephant> = new My_set();
    zoo.add(Babar); // Compilation error with '"strict": true'...
    zoo.add(Dumbo); // Compilation error with '"strict": true'...
    zoo.add(Jumbo); // Compilation error with '"strict": true'...
    console.log("'zoo.size' ** 1: " + zoo.size);
    Babar = new Elephant("Babar");
    console.log("'zoo.size' ** 2: " + zoo.size);
    zoo.add(Babar);
    zoo.add(Babar);
    const Babar_bis = new Elephant("Babar");
    zoo.add(Babar_bis);
    console.log("'zoo.size' ** 3: " + zoo.size);
    Dumbo = new Elephant("Dumbo");
    Jumbo = new Elephant("Jumbo");
    const zoo_annex: My_set<Elephant> = new My_set();
    zoo_annex.add(Babar);
    zoo_annex.add(Dumbo);
    zoo_annex.add(Jumbo);
    const symmetrical_difference = zoo.symmetrical_difference(zoo_annex);
    console.log("'zoo.symmetrical_difference(zoo_annex)': ");
    symmetrical_difference.forEach(element => {
        console.log(element + " ");
    });
    console.log("\n'zoo.strict_subset(zoo_annex)' ** 1: " + zoo.strict_subset(zoo_annex));
    console.log("'zoo_annex.strict_subset(zoo)' ** 1: " + zoo_annex.strict_subset(zoo));
    zoo.delete(null); // Compilation error with '"strict": true'...
    zoo.delete(Babar_bis);
    console.log("\n'zoo.strict_subset(zoo_annex)' ** 2: " + zoo.strict_subset(zoo_annex));
    console.log("'zoo_annex.strict_subset(zoo)' ** 2: " + zoo_annex.strict_subset(zoo));
})();

Rule(s)

Example (JavaScript WeakMap versus Map)

// const polynomial = new WeakMap();
// polynomial.set(1, -12); // Bug...
// polynomial.set(39, 8);  // Bug...
const polynomial = new Map();
polynomial.set(1, -12); // OK...
polynomial.set(39, 8);  // OK...
window.alert(polynomial.get(39)); // '8'

Example (TypeScript Map<K,V>)

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();

Rule(s)

Example currencies.react.zip 

static _Links = [
    {
        path: '/' + 'Currencies',
        component: CurrenciesWrapper,
        data: {material_icon: 'payment'}
    },
    {
        path: '/' + 'New',
        component: CurrenciesController,
        data: {material_icon: 'fiber_new'}
    },
    {
        path: '/' + 'Currencies' + '/' + `${Currencies.Currencies[Currencies.Dollar].iso_code}`,
        component: CurrenciesInformation,
        data: {iso_code: Currencies.Currencies[Currencies.Dollar].iso_code, material_icon: 'attach_money'}
    },
    {
        path: '/' + 'Currencies' + '/' + `${Currencies.Currencies[Currencies.Euro].iso_code}`,
        component: CurrenciesInformation_,
        data: {iso_code: Currencies.Currencies[Currencies.Euro].iso_code, material_icon: 'euro_symbol'}
    }
];
static _Components = [...new Set(CurrenciesMenu._Links.map(link => link.component))]; // 3 elements in the (re-computed) array...

Rule(s)

Example Base.ts.zip 

const h = function (a = -1, ...bs: number[] /*, c*/ /* <- error! */) {
    let sum = a;
    bs.forEach(b => {
        console.log("b: " + b);
        sum += b;
    });
    console.log("Sum: " + sum);
};
// Note that default value for 'a' is useless:
h(1, 2, 3); // 'Sum: 6' is displayed, i.e., '1' is substituted for 'a'
h(...[1, 2, 3]); // <=> 'h(1, 2, 3);'
Like Python typically, TypeScript offers a native support for tuples.

Example Miscellaneous.ts.zip 

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...
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...

Rule(s)

Example Miscellaneous.ts.zip 

const chromosomes_: ReadonlyArray<string> = ["Genetic female - XX", "Genetic male - XY"];
// chromosomes_.push("YY"); // Compilation error, content cannot be changed...
/* function switch_first_and_last<T>(array: readonly T[]): void {
    console.assert(array && array.length > 0);
    const temp: T = array[0];
    array[0] = array[array.length - 1]; // Compilation error because of 'readonly'...
    array[array.length - 1] = temp; // Compilation error because of 'readonly'...
} */
// Immutable tuple:
const center: readonly [number, number] = [0, 0];
Like JavaScript 6, TypeScript uses the class keyword for user-defined types. Contrary to JavaScript 6, TypeScript natively supports the notion of interface (reminder on JavaScript OO programming).

Rule(s)

Classes

Example N_INSEE.ts.zip 

class N_INSEE {

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

    calcul_clef(): boolean { // Any method is by default 'public'...
        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");
};

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 of inheritance tree (in French)

Example Inheritance_polymorphism.ts.zip 

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 Inheritance_polymorphism.ts.zip 

/** 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)...
    }
}

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 Inheritance_polymorphism.ts.zip 

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 Inheritance_polymorphism.ts.zip 

class Female extends Human_being {

    cloning(): Female {
        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:
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...
            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...
        console.assert(person === this);
        this.identities.forEach(function (element) {
            window.alert(element);
        }.bind(this)); // This guarantees 'console.assert(person === this)'
    }
};

person.display();
TypeScript has a limited support for exception management.

Rule(s)

Example Temperature.js.zip 
try {
    var t1 = new Temperature(-1, Temperature_unit.Kelvin); // 'let' is better!
} catch (ite) {
    window.alert(ite._message + ": physics' laws prevent -1°K...");
    throw ite; // This means that the execution context has no way of correcting the defect...
} finally {
    window.alert("'finally' as in Java...");
}

Rule(s)

Example (user-defined exception type) Temperature.ts.zip 

class Invalid_temperature_exception extends Error { // Inheriting from JavaScript root type for exceptions...
    constructor(private readonly _value, ...params) {
        // Pass remaining arguments to parent constructor:
        super(...params)
    }
// 'message' is an attribute inside 'Error' and thus cannot be overridden as a method here:
    public getMessage(): string {
        return this.constructor.name + ": " + this._value;
    }
}

Example (throwing) Temperature.ts.zip 

public decrement(): never | void { // 'Temperature' instance method...
    this._value -= this._step;
    if (this._value < Temperature.Min) {
        throw new Invalid_temperature_exception(this._value);
    }
    console.assert(this._value >= Temperature.Min);
}

Example (catching) Temperature.ts.zip 

const t__ = new Temperature(0., Temperature_unit.Kelvin);
// try {
//     t__.decrement();
// } catch (ite: any) { // Eventually, no type checking...
//     window.confirm(ite.getMessage());
// }
try {
    t__.decrement();
} catch (ite: unknown) { // Signature is from TypeScript 4.x only!
    window.confirm((ite as Invalid_temperature_exception).getMessage()); // Cast is imposed by 'unknown'...
}
TypeScript offers the notions of union and intersection types (here…).

Example (union)

let my_var: number | string | null = null;

Example (union) Intersection_union_type.ts.zip 

interface Flying_fox {
    fly: () => void;
}
interface Fox {
    run: () => void;
}
const Observe = function (f: Flying_fox | Fox): void {
    if ('fly' in f) {
        f.fly();
    }
};

Example (intersection) Intersection_union_type.ts.zip 

interface Female { highly_skilled: boolean }
class Business_woman implements Female { highly_skilled = true; /* Implementation */ }
class /* 'interface' also works... */ Manager { /* Empty for test */ }

type FemaleAndManager = Female & Manager;
const fm: FemaleAndManager = new Business_woman;
console.assert(fm.highly_skilled);

Example (multiple inheritance) Intersection_union_type.ts.zip 

interface Animal {
    children: Array<Animal>;
}
class Mammal implements Animal {
    children: Array<Animal> = new Array(); /* Implementation */
}
class Oviparous implements Animal {
    children: Array<Animal> = new Array(); /* Implementation */
    // eggs: any; // Compilation error!
}
const o: Mammal & Oviparous = new Mammal; // Property 'eggs' is missing in type 'Mammal' but required in type 'Oviparous'.
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.";
}
TypeScript supports, in Java-like style, genericity for classes, the notion of “interface”, and generic interfaces as well.

Generic class

Rule(s)

Example Inheritance_polymorphism.ts.zip 

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;
                }
            }
        }
    }
}

Interface

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 Palindrome.ts.zip 

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 Palindrome.ts.zip 

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…) Palindrome.ts.zip 

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_;

    …
}

Default value for parameterizing types in generics

Rule(s)

Example

class Garden<V extends Vegetable = Broccoli> {
    private readonly _ground: Set<V> = new Set();

    public plant(v: V) {
        this._ground.add(v);
    }
}
…
const g = new Garden; // Inferred type is 'Garden<Broccoli>'
// g.plant(new Tomato); // Compilation error...

See also

Lookup types allow the access to variable types from a compilation viewpoint. The keyof keyword complements this facility.

Example (base) Miscellaneous.ts.zip 

// 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) Miscellaneous.ts.zip 

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 Miscellaneous.ts.zip 

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]'
}
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 Miscellaneous.ts.zip 

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) Miscellaneous.ts.zip 

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) Miscellaneous.ts.zip 

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";

See also

Utility types (here…) allow some kind of “type adaptation”.

ReturnType<T> Miscellaneous.ts.zip 

Rule(s)

  • ReturnType<T> computes the returned type of a function (see also: here…).

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> Miscellaneous.ts.zip 

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;

Omit<T, Ks>

Rule(s)

  • Omit<T, Ks> constructs a type by picking all properties from T and then removing Ks.

Example

type P = Omit<Prisoner, 'given_name' | 'surname'>;
// import * as _ from 'lodash';
const p: P = _.omit(prisoner, ['given_name', 'surname']);
As in JavaScript, functions play a first-class role in TypeScript (reminder on JavaScript functions).

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);
}

Rule(s)

Example (solution)

// Overloads => multiple signatures without implementation:
public static Delete(motive: Mongoose.LeanDocument<Motive>): Promise<Mongoose.LeanDocument<Motive>>;
public static Delete(motive_number: string): Promise<string>;
// Implementation by means of union types:
public static Delete(m: Mongoose.LeanDocument<Motive> | string) {
    return typeof m === "string" ?
        MotiveModel.deleteOne({motive_number: m}).exec() :
        MotiveModel.deleteOne(m).exec();
}

Example (using conditional types…) Conditional_type.ts.zip 

Variable number of arguments

Rule(s)

Example Lottery.ts.zip 

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);
console.assert(result.draw[0] === 43);

<const> cast

Rule(s)

Example Lottery.ts.zip 

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>{ // On object literal here…
            bonus_number: bonus_number,
            date: Date.now(), // For simplicity
            draw: Array.from(numbers)
        }
    }
}

let result = Lottery.Draw(6, 43, 12, 3, 32, 34, 22);
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' inferred type is: '{ bonus_number: number, date: Date, draw: Array<number> }'
// so 'date' *CAN BE* changed because of inferred type:
result.date = Date.parse('10 Feb 2020 15:50:00 GMT');
// 'result_' inferred type is: '{ readonly bonus_number: number, readonly date: Date, readonly draw: Array }'
const result_ = Lottery.Draw_(6, 43, 12, 3, 32, 34, 22);
// so 'date' *CANNOT BE* changed because of inferred type:
// result_.date = Date.parse('10 Feb 2020 15:50:00 GMT'); // Compilation error!
Function types (here…) are the way of controlling how functions are rigorously 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;
        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;
        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)

Example (generated JavaScript files are loaded in sequence)

<script src="./Namespace_Forname.js"></script>
<script src="./Namespace_Surname.js"></script>

Rule(s)

export namespace SCION {
    export interface Event {
        …
    }
    export interface State {
        …
    }
    …
}
…
import {SCION} from "./SCION_CORE";
…
const state: SCION.State = …
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) => {
        console.assert(method_name === "my_method"); // See usage below...
        descriptor.configurable = value;
    };
}

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

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

const mc = new My_class();
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...
    console.log((error as TypeError).message);
}
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") {
        console.log(hello + "... " + bye);
    }
}

new Greetings(); // Decorator is called here...
TypeScript supports asynchronous programming through the Promise<T> generic type and its associated async/await syntactical sugars.

Rule(s)

Example (resolve class function) Promises.js.zip 

const Firefox = Promise.resolve(window.navigator.userAgent.includes("Firefox"));
Firefox.then(result => {
    if (result)
        window.alert(window.navigator.userAgent);
});

const Chrome = Promise.resolve(window.navigator.userAgent.includes("Chrome"));
Chrome.then(result => {
    if (result)
        window.alert(window.navigator.userAgent);
});

Example (race class function)

const image = new Image();
const is_image_loaded = new Promise(send => {
    image.onload = () => {
        console.assert(image.complete);
        send(image.complete);
    };
});
image.src = "img/Large_image.jpg"; // Image load…

const is_image_loaded_in_less_than_100ms = new Promise((yes, no) => {
    window.setTimeout(() => no(new Error('more than 100ms')), 100); // Change to '1000' afterwards...
});

const who_wins = Promise.race([is_image_loaded, is_image_loaded_in_less_than_100ms]);
who_wins.then(image_complete => window.alert("Image complete: " + image_complete));
who_wins.catch(error => window.alert(error));

Rule(s)

Example (all class function) Fetch.js.zip 

// 'fetch' support at 'http://caniuse.com/#feat=fetch'
Promise.all(
    [Promise.resolve(window.fetch !== undefined), window.DOM_ready,
        fetch("img/Large_image.jpg").then(function (response) {
            return response.blob();
        })
    ]).then(parameters => {
    if (parameters[0] === true) // 'fetch' is supported by the browser
//    'parameters[1]' -> DOM is ready with access to 'image_tag'
//    'parameters[2]' -> Image is available (i.e., 'complete') and transformed into blob
        parameters[1].src = URL.createObjectURL(parameters[2]); // 'image_tag' is loaded from "img/Large_image.jpg"
});

Rule(s)

Example Puppeteer.Node.js.zip 

/* Node.js program */

// 'puppeteer.js' library: https://developers.google.com/web/tools/puppeteer/
const puppeteer = require('puppeteer'); // 'npm install --save puppeteer' or 'sudo npm install --save puppeteer'
// Recent download problems require this: 'sudo npm install puppeteer --unsafe-perm=true --allow-root'

// Check whether 'puppeteer.js' has been installed: 'npm list -depth=0'

const take_picture = async () => { // 'async' returns an 'AsyncFunction' object
    /* Key rule:
     * 'await' returns the promise result, say what is computed by 'puppeteer.launch'
     * *NOT USING* 'await' leads to get the promise itself:
     */
//    const call_without_await = puppeteer.launch(); // Bad idea...
//    console.log(call_without_await instanceof Promise); // 'true' is displayed!
    const browser = await puppeteer.launch(/*{executablePath: '/Application/Google\ Chrome.app/Contents/MacOS'}*/); // Launch 'Chrome' without visual environment
    // Following call *DOES NOT* occur (because of prior 'await') until prior one terminates:
    const page = await browser.newPage();
    await page.goto('https://www.google.com/search?tbm=isch&q=nutella');//
    await page.screenshot({path: './Nutella.png'}); // Photo may be found in directory execution context
    await browser.close();
    return 'Nutella.png'; // Photo file name
};

// take_picture(); // Common style of calling a function...

take_picture().then((photo_file_name) => {
    console.log('Photo file name: ' + photo_file_name);
});

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
…