Getting started with TypeScript, List of Array Methods & String Methods

Fri Nov 24 2023

Let's take some notes on TypeScript.

Released in 2012 by Microsoft, TypeScript is a free and open-source programming language that builds on JavaScript. It was created by Anders Hejlsberg who is also the person who designed and developed C#.
TypeScript is designed for the development of large applications. It uses file extension .ts and needs to be transpiled to JavaScript.

TypeScript makes it easy to debug our code because it shows us the errors BEFORE we run it.

Getting started

Compiling

Once you've created your .ts file, open the terminal and cd into your project folder. Then type: tsc, the name of the file that needs to be compiled and the destination file. Running this command will create filename.js in the same directory.

tsc filename.ts filename.js      OR      tsc filename.ts

tsconfig

Placed in the root of the project, .tsconfig file's purpose is customizing the rules to be enforced by the compiler.

In the terminal run tsc -init to create the configuration file.

tsc -init

Then we need to decide where our .js and .ts files will be stored. Once we do that, in the tsconfig.json file we can find the rootDir and outDir and put the directory names that we will be using in our project.

Watching the .ts files

tsc -w

If we want all our .js files to be created in the folder we specified earlier, even if the .ts file is in our root directory, we need to make one change in the .tsconfig file.

{
    "compilerOptions": {
        ...
    },
    "include": ["src"]  
}

Explicit Return Types

TypeScript recognizes the primitive data types of JavaScript: string, number, boolean, null and undefined. Once we declare a variable with an initial value, the variable can never be reassigned a value of a different data type. This is called type inference.

We can explicitly state what type a variable is.

let color: string = 'blue'

If we don't specify that our variable is a string, TypeScript will figure it out. However if TypeScript in unable to infer the data type, it will assign it the any type. These won't give an error if we reassign to a different type.

Variable Type Annotations

We can tell TypeScript what type something is or will be by using a type annotation. These declarations are automatically removed when compiled to JavaScript.

let color: string;
let isFun: boolean;
let regular: boolean;
let phoneNumber: string | number;
let re: RegExp;

Parameter Type Annotations

Function parameters may be given type annotations as well. The syntax stays the same.
Parameters without type annotations are assumed to be of type any.


function hello(name: string){
    console.log(`Hello ${name}!`)
}

Optional Parameters

If a parameter is supposed to be optional, we need to add a ? after its name. If a function has multiple parameters the optional ones should be listed as last.

function hello(name?: string) {
  console.log(`Hello, ${name || 'everyone'}!`);
}

hello()        // Hello, everyone!

We need to use a type guard which will narrow down the possible value of our optional parameter.

const addNumbers = (num1: number, num2: number, num3?: number): number => {
    if (typeof num3 !== 'undefined') {
        return num1 + num2 + num3
    }
    return num1 + num2
}

addNumbers(1, 4, 6)             // 11
addNumbers(5, 9)                // 14

Default Parameters

function hello(name = 'everyone') {
  console.log(`Hello, ${name}!`);
}

hello()        // Hello, everyone!

Void Return Types

If a function does not return a value, we must treat the return type as void. A function whose declared type is neither 'void' nor 'any' must return a value.

function hello(name: string): void {
    console.log(`Hello ${name}!`)
}

Comments

// This is a single line comment 

/*
This is a multiline
comment
*/

/**
* This is a documentation comment
*/

We place a function's documentation comment in the code directly above the function declaration. It should explain the parameters (by using @param tag) and what the function is returning (by using @returns tag).

Arrays

TypeScript makes it very easy to keep track of element types in arrays.

Array Type Annotations

We need to put [ ] after the element type.

let colours: string[] = ['blue', 'purple'];

OR

let colours: Array<string> = ['blue', 'purple'];


let colours: string[] = [1, 2]        // Type Error

We can also declare multidimensional arrays: arrays of arrays (of some type).

let arr: string[][] = [['el1', 'el2'], ['el3', 'el4']];

Tuples

Tuples are arrays with a fixed sequence of types. With tuples we know the exact number of elements it has and the exact type of each of them.
Note that tuples and arrays do not have compatible types within TypeScript, which means that an array cannot be assigned to a tuple.

let tuple: [string, number, string, boolean] = ['spring', 4, 'Is it?', false]; 

Rest Parameters

The rest parameter syntax allows a function to accept an indefinite number of arguments as an array

function eat(breakfast, ...dinner: string[]) {
    for(let i = 0; i < dinner.length; i++) {
        console.log(dinner[i])
    }
}
function moveYourFeet(moveDirection: string, moveReps: number, fast: boolean): void {
    console.log(`Move your feet to the ${moveDirection} ${moveReps} times!`);
    if (fast) {
        console.log('Do it fast!');
    }
}

let feetMoves: [string, number, boolean][] = [
    ['right', 3, false],
    ['left', 4, true]
];

let move: [string, number, boolean][] = [...feetMoves]
    move.forEach(m => {
    moveYourFeet(m[0], m[1], m[2])
})

  //  Move your feet to the right 3 times!
      Move your feet to the left 4 times!
      Do it fast!

Custom Types

Numeric Enums

We use enums when we want to enumerate all the values that a variable will allow.

enum Direction {
  North,
  South,
  East,
  West
}

Direction.North, Direction.South, Direction.East, and Direction.West are equal to 0, 1, 2, and 3, respectively.
We could assign Disrection.North = 3, which would reassign the values of Direction.North, Direction.South, Direction.East, and Direction.West to 3, 4, 5, and 6, respectively.
We could also specify all numbers separately.

Another example.

There is a shop that sells scarfs. The scarfs are available only in 3 colours.

enum Scarf {
    Blue,
    Orange,
    Purple
}

We received an order for 3 blue scarfs, 1 purple and 2 orange. Let's visualize our order as an array.

let order: [Scarf, number][] = [
    [Scarf.Blue, 3],
    [Scarf.Purple, 1],
    [Scarf.Orange, 2]
]

String Enums

When working with string enums, we need to assign the strings explicitly - they will not be assigned automatically as it happens with numeric enums. The convention is assigning a variable to a value that is its capitalized version.

enum Direction { 
    North = 'NORTH', 
    South = 'SOUTH', 
    East = 'EAST', 
    West = 'WEST' 
}

Object Types

TypeScript has no restrictions when it comes to the types of an object's properties. They can be enums, arrays, and even other object types.

let myCar: { make: string, year: number };

myCar = { make: 'Dacia', year: 2019 }

Type Aliases

We can customize the types and use type aliases. This is useful for referring to complicated types that need to be repeated, especially object types and tuple types.

type MyString = string;

let hi: MyString = 'Hi';

Example from Codecademy

type Coord = [number, number, string, number, number, string]

let codecademyCoordinates: Coord = [40, 43.2, 'N', 73, 59.8, 'W'];
let bermudaTCoordinates: Coord = [25, 0 , 'N' , 71, 0, 'W'];

Function Types

In TypeScript we can specify the argument types and return type of a function thanks to function types. Note that even if there is only one parameter, we should not omit the parentheses.

type Function = (argument1: string, argument2: string) => number

        // This function type will take in two string arguments and return a number.
type Function = 

Generic Types

Generic types also called generics let us make collections of types that have certain similarities. T is the placeholder (it can be replaced by any other letter or word) later replaced with the provided type.

type Colours<T> = {
  primary: T, secondary: [T, T], tertiary: T[]
};

let warmPallete: Colours<string> = {
    primary: 'red',
    secondary: ['orange', 'brown'],
    tertiary: ['yellow', 'lightyellow', 'beige', 'lightbrown']
}

Another example:

const test = (arg: T): T => arg

const isObject = (arg: T): boolean => {
    return (typeof arg === 'object' && !Array.isArray(arg) && arg !== null)
}

console.log(isObject([1, 2, 3, 4]))         // false
console.log(isObject({colour: 'blue'}))     // true

Union Types

TypeScript allows us to be flexible with how specific our types are by combining different types. Each type in a union is called a type member.

let phoneNumber: string | number

Unions with Literal Types

type Color = 'green' | 'yellow' | 'red' 

In

It's useful to use the in operator when we want to see if a specific method exists on a type instead of a type like 'string'. TypeScript recognizes in as a type guard.

type Fantasy = {
  canDoMagic: () => void;
}

type Contemporary = {
  cantDoMagic: () => void;
}

function play(sport: Fantasy | Contemporary) {
  if ('canDoMagic' in book) {
    return book.canDoMagic();
  }

  if ('cantDoMagic' in book) {
    return book.cantDoMagic();
  }
}

Type vs. interface

The syntaxes for type and interface are slightly different, but both will enforce the typed object at compile time when typing a variable. The difference between these two is that interface can only be used to type objects, while type can be used to type objects, primitives, and more.
Interface, then, is perfect for writing object-oriented programs because these programs need many typed objects.

type Person = {
    height: number
    favColour: string
}

const list: Person = ...

interface Person {
    height: number
    favColour:string
}
const list: Person = ...

Classes

→ Public, private, protected

Parameters and methods inside of a class are called members.

We need to have the property (here: height, etc.) as a member of a class and in the constructor.

class Person {
    height: number
    favColour: string
    age: number
    language: string

    constructor (
        height: number, 
        favColour: string,
        age: number,
        language: string

    ) {
        this.height = height
        this.favColour = favColour
        this.age = age
        this.language = language
      }
}

As writing every parameter 3 times seems a bit redundant, 
we can use visibility modifiers to avoid it.

class Person {
   height: number
   favColour: string
   age: number
   language: string

    constructor (
       public readonly height: number, 
       public favColour: string, 
       private age: number,    // it can be accessed inside of a class as well as inside of derived classes (if we use extend)
       protected language: string = 'English'  // it can only be accessed inside of this class
    ) {
        this.height = height
        this.favColour = favColour
        this.age = age
        this.language = language      
      }

      public getAge() {
        console.log(`Hello, I am ${this.age} years old.`)
    }
}

const Adam = new Person(180, 'Blue', 47, 'English')

Adam.getAge()                   // Hello, I am 47 years old.
console.log(Adam.favColour)     // Blue
console.log(Adam.age)           // Error from TS: Property 'age' is private and only accessible within class 'Person'.

Note that even though TypeScript throws an error, the code will be compiled into JavaScript.

The Extends keyword + super()

class Chef extends Person {
    constructor (
        public  uniform: string,
        height: number, 
        favColour: string,
        age: number,
    ) 
    { 
        // super needs to come before we try to assign uniform

        super(height, favColour, age)   // language is not required as it has a default value 'English'
        this.uniform = uniform
    }
    public getLanguage() {
        return `I speak ${this.language} and I wear an ${this.uniform}.`
        // we can access language because it was  
    }
}

const Mario = new Chef('apron', 188, 'pink', 26)
console.log(Mario.getLanguage())            // I speak English and I wear an apron.


Static

The static keyword applies directly to the class - not to any object you intantiate with the class.

class Genre {

    static count: number = 0

    static getCount(): number {
        return Genre.count
    }

    public id: number

    constructor (public name: string) {
        this.name = name
        this.id = ++Genre.count
    }
}

const Thriller = new Genre('Thriller')
const Comedy = new Genre('Comedy')
const Romance = new Genre('Romance')

console.log(Thriller.id)        // 1
console.log(Comedy.id)          // 2
console.log(Romance.id)         // 3

console.log(Genre.count)        // 3

Interface & class

We can apply a type to an object/class with the implements keyword.

interface Person {
    sleep: (when: string) => void;
}

class NightWorker implements Person {
    sleep(when: string) {
        console.log(`I sleep ${when}.`)
    }
}

const Pete = new NightWorker
Pete.sleep('during the day')       // I sleep during the day.

Composed Types

Object types can be nested infinitely.

Extending Interfaces

The extends can help us organize our code. It can abstract out common type members into their own interface, then copy them into more specific types.

Index Signatures

When we don't know the property names for an object that we want to type, it is useful to write an object type that allows us to include a variable name for the property name.
Also, TypeScript requires an index signature if you attempt to access an object property dynamically.

interface Training {
    [index: string]: number
}
                

Let's try to access the properties dynamically.

interface ShoppingList { Bread: number, Milk: number, Newspaper: number }
instead
interface ShoppingList { [index: string]: number }
const todaysShopping: ShoppingList = { Bread: 1.5, Milk: 1.4, Newspaper: 2 } console.log(`I paid ${todaysShopping.Bread} for bread.`) // I paid 1.5 for bread. console.log(todaysShopping['Bread']) // 1.5 let prop: string = 'Bread' console.log(todaysShopping[prop]) const todaysShoppingCost = (items: ShoppingList): number => { let total = 0 for (const item in items) { total += items[item] } return total } console.log(todaysShoppingCost(todaysShopping)) // 4.9

keyof

The keyword keyof creates a union type as the specific string literal.

For comparison purposes, first let's see an example without the keyof keyword.

interface Song {
    [key: string]: string | number | string[] | undefined
    title: string,
    singer: string,
    year: number,
    authors?: string[]          // optional
}

const songOnlyYou: Song = {
    title: 'Only You',
    singer: 'Elvis Presley',
    year: 1955
}

console.log(songOnlyYou.authors)           // undefined    
                                           // (there's no error as we listed undefined as a type option)
_____________________

const songOnlyYou: Song = {
    title: 'Only You',
    singer: 'Elvis Presley',
    year: 1955
    authors: ['Ande Rand', 'Buck Ram']
}

for (const key in songOnlyYou) {
    console.log(`${key}: ${songOnlyYou[key]}`)
}

// title: Only You
// singer: Elvis Presley
// year: 1955
// authors: Ande Rand,Buck Ram

Now let's use the keyof keyword in the same example.
We will need to get rid of the index signature.

interface Song {
    title: string,
    singer: string,
    year: number,
    authors?: string[]
}

const songOnlyYou: Song = {
    title: 'Only You',
    singer: 'Elvis Presley',
    year: 1955
    authors: ['Ande Rand', 'Buck Ram']
}

for (const key in songOnlyYou) {
    console.log(`${key}: ${songOnlyYou[key as keyof Song]}`)
}

// title: Only You
// singer: Elvis Presley
// year: 1955
// authors: Ande Rand,Buck Ram

OR

Object.keys(songOnlyYou).map(key => {
    console.log(`${key}: ${songOnlyYou[key as keyof typeof songOnlyYou]}`)
})

// title: Only You
// singer: Elvis Presley
// year: 1955
// authors: Ande Rand,Buck Ram

OR

const logSongKey = (songOnlyYou: Song, key: keyof Song): void => {
    console.log(`The song ${key} is ${songOnlyYou[key]}`)
}

logSongKey(songOnlyYou, 'title')        // The song title is Only You

List of String Methods

  • charAt() Returns the character at the specified index.
  • charCodeAt() Returns a number indicating the Unicode value of the character at the given index.
  • concat() Combines the text of two strings and returns a new string.
  • indexOf() Returns the index within the calling String object of the first occurrence of the specified value, or -1 if not found.
  • lastIndexOf() Returns the index within the calling String object of the last occurrence of the specified value, or -1 if not found.
  • localeCompare() Returns a number indicating whether a reference string comes before or after or is the same as the given string in sort order.
  • match() Used to match a regular expression against a string.
  • replace() Used to find a match between a regular expression and a string, and to replace the matched substring with a new substring.
  • search() Executes the search for a match between a regular expression and a specified string.
  • slice() Extracts a section of a string and returns a new string.
  • split() Splits a String object into an array of strings by separating the string into substrings.
  • substr() Returns the characters in a string beginning at the specified location through the specified number of characters.
  • substring() Returns the characters in a string between two indexes into the string.
  • toLocaleLowerCase() The characters within a string are converted to lower case while respecting the current locale.
  • toLocaleUpperCase() The characters within a string are converted to upper case while respecting the current locale.
  • toLowerCase() Returns the calling string value converted to lower case.
  • toString() Returns a string representing the specified object.
  • toUpperCase() Returns the calling string value converted to uppercase.
  • valueOf() Returns the primitive value of the specified object.

List of Array Methods

  • concat() Returns a new array comprised of this array joined with other array(s) and/or value(s).
  • every() Returns true if every element in this array satisfies the provided testing function.
  • filter() Creates a new array with all of the elements of this array for which the provided filtering function returns true.
  • forEach() Calls a function for each element in the array.
  • indexOf() Returns the first (least) index of an element within the array equal to the specified value, or -1 if none is found.
  • join() Joins all elements of an array into a string.
  • lastIndexOf() Returns the last (greatest) index of an element within the array equal to the specified value, or -1 if none is found.
  • map() Creates a new array with the results of calling a provided function on every element in this array.
  • pop() Removes the last element from an array and returns that element.
  • push() Adds one or more elements to the end of an array and returns the new length of the array.
  • reduce() Apply a function simultaneously against two values of the array (from left-to-right) as to reduce it to a single value.
  • reduceRight() Apply a function simultaneously against two values of the array (from right-to-left) as to reduce it to a single value.
  • reverse() Reverses the order of the elements of an array -- the first becomes the last, and the last becomes the first.
  • shift() Removes the first element from an array and returns that element.
  • slice() Extracts a section of an array and returns a new array.
  • some() Returns true if at least one element in this array satisfies the provided testing function.
  • sort() Sorts the elements of an array.
  • splice() Adds and/or removes elements from an array.
  • toString() Returns a string representing the array and its elements.
  • unshift() Adds one or more elements to the front of an array and returns the new length of the array.

Sources:

Learn Typescript Course on Codecademy

TypeScript Docs on https://www.typescriptlang.org

Mozilla: Mdn Web Docs

TutorialsPoint: TypeScript

Illustration for the Getting started with TypeScript, List of Array Methods & String Methods

Comments (0)


Be the first to leave a comment