Work In Progress

Work on this post is underway... It will be done "soon..."


TypeScript is a superset of JavaScript, that means valid JavaScript code is valid TypeScript code, thus JavaScript libraries can be used in TypeScript. TypeScript introduces optional static typing which makes much easier to scale with larger development teams. TypeScript compiles to JavaScript for it to be executed/interpreted. We focus here only on TypeScript, not on using it on web development. We are trying to focus on one challenge only at a time. Here we are focusing on TypeScript only. This also means that we will not talk about alternative ways to do the same thing that are coming from JavaScript.

Many of the increments and type checks that TypeScript add does not carry over when the file is compiled to JavaScript. There is a strong dependency on the IDE or the code editor itself to give quick feedback on what is wrong. That said, I found that the feedback in the IDE to be very detailed an informative. That said, we can run the TypeScript compiler with the flag --strict for it not to avoid this leniency.

Basic IO

For the purposes of this document we will use console.log(...) for writing to std out

Variables, Constants and Basic Types

Declaration

  • let declare a variable
  • const declare a constant
  • While JavaScript’s var is still available, it is not recommended since variables declared using var are not scoped among other problems.
  • If no type is provided when declaring a variable or constant, the type any is assumed
  • To explicitly provide the type we write the variable/constant name followed by : then the type. let name: number = "Jane"

Deconstructing

  • Deconstructing is the act of taking a complex variable and assign its parts (deconstruct it) to more than one variable.
  • We will show here a simple example and defer the discussion of deconstruction to be close to each associated type
let array = [1, 2];
let [first, second] = array;

console.log(first);     // Prints 1
console.log(second);    // Prints 2

Commonly Used Data Types

Any

  • As the name implies this could be anything, which is the same behaviour JavaScript has. This is useful mostly in interfacing with other JavaScript libraries.

Void

  • Indicates an absence of value, usually used as the return type of a function that returns nothing.
  • A variable of type void can only be assigned either undefined or null only if --strictNullChecks is not specified

Never

  • never is a type indicating that the computation will never terminate or will terminate with an exception
  • never is a subtype of all other types
  • Null and undefined

  • null and undefined are subtypes of all other types except for never
  • when using the --strictNullChecks flag
    • null is only assignable to any
    • undefined is assignable to either any or void

Object

  • object is a type that represents any non-primitive type

Strings

  • Either ' or " can be used to surround string literals

    String Interpolation

  • A back tick can be used for_template strings_, which can span multiple lines and include an evaluable expressions in the format ${ expr }
let world = "World!";
let template = `Hello ${world}`;
console.log(template); // `Hello World!`

String Literal Types

  • In this case the type annotation is a string literal. This indicate that the variable can only have a string equivalent to that is of the type.
  • let green: "Green" can only have the value “Green”.
  • let trafficLight: "Red" | "Green" | "Yellow" means that this value can only be either of the three. This syntax is a Union Type which is discussed separately
  • Variable of string literal types may be considered a subtype of string as they can be converted to strings but not the other way around
  • The above is not enforced in compile or run time, but the IDE will complain
let red: "Red";
let green: "Green";
let yellow: "Yellow";
let trafficLight: "Red" | "Green" | "Yellow";

red = "Red";
green = "Green";
// red = green; // The IDE shall complain and a compile time error if `--strict` compiler flag is set
trafficLight = "Yellow";
// green = trafficLight; // The IDE shall complain and a compile time error if `--strict` compiler flag is set
let stringRepresentation = green; // That is absolutely OK!

Collections

Arrays

  • We can declare an array using one for two equivalent notations
    • let list: number[] = [1, 2, 3];
    • let list: Array<number> = [1, 2, 3];
  • Elements of the array can be accesses by index, and we can add elements in arbitrary indexes as well
let array: number[] = [1, 2, 3];
array[4] = 100;
console.log(array);     // Prints `[ 1, 2, 3, <1 empty item>, 100 ]`
console.log(array[5]);  // Prints `undefined`

Tuples

  • Tuples behaviour is more aligned with a conventional array, except the type of each element has to be predetermined and can’t be changed
let tuple: [number, string] = [1, "hello"];
// tuple[2] = "hi"; // Compile time error
console.log(tuple);
// console.log(tuple[4]); // Compile time error

Enums

  • Enum elements start at index 0 by default similar to an array. We can however set either the first element and the remaining elements will increment by one, or set each element index explicitly.
enum Color {Red, Green, Blue}
console.log(Color[2]);      // Prints `Blue`

enum Color2 {Red = 1, Green, Blue}
console.log(Color2[2]);     // Prints `Green`

enum Color3 {Red = 2, Green = 4, Blue = 6}
console.log(Color3[2]);     // Prints `Red`
  • Assigning an enum element to a variable is straightforward enough let color = Color.Green
  • We can expand an enum after the fact given that we explicitly give a value for the first element in the expansion
enum Color{Orange=10, Yellow}
console.log(Color[11]);     // Prints `Yellow`
console.log(Color[100]);    // Prints `undefined`
  • This also compiles, but I am not going to the rabbit whole of trying to understand what is going on ¯_(ツ)_/¯
enum Color4 {Red, Green}            // IDE complains about unused enum
enum Color4 {Yellow = 0, Orange}

console.log(Color4)
console.log(Color4[0]);             // Prints `Yellow`
console.log(Color4.Yellow);         // Prints `0`
console.log(Color4.Red);            // Prints `0`

Casting and Type Guards

as

  • as operator for type assertion (casting). It is not checked at compile time or runtime. The generated JavaScript omits this check altogether
  • Alternatively we can write person as Teacher as <Teacher> person. Both expressions have the same effect
interface Person {
}

class Teacher implements Person {
    teach() {
        console.log("I am teaching");
    }
}

let person: Person = new Teacher();

if (person instanceof Teacher) {

    person.teach();
}
let teacher = person as Teacher;
teacher.teach();
let wrongCast = person as string;
console.log(wrongCast); // Happily prints `Teacher{}`
console.log(wrongCast.charAt(0)); // throws an exception `charAt is not a function` 

instanceof

  • instanceof keyword is a boolean check of whether an object is of some given type. The object will be cast automatically within the scope in which type is checked
interface Person {
}

class Teacher implements Person {
    teach() {
        console.log("I am teaching");
    }
}

let person: Person = new Teacher();

if (person instanceof Teacher) {
    person.teach();     // Method `teach()` is not available out of this scope
}

typeof

  • typeof keyword is used to get the type of something, usually used to check for tye type of primitive types since they are not objects and hence instanceof will not work.
    • Used to check for one of the types string, number, bigint, boolean, symbol, undefined, object, function
let mayHaveAnyType: any = 4;
console.log(typeof mayHaveAnyType); // Prints `number`
mayHaveAnyType = "hello";
console.log(typeof mayHaveAnyType); // Prints `string

console.log(mayHaveAnyType.charAt(0));  // `charAt(0)` is accessible outside of the type scope. It will compile, but may fail in run time if the type is wrong
if (typeof mayHaveAnyType == "string") {
    console.log(mayHaveAnyType.charAt(0));
}

Resources