Stasoz
4 supporters
TypeScript Cheat Sheet

TypeScript Cheat Sheet

Dec 06, 2021

A short note about TypeScript and his capabilities.

What is TypeScript?

TypeScript is a super set of JavaScript.

Why TypeScript:

  1. TypeScript reduce number of bugs by using types which help you catch bugs at the compile-time instead of having them occurring at runtime;

  2. TypeScript allow you to use new JS today;

TypeScript Types

TypeScript uses types to analyse your code for errors and to understand what values are associated with variables.

Via TypeScript you can explicitly specify types for identifiers such variables, functions, objects, etc. It’s called — type annotations. Just use the : type syntax

// e.g.
let someNumber: number;
let someString: string;
let someBoolean: boolean = true;
someNumber = 1;
someString = 1; // Compile error

If you forget to explicitly specify type then TypeScript will infer the type you. It’s called — type inference. Let’s take a look for example:

let someNumber = 0;
// It is equivalent to the line below
let someNumber: number = 0;

When do I need use type inference or type annotations?

It’s recommended to use type annotations when:

  • When you create a variable and assign it a value later;

  • When you need a variable that can’t be inferred;

  • When a function returns the any type and you need to specify the value;

In all other cases you should use type inference as much as possible.

There are two categories of types

Primitive types:

  • string — represents text data;

// string examples
let someString1: string = 'Hello World'; // single quotes
let someString2: string = "Hello World"l // double quotes
let someString3 = `Hello World`; // multi-line string with backtick
let someString4 = `${someString1}`; // string interpolation
  • number — represents numeric values;

// decimal numbers
let someNumber1: number;
let someNumber2 = 5.55;
let x: number = 10,
    y: number = 20;// binary numbers
let someBinaryNumber1 = 0b99;
let someBinaryNumber2: number = 0b100;// big int
let someBigIntNumber = 9007199254740991n;// hexadecimal numbers
let someHexDecimalNumber = 0XA;
  • boolean — has true or false values

let someBoolean1 = true;
let someBoolean2: boolean;
someBoolean2 = true;
  • null — has only one value: null

  • undefined — has only one value: undefined. Also it is default value of an uninitialised variable

  • symbol — represents a unique const

Object types:

  • object — represents all values that are not in primitive types

let person: object;person = {
   name: 'Stas',
   age: 30
};

We can explicitly specify properties of the person object via syntax below

let person: {
   name: string,
   age: number
};
person = {
   name: 'Stas',
   age: 30
};
// or we can do it in this waylet 
person: {
   name: string,
   age: number
} = {
   name: 'Stas',
   age: 30
};

If you will try to access a property that doesn’t exist on the person object, you’ll get an error.

  • array — is an ordered list of data

let someArray: number[]; // to add element we can use next syntax
someArray.push(1); // or 
someArray[0] = 1;

We can declare array with initial items and TypeScript will infers the fruits array as an array of strings.

let fruits = ['banana', 'mango'];

It is equivalent to the following:

let fruits: string[];
fruits = ['banana', 'mango'];

Of course you can not add any another type to declared type of array. TS compiler will throw error.

fruits.push(100); // error
  • enum — list of named constants.

enum Week {
   Monday, // 0
   Tuesday, // 1
   Wednesday, // ...
   Thursday,
   Friday,
   Saturday,
   Sunday
}
// you can change start number explicitly
enum Week { 
   Monday = 1,
   ...
}

It’s recommended to use enums when you have a small set of fixed values that are closely related and known at compile time.

  • functions — are the building blocks of readable, maintainable, and reusable code

function functionName(par1: type, par2: type, ...): returnType {
   //some code
}

You can also declare a function type which has parameters and return type

(par: type, par,...) => returnType 
// example
let stringConcat: (str1: string, str2: string) => string;
stringConcat = function(str1: string, str2: string) {
   return str1 + str2;
}
// or you can do it like this
let stringConcat: (str1: string, str2: string) => string = 
   function(s1: string, s2: string) {
      return s1 + s2;
   }
}

Functions can have optional parameters

// use '?' after parameter name
function functionName(par1: type, par2?: type): returnType {
   //some code
}

Or default parameters

// use 'par:type = value' syntax
function functionName(par1: type, par2: type = 'Hello'): returnType {
   //some code
}

You can also overload function

function add(a: number, b: number): number; 
function add(a: string, b: string): string; 
function add(a: any, b: any): any { return a + b; }
  • class— JS doesn’t have a class, but TS allow you to create the class(like in C#, Java, C++) based on prototype inheritance.

class Person {
   name;
   age;
   
   constructor(name, age) {
      this.name = name;
      this.age = age;
   }   getPersonInfo() {
      return `${name} ${age}`;
   }
}
let person = new Person('Stas', 30);
console.log(person.getPersonInfo());

Or you can use type annotations for the properties and methods in class

class Person {
   name: string;
   age: number;
   
   constructor(name: string, age: number) {
      this.name = name;
      this.age = age;
   }  
   getPersonInfo(): string {
      return `${name} ${age}`;
   }
}
let person = new Person('Stas', 30);
console.log(person.getPersonInfo());

TS class has three access modifiers to properties and methods:

  1. privatemodifier — allows access to property within the same class.

  2. protectedmodifier — allows access to property within the same class and subclasses.

  3. publicmodifier — allows access to property from any location.

In class you can use readonly modifier that allows you to mark the properties immutable. With readonly you can assign a value only in property declaration or constructor.

class Person {
   readonly name: string;
   
   constructor(name: string) {
      this.name = name;
   }
}

If you want to inherit one class from another class, use extends keyword and if parent class has a constructor that initializes the some properties(name, age) you need to initialize these properties in the constructor of the Employee class by calling its parent class’ constructor via super().

class Person {
   name: string;
   age: number;
   
   constructor(name: string, age: number) {
      this.name = name;
      this.age = age;
   }
 
   getPersonInfo(): string {
      return `${name} ${age}`;
   }
}
class Employee extends Person {
   jobTitle: string;   constructor(
      name: string,
      age: number,
      jobTitle: string) {
         super(name, age);
         this.jobTitle = jobTitle;
   }   
  describe() {
      return super.getPersonInfo() + this.jobTitle;
   }
}
let employee = new Employee('Stas', 30, 'Developer');
console.log(employee.describe());

Class can has static properties and methods which shared by all instances of a class. Use statickeyword to declare static property or method

class Person {
   static counter: number;
   
   constructor() {
      Person.counter++;
   }
   
   static getPersonCount() {
      return Person.counter;
   }
}
let person1 = new Person();
let person2 = new Person();
console.log(Person.getPersonCount()); // 2

Another good TypeScript feature — abstract class, an abstract class has at least one abstract method and to use it you need inherit it and provide the implementation for the abstract methods.
Of course you can not create instance of abstract class

abstract class Person {
   constructor(name: string, age: number) {}
   
   abstract getInfo(): string;
}
class Manager extends Employee {
   constructor(name: string, age: number) {
      super(name, age);
   }
   
   getInfo(): string {
       return this.name + '  ' + this.age;
   }
}
  • interface— define contracts in your code and provide explicit names for type checking. Interface may contain readonly or optional properties and you can use interface as class type that make a contract between unrelated classes or as function types.

interface Person {
   name: string,
   age: number
}
function getInfo(person: Person) {
   return person.name + ' ' + person.age;
}
// optional properties 
interface Person {
   name: string,
   age: number,
   jobTitle?: string
}
function getInfo(person: Person) {
   if(person.jobTitle) {
      return person.name + ' ' + person.age + '-' person.jobTitle;
   }
   
   return person.name + ' ' + person.age;
}
//readonly properties
interface Person {
   readonly name: string,
   age: number
}
let person: Person;
person = {
   name: 'Stas',
   age: 30
}
person.name = 'David'; // error
person.age = 31; // ok

Via interface you can describe function types

interface StringFormat {
   (str: string, isLower: boolean): string
}
let format: StringFormat;
format = function(str: string, isLower: boolean) {
   return isLower 
      ? str.toLocaleLowerCase()
      : str.toLocaleUpperCase()
}
console.log(format('Hello World', true)); // hello world

Finally, class can implemented interface via implements

interface Json {
   toJson(): string
}
class Employee implements Person {
   name: string;   constructor(name: string, age: number) {
      this.name = name;
      this.age = age;
   }
   
   toJson() {
      return JSON.stringify(this);
   }
}
let employee = new Employee('Stas', 30);
console.log(employee.toJson()); // {"name":"Stas","age":"30"}

Last thing with interfaces — it can extend one or multiple existing interfaces.

interface A { 
   a(): void 
}  interface B extends A {     
   b(): void 
}
// B interface has two methods: a() and b()

Specific types

  • any — use when you don’t know type at the time of writing the program (some values may come from API) or when you migrate from JS to TS.

  • void — use when function doesn’t return any value.

function(message: string): void {
   console.log(message);
}
  • never — type that contains no value, it is mean you cannot assign any value to a variable with a never type. never use when you have a function that always throws an error. For example:

function someFunction(message: string): never {
   throw new Error(message);
}

Or you can use nevertype if you have an indefinite loop

let infinityLoop = function createInfinityLoop() {
   while(true) {
      console.log('.');
   }
}
  • union — allows you to store a value of one or several types in a variable

let someUnionValue: string | number;
someUnionValue = 1; // ok
someUnionValue = 'Hello'; // ok
someUnionValue = false; // error
  • aliases — allows you to define new names for existing types

type newAliasesType = string | number; 
let someVariable: newAliasesType;
  • literal — allows you to define a type that accepts only specific string values

let statusLiteral: 'approve' | 'reject' | 'pending';
let myStatus: statusLiteral;
myStatus = 'approve'; // ok
myStatus = 'hello'; // error
  • intersection — creates a new type by combining multiple existing types and new type will have all features of the existing types. Order doesn’t matter

type typeAB = typeA & typeB; // definition 
// example 
type hybridVariable = number & string;
let someVariable: hybridVariable = '3'; // ok
someVariable = 3; // ok
someVariable = false // error

Note, that uniontype and intersection are not the same.

Type Guard and Casting

  1. use typeof if you need to check variable type

let a: number;
if (typeof a === 'number') { ... }

2. use instanceof to check instance of class

class Person1 {...}
class Person2 {...}type Person = Person1 | Person2;
function showInfo(person: Person) {
    if (person instanceof Person1) {...}
    if (person instanceof Person2) {...}
}

3. use in if you need to check existence of a property on an object

class Person {
   name: string;
   ...
}
function showInfo(person: Person) {
   if('name' in person) {...}
}

TS allow you convert a variable from one type to another. Use the as keyword or <> operator for type castings.

let a: typeA;
let b = a as typeB;// or let a: typeA;
let b = <typeB>a;

Generics

Generics allow you to write the reusable and generalized form of functions, classes, and interfaces.

Generics benefits:

  1. Getting rid of type casting

  2. Implementing generics algorithms

  3. Leverage type checks at the compile time.

function getRandomElement<T>(items: T[]): T {
   let randomIndex = Math.floor(Math.random() * items.length);
   return items[randomIndex];
}
let numbers = [1, 5, 7, 4, 2, 9]; 
let randomNumberEle = getRandomElement<number>(numbers);  
console.log(randomNumberEle);
let strings = ['a', 'b', 'c'];
let randomStringEle = getRandomElement<string>(strings);
console.log(randomStringEle);

You can use generics with multiple types

function merge<U, V>(obj1: U, obj2: V) { 
    return {
         ...obj1,
         ...obj2
    };
}

And denote the constraint, you use the extends keyword

function merge<U extends object, V extends object>(obj1: U, obj2: V) {...}

Inspired by — https://www.typescripttutorial.net

Enjoy this post?

Buy Stasoz a coffee

More from Stasoz