Backbencher.dev

TypeScript Interview Questions Part 3 - Intersection, Union, Functions

Last updated on 29 May, 2021

Question:

Give one example where we can use TypeScript Intersection?

Answer:

We have a variable person that can accept either a student object or a teacher object. The Student type and Teacher type is defined separately.

interface Student {
  name: string;
  marks: number;
}

interface Teacher {
  name: string;
  salary: number;
}

Now based on some condition either a student object or teacher object is assigned to person. In that case person should be ready to accept either one of these objects. For that we use TypeScript Intersection.

let person: Student | Teacher;

if (Math.random() > 0.5) {
  person = {
    name: "Hanna",
    marks: 23,
  };
} else {
  person = {
    name: "Jenny",
    salary: 8900,
  };
}

In the first line, we can see the Intersection(|) operator. That will make person to accept either one of the types.

Once we use intersection operator, only the keys that are common to both types could be used there after. For eg, in the above code, name is the common property. So we can access name.

console.log(person.name);
console.log(person.marks); // Error

It is not sure if person will have marks property. So, TypeScript throws an error if we try to access it.

----o----

Question:

We have a TypeScript code here:

interface Student {
  marks: number;
}

interface Teacher {
  salary: number;
}

let person: Student | Teacher;

if (true) {
  person = {
    marks: 23,
  };
} else {
  person = {
    salary: 8900,
  };
}

console.log(person.marks);

marks is present only in Student, not in Teacher. Therefore, does the last line throw an error?

Answer:

No. The if condition is checking for true. So TypeScript knows that the else block will never get executed. For that reason person.marks does not throw an error. If there was any chance to go to else block, then it will be an error.

----o----

Question:

What are union types in TypeScript? When do we use it?

Answer:

Union operator(&) is used in TypeScript to join the types from multiple types. Say we already have a type Person.

interface Person {
  name: string;
  age: number;
}

We now need to define a teacher object, who is a person and also has some more teacher specific attribute.

interface Teacher {
  school: string;
}

let teacher: Person & Teacher;

Now teacher variable should be an object that is a union of Person and Teacher type. That means, it is mandatory for teacher variable to have name, age, school properties. Absence of any property will result in error.

teacher = {
  name: "John",
  age: 23,
  school: "Winterville",
};
----o----

Question:

What will happen if we use TypeScript union operator like this:

let street: string & number;
street = 23;

Answer:

In line 1, we are declaring the type of street as both a string and number at the same time. Even though, such a condition cannot be met, TypeScript will not throw any error in line 1. TypeScript simply sets the type of street as never. never is a type that says, no value can be accepted.

Since the type of street is never, line 2 throws an error when we try to assign a number type to never type.

----o----

Question:

Here is a JavaScript variable color.

let color;
color = "Red";

The variable can only contain either of 3 values: "Red", "Yellow", "Green". How can we bring this restriction using TypeScript?

Answer:

This can be done using intersection operator in TypeScript.

let color: "Red" | "Yellow" | "Green";
color = "Red";
----o----

Question:

Here we have a function that accepts an object and returns an object.

function setState(state) {
  const newState = { ...state, name: "John" };
  return newState;
}

The input and return object can have only 2 properties, name and age. How can we make sure the type safety using TypeScript?

Answer:

Since the argument type and return type of the function are same, it is good to create an interface first.

interface IPerson {
  name: string;
  age?: number;
}

Then set the type for function parameter and return type.

function setState(state: IPerson): IPerson {
  const newState = { ...state, name: "John" };
  return newState;
}
----o----

Question:

We have a ES5 typed function.

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

We need to convert above function to arrow function syntax. How can we do that?

Answer:

Here is the typed arrow function.

const sum = (a: number, b: number): number => {
  return a + b;
};
----o----

Question:

Here, we have a TypeScript function.

function subscribe(accepted: "yes" | "no", email?: string): void {
  console.log(email);
}

My intention was, if the value passed to accepted is "no", the second argument should not be passed. And if the value of accepted is "yes", then the second argument email is mandatory. But the code snippet above is failing. It accepts email, even if the value of accepted is "no". How can we fix it?

Answer:

Here we are talking about using the function with 2 different sets of inputs. One with only "no" and other with "yes" and email. So, what we need to do is function overloading. We need to define 2 separate signatures for TypeScript to understand. Here is how we need to do it:

function subscribe(accepted: "no"): void;
function subscribe(accepted: "yes", email: string): void;

function subscribe(accepted: "yes" | "no", email?: string): void {
  console.log(email);
}
----o----

Question:

Here is a simple TypeScript code.

function hello(this: string) {
  console.log(this);
}

hello();

Will it throw any error? If yes, please explain and how can we solve it.

Answer:

Yes. Above code throws an error.

When we define the type of this, TypeScript checks the value of invocation context when the function is invoked. When hello() is invoked, the value of this will be either undefined or window object. Both are not strings. That is the reason of the error.

In order to solve the error, we need to set the value of this explicitly using call(), apply() or bind(). Here is an example using call() that clears the error.

function hello(this: string) {
  console.log(this);
}

hello.call("my string");

call() is a static method of Function object. The first parameter passed to it is set as the value of this.

--- ○ ---
Joby Joseph
Web Architect