Link Search Menu Expand Document
Last updated: Aug 17, 22 15:10 UTC | Permalink

Typescript Basics

This tutorial describes the basic concepts and syntax of typescript with code examples. Typescript is treated here as an extension of JavaScript, but it requires no prior knowledge to follow along.

You can run the examples on Online Typescript Editor .


Typescript is a superset of JavaScript. Thus, all JavaScript code is valid Typescript code. Typescript introduces concepts of optional typing, modules, and few additional features to Javascript. Let us begin by extending JavaScript to use the features of Typescript and primitive types.

Types in typescript


The boolean type has two values: true and false.


let a = true                // boolean
     var b = false               // boolean
     const c = true              // true
     let d: boolean = true       // boolean
     let e: true = true          // true
  • Usage: You can compare them (with ==, ===, ||, &&, and ?), negate them (with !).


Number is the set of all numbers: integers, floats, positives, negatives, Infinity, NaN.


var b = Infinity * 0.10     // number
const c = 5678              // 5678
let d = a < b               // boolean
let e: number = 100         // number
let f: 26.218 = 26.218
  • Usage: Numbers can do things, like addition (+), subtraction (-), modulo (%), and comparison (<). When working with long numbers, use numeric separators to make those numbers easier to read.


The bigint type is the set of all BigInts, and supports things like addition (+), subtraction (-), multiplication (*), division (/), and comparison (<).


let a = 1234n               // bigint
const b = 5678n             // 5678n
var c = a + b               // bigint
let d = a < 1235            // boolean
let e = 88.5n               // Error TS1353: A bigint literal must be an integer.
let f: bigint = 100n        // bigint
let g: 100n = 100n
  • Usage: It lets you work with large integers without running into rounding errors. While the number type can only represent whole numbers it can hold numbers larger than 253 – 1.


String is the set of all strings and the things you can do with them like concatenate (+), slice (.slice),etc.


let a: any = 666            // any
let b: any = ['danger']     // any
let c = a + b               // any
  • Usage: Any functionality that requires the functionality of a string of characters. Here are a couple of string operations:

charAt() : This method returns the character from the specified index. Characters in a string are indexed from left to right. The index of the first character is 0, and the index of the last character in a string, called stringName, is stringName.length – 1.



slice() : This method extracts a section of a string and returns a new string. Syntax:

string.slice( beginslice [, endSlice])

split() : This method splits a String object into an array of strings by separating the string into substrings. Syntax:

string.split([separator][, limit])

concat(): This method adds two or more strings and returns a new single string. Syntax:

string.concat(string2, string3[, ..., stringN])

indexOf(): This method returns the index within the calling String object of the first occurrence of the specified value, starting the search at index or -1 if the value is not found. Syntax:

string.indexOf(searchValue[, fromIndex])


Like in JavaScript, TypeScript arrays are special kinds of objects that support things like concatenation, pushing, searching, and slicing. Arrays are iterable Objects with the keys as numbers.


let a = [1, 2, 3]           // number[]
var b = ['a', 'b']          // string[]
let c: string[] = ['a']     // string[]
let d = [1, 'a']            // (string | number)[]
const e = [2, 'b']          // (string | number)[]
let f = ['red']
let g = []                  // any[]
g.push(1)                   // number[]
g.push('red')               // (string | number)[]
let h: number[] = []        // number[]
h.push(1)                   // number[]


Tuples are subtypes of array. They’re a special way to type arrays that have fixed lengths, where the values at each index have specific, known types. Unlike most other types, tuples have to be explicitly typed when you declare them.


let a: [number] = [1]
// A tuple of [first name, last name, birth year]
let b: [string, string, number] = ['malcolm', 'gladwell', 1963]
  • Usage: In TypeScript Tuples support optional elements too. Just like in object types, ? means “optional”. Tuples also support rest elements, which you can use to type tuples with minimum lengths. Not only do tuple types safely encode heterogeneous lists, but they also capture the length of the list they type.


Enums are a way to enumerate the possible values for a type. They are unordered data structures that map keys to values.


enum Language {
  • Usage: There are two kinds of enums: enums that map from strings to strings, and enums that map from strings to numbers. Use it when you wish to provide flexibility making it easier to express and document intentions and use cases or when you want to save compile-time and runtime with inline code.


The any data type is the super type of all types in TypeScript. It denotes a dynamic type. Using the any type is equivalent to opting out of type checking for a variable.


let a: any = 666            // any
let b: any = ['danger']     // any
let c = a + b               // any
  • Usage: In TypeScript everything needs to have a type at compile time, and any is the default type when you (the programmer) and TypeScript (the typechecker) can’t figure out what type something is. It’s a last resort type, and you should avoid it when possible.


If any is the Godfather, then unknown is Keanu Reeves as undercover FBI agent Johnny Utah in Point Break: laid back, fits right in with the bad guys, but deep down has a respect for the law and is on the side of the good guys. You can compare unknown values (with ==, ===, &&, and ?), and refine them (like you can any other type) with JavaScript’s typeof and instanceof operators.


let a: unknown = 30         // unknown
let b = a === 123           // boolean
  • Usage: For the few cases where you have a value whose type you really don’t know ahead of time, don’t use any, and instead reach for unknown. Like any, it represents any value, but TypeScript won’t let you use an unknown type until you refine it by checking what it is.

Variable declaration in typescript

Variables can be declared in Typescript using one of 3 keywords:

  • var: var declarations are accessible anywhere within their containing function, module, namespace, or global scope - all which we’ll go over later on - regardless of the containing block. Some people call this var-scoping or function-scoping. Parameters are also function scoped.
  • let: When a variable is declared using let, it uses what some call lexical-scoping or block-scoping. Unlike variables declared with var whose scopes leak out to their containing function, block-scoped variables are not visible outside of their nearest containing block.
  • const: They are like let declarations but, as their name implies, their value cannot be changed once they are bound. In other words, they have the same scoping rules as let, but you can’t re-assign to them.

The syntax of declarations is as below:

var <name>: <type> = <value>;
let <name>: <type> = <value>;
const <name>: <type> = <value>;


let num: number = 1;
const PI: number = 3.14;
let x: string = "This is a string";
const t: boolean = true;
const f: boolean = false;
let uninitialized: any;


  • Objects are similar to dictionaries with key-value pairs.
  • Arrays are iterable Objects with the keys as numbers.


const myObj: any = {
    key1: 'value1',
    key2: 'value2'

console.log(myObj.key1); // prints 'value1'

Control Flow Statements

Typescript contains the following control flow statements:

  • If-Else
  • Switch
  • Ternary operator: Shorthand for if-else which returns a value.

If condition syntax:

if(condition) {
    // executed when condition is true

if(condition) {
    // executed when condition is true
} else {
    // executed when condition is false

if(condition) {
    // executed when condition is true
} else if(condition2) { // checked only if condition is false
    // executed when condition2 is true
} else {
    // executed when all conditions in the if-elseif ladder are false.

Switch case syntax:

switch (variable) {

    case <case1>:
        // executed when value of variable matches <case1>
        break; // Break is required to prevent all subsequent cases from executing
    case <case2>:
        // executed when value of variable matches <case1>
        break; // Break is required to prevent all subsequent cases from executing
        // executed if variable does not match any prior cases

Ternary Syntax:

let x = (condition) ? /* Executed when condition is true */ : /*Executed when condition is false*/;


const str: string = "ABCD";

if(str === "ABCD") {
    console.log("it was true");
} else {
    console.log("it was false");

switch (str) {
    case "ABCD":
        console.log('It was ABCD');
    case "WXYZ":
        console.log('It was WXYZ');
        console.log('It was something completely different')

let y: string = (str.includes("A")) ? "The string contains A" : "The string does not contain A";
// y now contains "The string contains A"

Equality vs Strict Equality

Typescript/JavaScript contain 2 equality operators:

  • ==: Compares only the value of entities being compared.
  • ===: Compares the type and value of entities being compared.


if(0 == '0') { } // Evaluates to true despite comparing string to number.

if(0 === '0') { } // Evaluated to false because types are different.

We recommend using strict equality (===) in all cases, and this recommendation is enforced by our linter.


Typescript contains the following loops:

  • For
  • While
  • Do-while


for(let i: number = 0; i < 10; i++) {


while(condition) {


do {

} while(condition)

Array Functions

ForEach, Map, reduce, and filter are all array methods in JavaScript. Each one will iterate over an array and perform a transformation or computation. Each will return a new array based on the result of the function.


forEach() method calls a function for each element in the array. It returns the resultant array.


array.forEach(callback[, thisObject]);


let num = [7, 8, 9];
num.forEach(function (value) {


It is an array function that transforms the array according to the applied function and returns the updated array. It works on each element of an array.


callback - It is a function that provides an element of the new Array from an element of the current one.

object - object to use as this when executing callback.

Return Type - List


//Calculate cube of each element with the help of map. 
function cube(n){  
   return n*n*n;  
var arr=new Array(1,2,3,4)  
console.log(newArr)  // Output : [1,8,27,64]

In the above example, a function called “cube” is created and then is passed as a callback function into map().


It also works on a callback for each element of an array. It reduces the result of this callback function from one array element to the other.



callback - this parameter is the function to execute on each value in the array.

intitalValue - this parameter is the object to use as the first argument of the first call of the callback.


//To calculate product of every element of an array,
var arr = new Array (1,2,3,4,5)    
var val = arr.reduce(function(a,b){    
   return a*b;    
//output => 120  

Calculate the total salary from a list of object, then you will use the reduce() like,

var employees = [    
   { id: 20, name: 'Ajay', salary:30000 },    
   { id: 24, name: 'Vijay', salary:35000 },    
   { id: 56, name: 'Rahul', salary:32000 },    
   { id: 88, name: 'Raman', salary:38000 }    
var totalSalary= employees .reduce(function (total, record) {    
   return total + record.salary;    
}, 0);    
//It will return the total salary of all the employees.  


As the name suggests it can filter out the data/array elements on the basis of condition and return the result as a list. Basically, this function pushes the current element into a new array when the callback functions return true.


callback - it is a function that provides an element of the new Array from an element of the current one. object - object to use as this when executing callback.

Return Type: List


//Calculate a list of even elements from an array :    
arr = new Array(1, 2, 3, 6, 5, 4)  
var newArr = arr.filter(function(record) {  
    return record % 2 == 0;  
}); // output => [2,6,4] 


  • Functions usually “take in” data, process it, and “return” a result. Remember one function/method one job!

  • There is a lot you can do with functions in JavaScript like assign them to variables, pass them to other functions, return them from functions, assign them to objects and prototypes, write properties to them, read those properties back, and so on, and TypeScript models all of those things with its rich type system, which we just learnt.

  • Typescript enables us to set the types for our function parameters and our return type.

  • Functions in Typescript are defined as below:

    function functionName(argument1: <type>, defaultArgument: <type> = value, optionalArgument?: <type>): <return type> {
    // Function body

Typing the function

A simple function in javascript would look like this:

// Named function
function add(a, b) {
  return a + b;

Now, let’s add types to our simple examples above:

function add(a: number, b: number): number {
  return a + b;

You will usually explicitly annotate function parameters (a and b in this example)—TypeScript will always infer types throughout the body of your function, but in most cases it won’t infer types for your parameters. The return type is inferred, but it’s a good practice to explicitly annotate it.

Invoking the function

When you invoke a function in TypeScript, you don’t need to provide any additional type information—just pass in some arguments, and TypeScript will go to work checking that your arguments are compatible with the types of your function’s parameters:

add(1, 2);         // evaluates to 3

Of course, if you forgot an argument, or passed an argument of the wrong type, TypeScript will be quick to point it out:

add(1);            // Error TS2554: Expected 2 arguments, but got 1.
add(1, 'a');       // Error TS2345: Argument of type '"a"' is not assignable
                  // to parameter of type 'number'.

Optional and Default Parameters

Like in object and tuple types, you can use ? to mark parameters as optional. When declaring your function’s parameters, required parameters have to come first, followed by optional parameters:

function log(message: string, userId?: string) {
  let time = new Date().toLocaleTimeString()
  console.log(time, message, userId || 'Not signed in')
log('Page loaded') // Logs "12:38:31 PM Page loaded Not signed in"
log('User signed in', 'da763be') // Logs "12:38:31 PM User signed in da763be"

You can provide default values for optional parameters. Semantically it’s similar to making a parameter optional, in that callers no longer have to pass it in (a difference is that default parameters don’t have to be at the end of your list of parameters, while optional parameters do).

For example, we can rewrite log as:

function log(message: string, userId = 'Not signed in') {
  let time = new Date().toISOString()
  console.log(time, message, userId)

log('User clicked on a button', 'da763be')
log('User signed out')

Rest Parameters

If a function takes a list of arguments, you can of course simply pass the list in as an array:

function sum(numbers: number[]): number {
  return numbers.reduce((total, n) => total + n, 0);
sum([1, 2, 3]); // evaluates to 6

Functions as Constructors

In Typescript/Javascript, Functions can be used as constructors for creating objects. This is similar to classes, but not quite the same.


function Person(firstName: string, lastName: string): void {
    this.firstName = firstName;
    this.lastName = lastName;
    this.getFullName = function(): string {
        return this.firstName + ' ' + this.lastName;
const person1 = new Person('first', 'last');
console.log(person1.getFullName()); // Returns 'first last'.
  • Constructors can’t have type parameters - these belong on the outer class declaration

  • Constructors can’t have return type annotations - the class instance type is always what’s returned ```ts class Point { x: number; y: number;

// Normal signature with defaults constructor(x = 0, y = 0) { this.x = x; this.y = y; } }

## Arrow Functions

Arrow Functions (also called fat arrow functions) are functions which have lexical 'this' and 'arguments'. This are especially useful in class methods to preserve the context when using higher order functions.

let sum = (x: number, y: number): number => {
    return x + y;

sum(10, 20); //returns 30

In the above example, sum is an arrow function. (x:number, y:number) denotes the parameter types, :number specifies the return type. The fat arrow => separates the function parameters and the function body. The right side of => can contain one or more code statements.

Function Overloads

In TypeScript, we can specify a function that can be called in different ways by writing overload signatures. To do this, write some number of function signatures (usually two or more), followed by the body of the function:

//function makeDate() with one parameter
function makeDate(timestamp: number): Date;
//function makeDate() with three parameters 
function makeDate(m: number, d: number, y: number): Date;
//function makeDate() with one parameter and 2 default parameters
function makeDate(mOrTimestamp: number, d?: number, y?: number): Date {
  if (d !== undefined && y !== undefined) {
    return new Date(y, mOrTimestamp, d);
  } else {
    return new Date(mOrTimestamp);
const d1 = makeDate(12345678);
const d2 = makeDate(5, 5, 5);
const d3 = makeDate(1, 3); //No overload expects 2 arguments, but overloads do exist that expect either 1 or 3 arguments.


Classes are blueprints for creating objects.

  • They can contain properties, methods, and a constructor.
  • All members of a class can have an access modifier: public, protected, private.
  • Members can also be static (shared across all instances of the class) and final (immutable).
  • Class properties may have getters and setters.
  • Classes can extend other classes.
  • Classes can implement interfaces.

Creating a Class

A class definition can include the following:

  • Fields − A field is any variable declared in a class. Fields represent data pertaining to objects
  • Constructors − Responsible for allocating memory for the objects of the class
  • Functions − Functions represent actions an object can take. They are also at times referred to as methods


class Person {

    private firstName: string = '';
    protected middleName: string;
    public lastName: string = '';

    private static final NeverGonnaGiveYouUp: any;
    protected static final NeverGonnaLetYouDown: any;
    public static final isRickRolled: boolean = true;

    constructor() {
        // I execute when you call new Person().
        // No access modifier === public by default.
        // Make me private if implementing a singleton.

    public anyoneCanCallMe(): void {

    protected childClassesCanCallMe(): void {
    private onlyAccessibleInsidePerson(): void {
        // I lied, anyone can call me if you know how.
        // Welcome to JavaScript :p

    public get firstName(): string {
        return this.firstName;

    public set firstName(firstName: string): void {
        this.firstName = firstName;


const person = Person();
person.firstName = 'first';

class SpecialPerson extends Person {
    // I contain everything person has, and can extend/override it.

    constructor() {
        super() // I call the constructor for Person.


Creating an abstract class

  • Define an abstract class in Typescript using the abstract keyword. Abstract classes are mainly for inheritance where other classes may derive from them. We cannot create an instance of an abstract class.

  • An abstract class typically includes one or more abstract methods or property declarations. The class which extends the abstract class must define all the abstract methods.


abstract class Person {
 abstract name: string;
 display(): void{
class Employee extends Person {
 name: string;
 empCode: number;
  constructor(name: string, code: number) {
     super(); // must call super()
     this.empCode = code; = name;
let emp: Person = new Employee("James", 100);
emp.display(); //James

Type Aliases

We’ve been using object types and union types by writing them directly in type annotations. This is convenient, but it’s common to want to use the same type more than once and refer to it by a single name. A type alias is exactly that - a name for any type. The syntax for a type alias is:

type Point = {
  x: number;
  y: number;
function printCoord(pt: Point) {
  console.log("The coordinate's x value is " + pt.x);
  console.log("The coordinate's y value is " + pt.y);
printCoord({ x: 100, y: 100 });

You can actually use a type alias to give a name to any type at all, not just an object type. For example, a type alias can name a union type:

type ID = number | string;

Note that aliases are only aliases - you cannot use type aliases to create different/distinct “versions” of the same type. When you use the alias, it’s exactly as if you had written the aliased type. In other words, this code might look illegal, but is OK according to TypeScript because both types are aliases for the same type:

type UserInputSanitizedString = string;
function sanitizeInput(str: string): UserInputSanitizedString {
  return sanitize(str);
// Create a sanitized input
let userInput = sanitizeInput(getInput());
// Can still be re-assigned with a string though
userInput = "new input";


Interfaces are used to define contracts for interaction with external entities. If an interface has a property/method, an Object/Class which implements it must have it. Interfaces can be used to define custom types for Objects in Typescript.


//Interface IPerson respresents a person by attributes firstName and lastName and a method to getFullName()
interface IPerson {
    firstName: string;
    lastName: string;
    getFullName(): string;

//Class Person implements interface Iperson. Person class must contains ll the attributes and methods of interface. 
class Person implements IPerson {

    public firstName: string = '';
    public lastName: string = '';

    public getFullName(): string {
        return this.firstName + ' ' + this.lastName;

    // It can contain any other properties/methods but must contain those in the interface.


const person: IPerson = new Person();

Custom types

  • For complicated types (usually required for Objects), it is recommended to use an interface.
  • Additionally, typescript contain the ‘type’ keyword which can be used to create new types.
  • This is generally used when a union of different types is required.


interface IStudent {
    name: string;
    age: number;
    studentID: number;
    gender: string;
    isEnrolled: boolean;

const student: IStudent = {
    name: 'name',
    age: 20,
    studentID: 111111111,
    gender: 'hidden'
    isEnrolled: true;

type StringOrNumber = string | number;

let x = 1;
x = 'some string';


  • Generics allow us to pass different types to a function/class making it more generalized.
  • This will be especially useful when working with http requests.


function myFunc<T>(data: T): T {
    return data

let x: string = myFunc<string>('some string');
let y: number = myFunc<number>(5);

class myClass<T> {


interface IMyInterface<T> {



A Module in typescript is defined by using the ‘Import’ or ‘Export’ keywords within a file. Variables declared in a module are scoped locally and are not added to the global scope.

  • Export makes the variables and functions from a file visible outside it.
  • Import allows use to use variables and functions exported by another file.


// file1.ts

export const someVar: string = 'Variables can be exported too.';

export default function add(x: number, y: number): number {
    return x + y;

export function subtract(x: number, y: number): number {
    return x - y;

function multiply(): void {
    throw new Error();

// file2.ts
import add from './file1'; // curly braces are not required if export default is used.
// There can be only 1 export default in a while. Use it if file has only 1 export.
import { subtract } from './file1';

add(1, 2);
subtract(2, 1);

// multiply cannot be imported.

Object oriented programming concepts using typescript

Object Oriented Programming or OOP is a programming paradigm that has four principles which are:

  • Inheritance
  • Polymorphism
  • Abstraction
  • Encapsulation


TypeScript supports the concept of Inheritance. Inheritance is the ability of a program to create new classes from an existing class. The class that is extended to create newer classes is called the parent class/super class. The newly created classes are called the child/sub classes.

A class inherits from another class using the ‘extends’ keyword. Child classes inherit all properties and methods except private members and constructors from the parent class. However, TypeScript doesn’t support multiple inheritance.


class child_class_name extends parent_class_name


//Parent class Shape
class Shape {
  constructor(a:number) {
    this.Area = a

//Child class Circle that inherits properties of Shape 
class Circle extends Shape {
 disp():void {
    console.log("Area of the circle:  "+this.Area)
var obj = new Circle(223);


When multiple classes inherit from a parent and override the same functionality, the result is polymorphism. Each of those child classes now implements a property or method, but they each may have their own way of performing that implementation.

lternatively, one child class might override the parent’s members while another child doesn’t but just accepts the parent class’s implementation instead. This also demonstrates polymorphic behavior, since those behaviors are different between the siblings.

class CheckingAccount {
  open(initialAmount: number) {
    // code to open account and save in database

class BusinessCheckingAccount extends CheckingAccount {
  open(initialAmount: number) {
    if (initialAmount < 1000) {
      throw new Error("Business accounts must have an initial deposit of 1.000 Euros")

class PersonalCheckingAccount extends CheckingAccount {
  open(initialAmount: number) {
    if (initialAmount <= 0) {
      throw new Error("Personal accounts must have an initial deposit of more than zero Euros")

In the above code sample shows, the two child classes have different business rules to implement when it comes to opening an account – mainly different opening balances. Because both children have a method to open the account but both children choose to do it differently means the behavior is polymorphic.

To achieve polymorphism, inherit from a base class, then override methods and write implementation code in them. In addition to overriding methods, you can overload methods to achieve polymorphism.

Overloaded methods are methods that have different signatures (i.e., different data types or number of arguments) with the same name. However, in TypeScript, methods aren’t overloaded by simply modifying the types or number of arguments like in some other languages.

To create an overload in TypeScript, you can either add optional arguments to a method, or overload function declarations in an interface and implement the interface.


Abstraction is a way to model objects in a system that creates a separation of duties between class or type and the code that inherits it.

A developer creates a type, i.e., a class or interface, and that type specifies what the calling code should implement, but not how. So it’s the job of the abstract type to define what needs to be done, but up to the consuming types to actually do those things. To enforce abstraction, inherit or implement from abstract classes and interfaces.

For example, some bank accounts have fees. You can create a Fee interface that defines a method for charging a fee. Fees don’t apply to all types of accounts, so it’s best to create an interface that can be applied to specific classes anywhere in the inheritance hierarchy. A checking account might charge fees, where its parent and sibling, the generic bank account and savings accounts might not.

interface Fee {
  chargeFee(amount: number );

// parent BankAccount and sibling SavingsAccount do not implement Fee interface
class BankAccount { ... }

class SavingsAccount extends BankAccount { ... }

// checking implements Fee
class CheckingAccount extends BankAccount implements Fee {
  chargeFee(amount: number) {}

Children classes inherit interface members that have been implemented in their parent, so if a BusinessChecking account has inherited from the CheckingAccount class, then it inherits that implementation.

// BusinessChecking inherits CheckingAccount and therefore Fee
class BusinessChecking extends CheckingAccount {  }

// Code that uses BusinessChecking can call chargeFee
function CalculateMonthlyStatements() {
  let businessChecking = new BusinessChecking();


Encapsulation is a key part of Object Oriented Programming that is a way to structure code so that a certain block of code has specific access points for external code. The term for this is “visibility” or “accessibility”. Visibility defines what code from one method, property, or class can call code in another method, property, or class.

In TypeScript, we enforce encapsulation with methods and properties that only allow access to data that we control. The Withdraw method below does that by doing the calculation and updating the class level _balance field. The Balance property then returns the private _balance field to the calling code.

Withdraw(amount: number): boolean
    if (this._balance > amount)
        this._balance -= amount
        return true;
    return false;
private _balance: number;
get Balance(): number {
    return this._balance;

General Guidelines

  • Naming conventions
    • use kebab-case for file names.
    • Use camelCase for variable and function names.
    • Use PascalCase for Class and constructor function names.
  • Prefer descriptive names over random letters.
  • Although typing is optional in typescript, it is not optional for this course.
  • Always use strict equality.
  • Use a linter. (as specified on course website)
  • Use a prettifier. (If the linter doesn’t do it already)
  • Use general coding guidelines as discussed in Week1.

© 2022 Jonathan Bell, Adeel Bhutta, Ferdinand Vesely and Mitch Wand. Released under the CC BY-SA license