Notebook about learning TypeScript

BB

Lacking of a chinese input method, I write this article in English. Sorry for my poor English in advance. QAQ
The following note is not containing all the content in the official guide.

 

Reference

https://www.typescriptlang.org/docs/home.html
https://www.differencebetween.com/difference-between-javascript-and-vs-typescript/

 

Enviroment

TypeScript: v3.6.2

 

What is TypeScript

Typescirpt is a superset of JavaScript, which is needed to compiled into javascript file via compiler. It is an OOP language and has static type checking, modules, etc.

 

Basic Type

// boolean
let isOk: boolean = false;

// number
let x: number = 66;
let inf: number = 0x3f3f3f3f;

// string
let str: string = "Hello World";
let template_str: string = `The no. ${ x } string is: ${ str }`; // Template strings
let equiv_str: string = "The no. " + x + " string is: " + str; // equivalant type

// Array
let list: number[] = [1, 2, 3];
let list2: Array<number> = [1, 2, 3];

// Tuple
let tupleX: [string, number] = ["abc", 123];

// Enum
enum Col {Red, Green, Blue};
let c: Col = Col.Green;

// Any
// "Any" can call arbitary methods, but "Object" not
let listAny: any[] = [isOk, x, inf, str, template_str, equiv_str, list, list2, tupleX, c];

// Void
// "void" can be assigned only "null" or "undefined"
function printStr(x : string) : void {
    console.log(x);
}

// Null & Undefined
// "null" can be assigend only "null", and "undefined" can be assigned only "undefined"
let n1 : null = null;
let u1 : undefined = undefined;

// Never
// "never" represents the types of values that never occur
function forever(): never {
    while(true) {

    }
}

// Object
// "object" presents the non-primitive type
function callStudent(x: object) {
    console.log("The id of ${ x.name } is ${ x.id }.");
}
let student: object = {
    id: 1000,
    name: "John"
};

// Type Assertions
// type assertions: like type cast
let aVal: any = "this is a string";
let l1: number = (<string>aVal).length;
let l2: number = (aVal as string).length;

 

Interface

Basic

Interace can describe the requirement of the object.

Point 1: Optional Property

interface SquareConfig {
    color?: string,  // optional property (use '?')
    width: number,
    height: number   // needed property
}

function createSquare(conf: SquareConfig): { color: string, area: number } {
    let nSqure = {
        color: "black",
        area: conf.height * conf.width
    }
    if(conf.color) {
        nSqure.color = conf.color;
    }
    return nSqure;
}

// Okay, must satisfy the needed property
let withdout_color_square = createSquare( { width: 100, height: 123 });
// Okay, can have the optional property
let with_color_squre = createSquare( { width: 34, height: 45, color: "green"} );
// Err, property "name" doesn't in the interface
//let errSquare = createSquare({ width: 12, height: 45, name: "Err!" });
// Err, property "height" dosen't exist
//let errSqre = createSquare( { width: 2 } );

Point 2: Readonly Property

Readonly Property can make variables unchanged after initialization
Variables use const, whereas property use readonly

interface Point {
    readonly x: number,
    readonly y: number
};

let p1: Point = {
    x: 50,
    y: 50
};
// p1.x = 50;  // Err!

let arr = [1, 2, 3, 4];
let roArr: ReadonlyArray<number> = arr;
// roArr[0] = 12;  // Err!
// roArr.length = 12; // Err!
// roArr.push(12); // Err!
// arr = roArr // Err!
arr = roArr as number[]  // type assertion, okay

Point 3: String Index Signature

Add a string index signature if the object can have some extra properties

interface SquareConfig {
    color?: string,
    width: number,
    height: number,
    [prop: string]: any   // Receive any number of props
}

function createSquare(conf: SquareConfig): void { }

// okay
createSquare( { width: 100, height: 123, aProp: [1, 2], bProp: [3, 4] });

Point 4: Function Types

interface cmpFunc {
    (a: number, b: number): boolean   // Function
};

let myCmpFunc: cmpFunc = function(x: number, y: number): boolean {
    return x > y;
}

Point 5: Indexable Types

interface StringArray {
    [index: number]: string
}
interface StringOrNumberArray {
    [index: number]: string | number    // Union
}
let arr: StringArray = ["AA", "BB"];
// let arr2: StringArray = [1, 2]; // Err!
let str: string = arr[1];

let arr3: StringOrNumberArray = ["AA", 12];
// let num: number = arr3[1];   // Err!

Point 6: Class Types

interface ClockConstructor {
    new (hour: number, minute: number): ClockInterface;  // Constructor
}
interface ClockInterface {
    tick(): void
}

class DigitalClock implements ClockInterface {
    constructor(h: number, m: number) {}
    tick() {
        console.log("beep beep");
    }
}

class AnalogClock implements ClockInterface {
    constructor(h: number, m: number) {}
    tick() {
        console.log("tick tick")
    }
}

function createClock(obj: ClockConstructor, h: number, m: number): ClockInterface {
    return new obj(h, m);
}

let obj1 = createClock(DigitalClock, 1, 2);
let obj2 = createClock(AnalogClock, 3, 4);

Point 7: Hybrid Types

interface Counter {
    (start: number): string,
    interval: number,
    reset(): void
};

function getCounter(): Counter {
    let obj = (function (start: number) {}) as Counter;
    obj.interval = 123;
    obj.reset = function() {};
    return obj;
}

let a = getCounter();
let counterName: string = a(10);
a.interval = 156;
a.reset();

 

Function

Basic Function Type

function add1(x: number, y:number): number {
    return x + y;
}

let add2 = function(x: number, y: number): number {
    return x + y;
};

let add3: (x: number, y:number) => number =
    function(x: number, y: number): number {
        return x + y;
    };

let out : string = `Res: ${add1(1, 2)} ${add2(1, 2)} ${add3(1, 2)}`;
console.log(out);

Inferring the types

For example, the typescript compiler can figure out the type of function(x, y) in function add2

let add1: (x: number, y:number) => number =
    function(x: number, y: number): number {
        return x + y;
    };

let add2: (x: number, y:number) => number =
    function(x, y) {
        return x + y;
    };

let x : number = add1(1, 2); // okay
let y : number = add2(1, 2); // okay
// let z : number = add2("1", 2);  //ERR!

Optional and Default Parameters

Optional parameters are metioned above, and default parameters are similar to those in C++. In addition, when the user does not pass the value to the optional parameter, the value of the parameter is undefined

function buildName(firstName: string, lastName?: string) {
    return lastName ? `${firstName} ${lastName}` : `${firstName}`;
}

function buildName2(firstName: string, lastName: string = "HHH") {
    return `${firstName} ${lastName}`;
}

function buildName3(firstName: string = "HHH", lastName: string) {
    return `${firstName} ${lastName}`;
}

let s1 = buildName("aaa", "bbb");   //"aaa bbb"
let s2 = buildName("aaa");  //"aaa"
let s3 = buildName2("aaa", "bbb");  //"aaa bbb"
let s4 = buildName2("aaa"); //"aaa HHH"
let s5 = buildName2("aaa", undefined);  //"aaa HHH"
//let s6 = buildName3("aaa"); // ERR! Too few arguments
let s7 = buildName3("aaa", "bbb");  //"aaa bbb"
let s8 = buildName3(undefined, "bbb");  //"HHH bbb"

Rest Parameters

The rest parameters can deal with the function with unknown number of the parameters

function buildName(firstName: string, ...restName: string[]) {
    return firstName + " " + restName.join(" ");
}

let s1 = buildName("aaa", "bbb", "ccc", "ddd"); //"aaa bbb ccc ddd"

this

this and arrow functions

The example is simplified from official guide. In ES6, arrow functions capture the this where the function is created rather than where it is invoked.

/*
// ERR!

let deck = {
    suits: ["hearts", "spades", "clubs", "diamonds"],
    createCardPicker: function() {
        return function() {
            return {suit: this.suits[0]};
        }
    }
}
*/

let deck = {
    suits: ["hearts", "spades", "clubs", "diamonds"],
    createCardPicker: function() {
        return () => {
            return {suit: this.suits[0]};
        }
    }
}
this parameters
interface Card {
    suit: string
};

interface Deck {
    suits: string[],
    createCardPicker(this: Deck): () => Card;
}

let deck: Deck = {
    suits: ["hearts", "spades", "clubs", "diamonds"],
    createCardPicker: function(this: Deck) {
        return (): Card => {
            return {suit: this.suits[0]};
        }
    }
}

Overload

function add(a: number, b: number): number;
function add(a: string, b: string): string;
function add(a: any, b: any): any {
    if(typeof a == "number" && typeof b == "number") {
        return a + b;
    } else if(typeof a == "string" && typeof b == "string") {
        return a + b;
    } else {
        return null;
    }
}

add(1, 2);
add("1", "2");
// add(1, "2");    // ERR!

 

Generics

Basic of Generics

The concept of generics is similar with the most popular language.

function fun<T>(arg: T): T {
    return arg;
}
let x = fun<string>("str");
let y = fun<number>(1);
// let z = fun<string>(1); // ERR!