Log In

JS Typescript Interview questions


by Yariv Katz

I used to interview a lot of junior as well as senior software developers, and also I taught a lot of courses that prepares the students to interview questions in full stack programming. Seeing things from both the interviewee eyes and the interviewers made me want to summerise the questions and code snippets you may get during job interviews. Of course there are infinite amount of topics in the software world so this article will focus on question you may get in the following programming languages:

  • Javascript
  • Typescript

And since Typescript is a subset of Javascript, alot of these questions apply to both the programming languages. The code in this lesson will be available in the following repository: https://github.com/ywarezk/ts-js-interview-quiz.git

Variable declarations

In JS/TypeScript we use the keywords: var/let/const for variable declarations. It's important to understand the difference between those 2 kinds and know when to use what. The following code examples will reside in the folder: variable-declarations In the repository of the article. br Question #1

for(var i = 0; i < 10; i++) {
    for(var i = 0; i < 10; i++) {
        console.log(i);
    }    
}

console.log('now with let');

for(let j = 0; j < 10; j++) {
    for(let j = 0; j < 10; j++) {
        console.log(j);
    }    
}

The question is what will be printed in the first loop. Notice that the first loop is a nested for loop where the variable i is defined in the external and internal loop. Same goes for the second loop where we have a different variable j defined in the outer and internal loop. .

Answer

The first loop you declare two variables using the var keyword. But actually you declare a single variable called i. The scope of a variable declared with var is in the function that variable is in, in the browser if not in a function the variable will be global and attached to window. If running the file using node and not the browser, then the variable scope will be in the file we are in. While running modules, node wraps then in a function so the scope will be the function that wraps our module. So the scope of var is functional, this means that while choosing the same variable name we are basically using the same variable. The is why the inner loop will print 0 to 9 and then the outer loop will exit cause it's the same i and it's already reached the condition in the loop.

the second loop we define the variable named j with let, let (as well as const) scope is in the block they are defined. This means that we actually defined two variables one for each block and this means that 0 to 9 will be printed 10 times. const has the same scope, the difference between let and const is that const has a single assignment, this does not mean the const is immutable, we can still place an array in const and push items to that array.

what to choose and when? You should start with the most strict, const and climb up. place const and if need multiple assignments change to let, if you need the variable outside the block as well then go with var. I think the majority of variables expecially advanced types like array and objects will be defined with const.

equiped with the following knowledge, what will the above code print:

function sayHello(isMessage) {
    if (isMessage) {
        var message = 'hello from block';
    }
    console.log(message);
}

sayHello(true);
sayHello(false);

closure

commonly asked in questions about JS is the result of a function running while closure is used. Closure refers to how JS sets the environment variables while running functions. The following example is in the folder closure

function withName(name) {
    return function sayHello() {
        return `hello world ${name}`;
    }
}

const nerdeez = withName('nerdeez');
const bugeez = withName('bugeez');

console.log(nerdeez());
console.log(bugeez());

With closure JS will look for the variables in the inner scope of the function, then in the outer scopes of the wrapping functions, then in the global scope. Although there are 2 functions declared each one is with it's own seperate environment of variables. same goes with assignment, if we are assigning a variable it will assign in the inner function then the outer one and then... lets examine another example:

function withName() {
    var name = 'bugeez.io';
    return function sayHello(newName) {
        name = `hello world ${newName}`;
        return name;
    }
}

let nerdeez = withName();
let bugeez = withName();

console.log(nerdeez('nerdeez'));
console.log(bugeez('bugeez'));

function withName2() {
    return function sayHello(newName) {
        name = `hello world ${newName}`;
        return name;
    }
}

nerdeez = withName2();
bugeez = withName2();

console.log(nerdeez('nerdeez'));
console.log(bugeez('bugeez'));

function withName3() {
    return function sayHello(newName) {
        "use strict"
        name = `hello world ${newName}`;
        return name;
    }
}

nerdeez = withName3();
bugeez = withName3();

console.log(nerdeez('nerdeez'));
console.log(bugeez('bugeez'));

3 extra examples for closure, the first one if I declare a variable in the outer function I can still assign a new value to it. assigning a new value will change only the scope of current lexical environment and nerdeez changing the name will not effect bugeez. What is intresting is what happens if we remove the variable declaration of name in the outer scope, the second example will look for that variable and when name not found it will be attached to the global scope. Usually this kind of behaviour where an undeclared variable will be attached to the global scope is not something we will delibretly aim to do, so the 3rd example we are using use strict. In use strict mode JS will not allow us to attach variables to the global like we did in example 2 and if looking for name variable and not finding it the result will be exception.

this

this in JS behaves differently then other languages, and I guess that is why it's a very popular topic to ask about in job interviews. unless specifically set otherwise the default behaviour of this in JS is to determine this during runtime by setting this to the object from which we called the function. The following questions will be in the folder this. Also important to note that this example we will write with Typescript since it contains some stage3 syntax that is compatible with Typescript or JS trasnpiled with Babel. Lets see a few code questions you might get asked about this.

class Person {
    constructor(public age = 33) {
        this.birthday3 = this.birthday3.bind(this);
    }

    birthday1() {
        this.age++;
    }

    birthday2 = () => {
        this.age++;
    }

    birthday3() {
        this.age++;
    }
}

const yariv = new Person();
const obj = {
    birthday1: yariv.birthday1,
    birthday2: yariv.birthday2,
    birthday3: yariv.birthday3,
}

obj.birthday1();
console.log(yariv.age);
obj.birthday2();
console.log(yariv.age);
obj.birthday3();
console.log(yariv.age);

We created here a class called Person and even though it's a class, this might still change. As an example of the change we placed the method birthday1 which we didn't bind this and we showed an example of calling this method from the object obj will place a different this in the function (specifically this is obj). the other methods birthday2 and birthday3 are bound to the class instance, one is bound in the constructor, and one is bound with a stage3 experimental syntax that is supported with typescript and babel, which is the lambda syntax in class, you can declare a method in a class as a lambda and it will forever be bound to the class instance. The caller of the method can also set this while calling a method with call or apply.

Prototype

Tons of possible questions on this topic, and since most of the JS programmers do not fully understand this topic, they tend to fail in these questions. Essentially prototype is an object with methods and properties that are passed to the children. Prototype explains how JS deals with inheritance and how JS is looking for properties in an object. First JS will look in the object itself then it will look for the property down the prototype chain. Must of the types in JS have a common parent called Object.prototype. The prototype chain is linked through a pointer called __proto__. Try to examine your knowledge in prototype by answering the code questions in the folder prototype.

const myNumber = 10;
myNumber.__proto__ // 1. this is equal to what?
myNumber.__proto__.__proto__ // 2. this is equal to what?

function Person1(name) {
    this.name = name;
}

Person1.__proto__; // 3. this is equal to what?
Person1.prototype.__proto__; // 4. this is equal to what?

let bugeez = new Person1('bugeez');

bugeez.__proto__ // 5. this is equal to what?
bugeez.__proto__.__proto__ // 6. this is equal to what?

const myFirstPrototype = {
    hello: 'world'
}

const child = Object.create(myFirstPrototype);
child.__proto__ // 7. this is equal to what?

function Person2(name) {
    this.name = name;
    this.sayHello1 = () => {
        console.log(this.name);
    }
}

Person2.prototype.sayHello2 = function() {
    console.log(this.name);
}

bugeez = new Person2('bugeez');
let nerdeez = new Person2('nerdeez');

bugeez.sayHello1 === nerdeez.sayHello1; // 8. what will this return?
bugeez.sayHello2 === nerdeez.sayHello2; // 9. what will this return?

for(let key in bugeez) {
    console.log(key) // 10. what will this print?
}

bugeez.hasOwnProperty('name'); // 11. what will this be equal?
bugeez.hasOwnProperty('sayHello1'); // 12. what will this be equal?
bugeez.hasOwnProperty('sayHello2'); // 13. what will this be equal?

Lets try to answer the following questions. Questions number 1, 2 are talking about prototype of types, specifically in this type it is the number but basically same rules apply to all the types. When we create a type we create an instance that is connected to a prototype chain. Creating a number the first link in the linked list of prototypes will be the Number.prototype which is connected to a __proto__ pointing to Object.prototype. Same goes if the type was Array: Array.prototype --> Object.prototype
Or if the type was string: String.prototype --> Object.prototype

Questions 3,4,5,6 could get a bit confusing. We are creating a function which basically represents a custom type we are creating (AKA class). When creating the Person1 function, we are creating a function type, so same rules apply like 1,2 question, where the __proto__ of the Person1 will point to Function.prototype When creating a function in JS it will attach an empty prototype object to that function where the __proto__ will point to Object.prototype. When creating an instance from the custom type we created essentially we are creating the instance where the __proto__ of that object will point to Person1.prototype. This means that the prototype chain of the variable bugeez will be:
bugeez --> Person1.prototype --> Object.prototype

Question number 7 shows that you can also use Object.create to create instance of a type with a custom prototype of your choosing. You can also pass null if you want your object not to inherit from Object.prototype. In this example child prototype chain will be:
child.__proto__ --> myFirstPrototype --> Object.prototype

Questions number 8,9 try to show the difference between attaching a function to the prototype and attaching the function to the instance. When grabbing this and attaching a function to it, basically we are creating a function for each instance (could be a waste of resource at times) but when we attach the function to the prototype we are creating a single function shared among all the instances.

Question number 10-13 try to explain how JS looks for properties down the prototype chain. First JS will look for the property in the instance, then the first __proto__ then the one after that and so on. In fact when we are using the for in loop we are basically iterating over the entire prototype chain printing all the enumerable properties (unless we use Object.defineProperty the properties we will define will be enumerable by default). So we have to understand which properties reside in the instance and which down the prototype chain. We can use hasOwnProperty which will return a boolean if the property is on the instance. Note that if we attach to the prototype chain the property won't belong to the instance and it will return false. The for in could be unpredictable and change when we change the prototype chain, you can iterate on the instance keys by calling Object.keys which will return the keys on the instance, or iterate on all the keys and condition the loop using hasOwnProperty.

Promise

Perhaps the main feature of JS is the ease of use of async events, and due to the fact the our JS code will be filled with async code, a JS developer has to know this topic perfectly. Due to the importance of this topic it will be asked a lot in interviews. Promise is an object that wraps an async code, and allows us to attach listeners that will be called when the async code is resolved or rejected. Promise gives us an easy interface for throwing and catching an error from our async code, run async code in parallel or each one waits for the previous one (waterfall), and manipulate and map the data returning from the promise. Lets try and answer some few code examples using a promise, these question are in the folder: promise:

const timerPromise1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('hello from promise');
    }, 1000);
})

const timerPromise2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject(new Error('timer error'));
    }, 1000);
})

timerPromise1.then((msg) => {
    console.log(msg); // 1. what will be printed? 
});

timerPromise2.then(() => {}, (err) => {
    console.log(err.message); // 2. what will be printed here?
});

timerPromise2.catch((err) => {
    console.log(err.message); // 3. what will be printed here
});

this code shows how to create a promise and how to add a listener to a promise. A promise can be in a pending or resolved/rejected state. When we want to move the state from pending to resolve/reject state, we have to call the resolve or reject method. To attach a listener we use the then method that will get a fulfilled method to be called on resolve and a rejected method to be called when rejecting. We can also attach a listener to the reject by calling the catch method which is similar to calling the then and passing first function argument as null. It's common to call the reject with an instance of Error class. Note that simply use throw in the promise will not work.

lets demonstrate some possible questions on Promise Chaining. When calling then or catch, the return of those question is a promise. The promise returned is with a different data that depends on what we are returning from the method in the then or in the catch. If we are returning a promise, it will be filled with the data of the promise. Example of code questions:

const timerPromise1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('hello from promise');
    }, 1000);
});

// 1. what is the type of this promise: Promise<?>?
const chainedPromise1 = timerPromise1.then((msg) => {
    return msg.length;
})

// this example will work only in the browser
// 2. what is the type of this promise: Promise<?>
const chainedPromise2 = timerPromise1.then(() => {
    // the url will return json when placed on the browser
    return fetch('https://nztodo.herokuapp.com/api/task/?format=json');
}).then((res) => {
    return res.json();
});

const timerPromise2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject(new Error('error'));
    }, 1000);
});

// 3. what will be the type of timerPromise3: Promise<?>
const timerPromise3 = timerPromise2.catch((err) => {
    return err.message;
}).then((errorMessage) => {
    return errorMessage.length;
});

Few examples of promise chaining in the code above. The goal of the questions is for you to understand what the promise will contain. In the first question we are grabbing a promise containing string and returning from the fulfilled function the length of the string which will turn the promise to containing a number. The answer for the first question is Promise&ly;number>
On the second question we are writing a fulfilled method for the timer promise and in that method returning a promise containing response: Promise<Response>
This turns the promise from Promise<string> to Promise<Response> after that we are returning a the response json method which also returns a promise of the json object. So chainedPromise2 is a variable of type Promise containing the json response as an object. The third example shows a rejected promise, we catch that promise with the catch method which also returns a promise based on what returned from that function so we turned the rejected promise to a Promise containing string by returning the error message, and then converting the string promise to a number by returning the length of the error message. The catch will return a promise as well which allows us to gracefully catch errors along the promise chain and return an alternate promise instead of rejecting the entire chain.

So with the catch returning a promise we can now determine how to deal with errors on our chain of promises. When we think of our errors we need to think about two types of errors:
- critical error that breaks the chain
- error that should deal with the error properly and continue
In the then or catch methods we can also throw error will continue the reject cycle, this fact gives us the ability to have those 2 types of errors that can originate from a single promise and deal differntly on each type. Lets examine promise questions relating to dealing with errors on the promise chain:

const timerPromise1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('hello from promise');
    }, 1000);
});

const chainedPromise2 = timerPromise1
    .catch()
    .then(() => {
        // the url will return json when placed on the browser
        return fetch('https://nztodo.herokuapp.com/api/task/?format=json');
    })
    .then((res) => {
        if (res.status !== 200) {
            throw new Error();
        }
        return res.json()
    })
    .catch((err) => {
        // 1. in what scenarios will this function be called? 
        console.log(err.message);
    })

Few things to note here. The first catch will catch an error in the timer promise but will continue anyway. The second promise will make a server call, this may fail if there is a network error. If fails it will jump to the last catch. If success it will check if the response is 2XX and if not throw and error which will also jump to the last catch.

Async Await

Equipped with the knowledge about promises, we can now answer questions about async await functions. async functions are function that returns promise. When people undersrtand the async await syntax, they will never stop using it, it simply makes your code really readable and simple. With the async function you can use await keyword to "wait" for a promise to be resolved. What actually happens is that the function will exit and return when the promise is resolved and continue from the point we left the function. In async functions we can now deal with errors with try and catch, we can throw an error to make the promise reject. Lets examine the questions in the folder async-await

const timerPromise1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('hello from promise');
    }, 1000);
});

async function timerAndFetch() {
    try {
        const [msg, res] = await Promise.all([timerPromise1, fetch('https://nztodo.herokuapp.com/api/task?format=json')]);
        const json = await res.json();
    } catch(err) {
        return err.message;
    } 
}

timerAndFetch().catch((err) => {
    // 1. when will this be called
    console.log(err.message);
});

we define here an async function which calls in parallel a timer promise and a fetch from server promise. In async function we are dealing with promise reject with try and catch, and we use throw to pass a promise reject. Since we are catching the reject with a try and catch, the promise returned from the async function will never be rejected. So the answer is never be called.

Observables and async await

The async await syntax is so easy to write and understand, when we get used to it we would like to combine these functions in our angular development. Angular mostly use Observables for their async code, but observables have a method called toPromise we can use to easily convert observable to promise. For examples it's a common practice in angular to connect to the lifecycle hook OnInit and query the server from that method through HttpClient or through a service that wraps HttpClient. This is a valid init from server code using async await syntax:

import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html',
    styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
    title = 'test-angular';
    json: Object;

    constructor(private _http: HttpClient) {}
    
    async ngOnInit() {
        const json = await this._http.get('https://nztodo.herokuapp.com/api/task/?format=json').toPromise();
        this.json = json;
    }
}

the method ngOnInit is prefixed with async, and it's much more readable and easy to debug now. Be carful with turning observables to promise, promises are made for a single value while observables represent a data stream which is possibly infinite. This means that on an infinite data stream the complete won't be called and turning that to promise will be stuck on a pending state. Same goes if our observables emit multiple data and closing the promise will be resolved with the last data.

Summary

Almost all interviews have some part of theory in them. The theory will not only focus on framework questions on React and Angular, they can also focus on the programming language. We tryed to give some topic and example questions here but there are a few categories that we didn't talk about mainly because they are relativly simple. Topics like OOP are often asked about as well, and this might include questions on inheritance, abstract class, interfaces, etc. Keep on studying on your own and you will nail the next job interview.

how Bugeez can help you study

Working with a Todo list, and organizing in that todo list the topics you need to study will keep you organized and prepared for the next interview. Bugeez can help you organize your todo list. Open a new application, you might call it: interviews
organize a bug for each topic you need to study. The topics will be arranged in a easy to manage board and once you finished studying a topic, move that ticked to the closed board. You can also prioritize the order which you need to study. Writing all the things you need to study and organize them with priority will help you achieve your goals easily.