New 120 TypeScript Interview Question

Table of Contents

Introduction

TypeScript is a popular programming language that enhances JavaScript with static typing and additional features. If you’re preparing for a TypeScript interview, it’s essential to familiarize yourself with some common questions. These questions often focus on TypeScript’s key concepts, such as types, interfaces, classes, modules, and generics. Additionally, you may be asked about the benefits of using TypeScript, its compatibility with JavaScript, and its integration with popular frameworks like Angular or React. By understanding these fundamentals and practicing related questions, you’ll be better prepared to showcase your TypeScript knowledge and excel in your interview.

Basic Questions

1. What is TypeScript and why should one use it?

TypeScript is a statically typed superset of JavaScript that adds optional static typing to the language. It allows developers to write more robust, maintainable, and scalable code by catching type-related errors during development. TypeScript code is transpiled into JavaScript, which means it can run in any browser or Node.js environment.

Example:

TypeScript
// TypeScript code
function addNumbers(a: number, b: number): number {
  return a + b;
}

const result: number = addNumbers(5, 10);
console.log(result); // Output: 15

In this example, we define a function addNumbers that takes two parameters a and b, both of type number, and returns a value of type number. By specifying types for parameters and return values, TypeScript helps catch type-related errors early in the development process.

2. List the built-in types in TypeScript.

TypeScript includes several built-in types:

  1. number: Represents numeric values like integers or floating-point numbers.
  2. string: Represents textual data, enclosed in single or double quotes.
  3. boolean: Represents true/false values.
  4. object: Represents a non-primitive type (anything that is not number, string, boolean, symbol, null, or undefined).
  5. array: Represents an array of values of a specific type.
  6. tuple: Represents an array with a fixed number of elements, where each element may have a different type.
  7. enum: Represents a set of named constants.
  8. any: Represents a dynamic type that can hold any value, similar to regular JavaScript behavior.
  9. void: Represents the absence of a value (usually used as a return type for functions that don’t return anything).
  10. null and undefined: Represents null and undefined values, respectively.
  11. never: Represents a type for values that never occur (e.g., return type for functions that throw exceptions or have infinite loops).

3. Explain generics in TypeScript.

Generics in TypeScript allow us to create reusable components or functions that work with different types while maintaining type safety. They provide a way to parameterize types and create flexible code that can be used with various data types.

Example:

TypeScript
// Generic function to reverse an array
function reverseArray<T>(arr: T[]): T[] {
  return arr.reverse();
}

const numbers: number[] = [1, 2, 3, 4, 5];
const reversedNumbers: number[] = reverseArray(numbers);

const strings: string[] = ["apple", "banana", "orange"];
const reversedStrings: string[] = reverseArray(strings);

console.log(reversedNumbers); // Output: [5, 4, 3, 2, 1]
console.log(reversedStrings); // Output: ["orange", "banana", "apple"]

In this example, we define a generic function reverseArray that takes an array arr of type T and returns an array of the same type T. The function can be used with both number and string arrays without having to rewrite the logic for each data type.

4. What are Modules in TypeScript?

Modules in TypeScript are used to organize code into reusable and independent units. Each file is considered a separate module, and you can use the export keyword to expose elements (variables, functions, classes) that can be imported and used in other modules.

Example:

mathOperations.ts

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

export function subtract(a: number, b: number): number {
  return a - b;
}

app.ts

TypeScript
import { add, subtract } from "./mathOperations";

const result1 = add(5, 3);
const result2 = subtract(10, 4);

console.log(result1); // Output: 8
console.log(result2); // Output: 6

In this example, we have two files: mathOperations.ts containing math functions, and app.ts importing and using these functions. The export keyword allows us to make the add and subtract functions accessible in other files, and the import keyword lets us bring these functions into the app.ts file.

5. What is TypeScript, and why would I use it in place of JavaScript?

TypeScript
// TypeScript code with type annotations
function calculateArea(radius: number): number {
  return Math.PI * radius * radius;
}

const circleRadius: number = 5;
const area: number = calculateArea(circleRadius);
console.log(area); // Output: 78.53981633974483

In this example, we have a function calculateArea that takes the radius of a circle as a parameter and returns its area. The type annotations (: number) provide type safety, ensuring that the function is called with a numeric value and that the result is also of type number. If we mistakenly pass a non-numeric value, TypeScript will catch the error during development, preventing potential runtime issues.

6. Do we need to compile TypeScript files, and why?

Yes, TypeScript code needs to be compiled into regular JavaScript before it can be executed in a browser or Node.js environment. The reason for this is that browsers and Node.js only understand JavaScript, not TypeScript. The TypeScript compiler (tsc) converts TypeScript code into JavaScript code, preserving the intended behavior and type annotations.

During compilation, TypeScript performs various tasks:

  1. Type Checking: It checks the code for type errors and ensures type safety.
  2. Transpilation: It transforms modern TypeScript features into compatible JavaScript code that works across different platforms and browsers.
  3. ECMAScript Targeting: It allows targeting specific ECMAScript versions based on the project’s requirements (e.g., ES5, ES6, etc.).
  4. Bundling: It can bundle multiple TypeScript files into a single output file for optimization.

7. What are the benefits of TypeScript?

  • Enhanced Type Safety: TypeScript adds static typing, catching type-related errors during development, leading to more reliable and robust code.
  • Improved Tooling: TypeScript provides better tooling support with code editors and IDEs, offering features like autocompletion, type inference, and code navigation.
  • Readability and Maintainability: The use of type annotations makes the codebase more self-documenting, enhancing readability and maintainability.
  • Code Refactoring: With type information, refactoring becomes more comfortable and safer as the compiler can find references to the renamed elements.
  • Easier Collaboration: Type annotations make it easier for team members to understand each other’s code, reducing communication overhead.
  • Catching Errors Early: The type checking during development catches many potential bugs before the code reaches the runtime environment.
  • IDE Support: Editors and IDEs provide helpful suggestions and auto-completions due to TypeScript’s static typing, improving developer productivity.
  • Compatibility: TypeScript code can be transpiled to target specific ECMAScript versions, making it compatible with various browsers and Node.js environments.

8. How to call the base class constructor from a child class in TypeScript?

In TypeScript, to call the base class constructor from a child class, we use the super keyword within the child class constructor.

Example:

TypeScript
class Animal {
  constructor(private name: string) {}

  makeSound() {
    console.log("Animal sound");
  }
}

class Dog extends Animal {
  constructor(name: string, private breed: string) {
    super(name); // Calling the base class constructor
  }

  makeSound() {
    console.log("Bark!");
  }

  displayInfo() {
    console.log(`Name: ${this.name}, Breed: ${this.breed}`);
  }
}

const dog1 = new Dog("Buddy", "Golden Retriever");
dog1.makeSound(); // Output: Bark!
dog1.displayInfo(); // Output: Name: Buddy, Breed: Golden Retriever

In this example, we have a base class Animal with a constructor that takes a name parameter. The child class Dog extends Animal, and in its constructor, we call the base class constructor using super(name). This ensures that the name property from the base class is properly initialized.

10. How to perform string interpolation in TypeScript?

String interpolation in TypeScript can be done using template literals, which are enclosed in backticks () and allow embedding expressions using ${expression} syntax.

Example:

TypeScript
function greet(name: string): string {
  return `Hello, ${name}!`;
}

const personName: string = "Alice";
const greeting: string = greet(personName);
console.log(greeting); // Output: Hello, Alice!

In this example, we use string interpolation to embed the name variable inside the greeting message using ${name}.

11. What is the difference between .ts and .tsx extensions in TypeScript?

File ExtensionPurposeJSX Support
.tsUsed for regular TypeScript files without JSX syntaxNo
.tsxUsed for TypeScript files with JSX syntaxYes

The .ts extension is used for regular TypeScript files that do not contain JSX syntax. The .tsx extension is used for TypeScript files that include JSX syntax, which is used for working with React components and other JSX-based frameworks or libraries.

12. What is the difference between Classes and Interfaces in TypeScript?

Classes:

  • Classes are blueprint for creating objects with properties and methods.
  • They can have constructors to initialize object properties.
  • Classes support inheritance and encapsulation.

Example:

TypeScript
class Animal {
  constructor(private name: string) {}

  makeSound() {
    console.log("Animal sound");
  }
}

const dog = new Animal("Buddy");
console.log(dog.name); // Error: 'name' is private and only accessible within class 'Animal'

Interfaces:

  • Interfaces define the structure of an object.
  • They cannot be instantiated directly and do not contain implementation details.
  • Interfaces support multiple inheritance, allowing a class to implement multiple interfaces.

Example:

TypeScript
interface Animal {
  name: string;
  makeSound(): void;
}

class Dog implements Animal {
  constructor(public name: string) {}

  makeSound() {
    console.log("Bark!");
  }
}

const dog = new Dog("Buddy");
console.log(dog.name); // Output: Buddy

In this example, we have a Animal class with a constructor and a makeSound method. On the other hand, we have an Animal interface with the same structure. While the class defines the implementation details, the interface defines the contract that a class must adhere to.

13. What is an Interface in TypeScript?

In TypeScript, an interface is a structural contract that defines the shape of an object. It describes the properties and methods an object must have without specifying the implementation.

Example:

TypeScript
interface Person {
  name: string;
  age: number;
  sayHello(): void;
}

class Student implements Person {
  constructor(public name: string, public age: number) {}

  sayHello() {
    console.log(`Hello, my name is ${this.name}, and I'm ${this.age} years old.`);
  }
}

const student = new Student("Alice", 25);
student.sayHello(); // Output: Hello, my name is Alice, and I'm 25 years old.

In this example, we define an interface Person with name, age, and sayHello properties. The Student class implements the Person interface, ensuring that it adheres to the contract specified by the interface.

14. What are Decorators in TypeScript?

Decorators in TypeScript are a feature inspired by the ESNext proposal and are used to modify or enhance classes, methods, properties, or parameters. They are prefixed with an @ symbol and are followed by a decorator factory function.

Example:

TypeScript
function myDecorator(target: any, propertyKey: string) {
  console.log(`Decorating property ${propertyKey} of class ${target.constructor.name}`);
}

class MyClass {
  @myDecorator
  myProperty: string = "Hello, decorators!";
}

const obj = new MyClass();

In this example, we define a decorator function myDecorator, which logs information about the property being decorated. The decorator is applied to the myProperty of the MyClass class. When an instance of MyClass is created, the decorator is executed, and the output will be: “Decorating property myProperty of class MyClass”.

15. What are the differences between TypeScript and JavaScript?

TypeScriptJavaScript
Statically typed languageDynamically typed language
Supports optional static typingNo static typing (all types determined at runtime)
Provides type checking during developmentType checking only at runtime (may cause runtime errors)
Allows the use of interfaces and genericsNo interfaces, limited support for generics
Supports modern ECMAScript featuresSupports ECMAScript features based on the environment
Needs compilation to JavaScript for executionCan be directly run in browsers and Node.js environment
Enhanced tooling supportLimited tooling support

16. When to use interfaces and when to use classes in TypeScript?

Interfaces:

  • Use interfaces when you need to describe the structure of an object and its contract.
  • Use interfaces when you want to enforce a specific shape on objects but don’t need implementation details.
  • Interfaces are useful when defining contracts that multiple classes can implement.

Example:

TypeScript
interface Shape {
  calculateArea(): number;
}

class Circle implements Shape {
  constructor(private radius: number) {}

  calculateArea() {
    return Math.PI * this.radius * this.radius;
  }
}

class Square implements Shape {
  constructor(private sideLength: number) {}

  calculateArea() {
    return this.sideLength * this.sideLength;
  }
}

const circle = new Circle(5);
console.log(circle.calculateArea()); // Output: 78.53981633974483

const square = new Square(4);
console.log(square.calculateArea()); // Output: 16

Classes:

  • Use classes when you need to create objects with both properties and methods.
  • Use classes when you need to encapsulate data and behavior within the same unit.
  • Classes support constructors, inheritance, and access modifiers (e.g., private, protected).

Example:

TypeScript
class Person {
  constructor(private name: string,

 private age: number) {}

  sayHello() {
    console.log(`Hello, my name is ${this.name}, and I'm ${this.age} years old.`);
  }
}

const person = new Person("Alice", 25);
person.sayHello(); // Output: Hello, my name is Alice, and I'm 25 years old.

In this example, we use interfaces to define the Shape contract with a method calculateArea, and then we implement that interface in the Circle and Square classes. On the other hand, we use a class to create a Person object with properties and methods, encapsulating data and behavior within the same unit.

17. How to implement class constants in TypeScript?

In TypeScript, class constants can be implemented using the static keyword within the class. These constants belong to the class and not to individual instances.

Example:

TypeScript
class MathOperations {
  static readonly PI: number = 3.14159;

  static calculateArea(radius: number): number {
    return MathOperations.PI * radius * radius;
  }
}

const circleRadius: number = 5;
const area: number = MathOperations.calculateArea(circleRadius);
console.log(area); // Output: 78.53975

In this example, we define a MathOperations class with a PI constant marked as readonly and static. The readonly modifier ensures that the constant cannot be modified after it is initialized. We can access the constant using the class name without creating an instance of the class.

18. How could you check null and undefined in TypeScript?

In TypeScript, you can check for null and undefined using strict equality (=== or !==) or optional chaining (?.) for safer property access.

Example:

TypeScript
function processValue(value: string | null | undefined): void {
  if (value === null || value === undefined) {
    console.log("Value is null or undefined.");
  } else {
    console.log("Value is:", value);
  }
}

const stringValue: string | null = "Hello";
processValue(stringValue); // Output: Value is: Hello

const nullValue: null = null;
processValue(nullValue); // Output: Value is null or undefined

const undefinedValue: undefined = undefined;
processValue(undefinedValue); // Output: Value is null or undefined

In this example, we have a function processValue that takes a parameter of type string, null, or undefined. We check for null and undefined using strict equality (===), and if the value is either null or undefined, we log an appropriate message.

19. Does TypeScript support all object-oriented principles?

Yes, TypeScript supports all object-oriented principles like encapsulation, inheritance, and polymorphism.

Example:

TypeScript
// Encapsulation: Using private and public access modifiers
class Circle {
  constructor(private radius: number) {}

  getRadius(): number {
    return this.radius;
  }

  setRadius(radius: number): void {
    if (radius >= 0) {
      this.radius = radius;
    }
  }

  calculateArea(): number {
    return Math.PI * this.radius * this.radius;
  }
}

const circle = new Circle(5);
circle.setRadius(10);
console.log(circle.getRadius()); // Output: 10
console.log(circle.calculateArea()); // Output: 314.1592653589793

// Inheritance
class Square extends Circle {
  constructor(radius: number, private sideLength: number) {
    super(radius);
  }

  calculateArea(): number {
    return this.sideLength * this.sideLength;
  }
}

const square = new Square(5, 4);
console.log(square.calculateArea()); // Output: 16

// Polymorphism
function printArea(shape: Circle | Square): void {
  console.log("Area:", shape.calculateArea());
}

printArea(circle); // Output: Area: 314.1592653589793
printArea(square); // Output: Area: 16

In this example, we demonstrate the object-oriented principles of encapsulation by using private and public access modifiers in the Circle class. We showcase inheritance by creating a Square class that extends Circle, inheriting its properties and methods. Finally, we demonstrate polymorphism by using the same function printArea to calculate and display the area of both a Circle and a Square.

20. Which object-oriented terms are supported by TypeScript?

TypeScript supports the following object-oriented terms:

  1. Classes: Used to define blueprints for creating objects with properties and methods.
  2. Objects: Instances created from classes.
  3. Properties: Data members or attributes of an object.
  4. Methods: Functions that belong to objects and perform specific actions.
  5. Inheritance: The ability of a class to inherit properties and methods from another class.
  6. Encapsulation: The bundling of data and methods within a class to hide implementation details.
  7. Polymorphism: The ability of different classes to be treated as instances of a common superclass.

21. What are getters/setters in TypeScript?

Getters and setters are special methods in TypeScript (and JavaScript) used to access and modify the properties of an object. They provide a way to control how properties are accessed and set.

Example:

TypeScript
class Person {
  private _age: number;

  constructor(private _name: string, age: number) {
    this._age = age;
  }

  // Getter for 'name' property
  get name(): string {
    return this._name;
  }

  // Setter for 'name' property
  set name(newName: string) {
    this._name = newName.trim();
  }

  // Getter for 'age' property
  get age(): number {
    return this._age;
  }

  // Setter for 'age' property
  set age(newAge: number) {
    if (newAge >= 0) {
      this._age = newAge;
    }
  }
}

const person = new Person("Alice", 30);
console.log(person.name); // Output: Alice

person.name = " Bob  ";
console.log(person.name); // Output: Bob

console.log(person.age); // Output: 30

person.age = -5; // Setter won't update the age because it's negative
console.log(person.age); // Output: 30

In this example, we define a Person class with private properties _name and _age. We provide getter and setter methods for each property, allowing controlled access and modification of the properties. The name setter, for example, trims any leading or trailing spaces from the provided value before updating the _name property.

22. Could we use TypeScript on the backend, and how?

Yes, TypeScript can be used on the backend, allowing developers to write server-side applications using TypeScript. Here’s how you can do it:

  1. Node.js Environment: TypeScript can be used with Node.js, as both TypeScript and Node.js use the same JavaScript runtime. You can install TypeScript globally using npm and compile TypeScript files to JavaScript using the TypeScript compiler (tsc).
  2. Express.js Framework: TypeScript can be used with Express.js, a popular web application framework for Node.js. By using TypeScript, you get the benefits of static typing, better IDE support, and improved code organization.
  3. Nest.js Framework: Nest.js is a progressive Node.js framework built with TypeScript. It provides a solid architectural design and leverages TypeScript decorators for dependency injection and routing.

Example:

TypeScript
// server.ts
import express from "express";

const app = express();
const port = 3000;

app.get("/", (req, res) => {
  res.send("Hello, TypeScript on the backend!");
});

app.listen(port, () => {
  console.log(`Server is running on http://localhost:${port}`);
});

To run the TypeScript backend, compile the TypeScript file (tsc server.ts) to generate the JavaScript file and then run the JavaScript file using Node.js (node server.js).

23. What is a TypeScript Map file?

In TypeScript, a Map file (with a .map extension) is a separate file generated during the compilation process. It contains mapping information that connects the generated JavaScript code to the original TypeScript code. This helps in debugging by allowing the debugger to map the executed JavaScript code back to the corresponding TypeScript code.

The TypeScript compiler generates a .map file when the --sourceMap option is set to true in the tsconfig.json file or when using the --sourceMap command-line flag.

Example:

tsconfig.json

TypeScript
{
  "compilerOptions": {
    "sourceMap": true,
    "outDir": "dist"
  }
}

After running the TypeScript compiler (tsc), you’ll have the following files:

main.ts

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

const result: number = add(5, 10);
console.log(result);

main.js

TypeScript
function add(a, b) {
    return a + b;
}
const result = add(5, 10);
console.log(result);

main.js.map

TypeScript
{"version":3,"file":"main.js","sourceRoot":"","sources":["main.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC"}

The .map file contains JSON data that represents the mapping between the original main.ts file and the generated main.js file.

24. What is the difference between types String and string in TypeScript?

Type NamePurposeType Definition
StringRepresents a String object (primitive wrapper)String('Hello')
stringRepresents a string primitive type'Hello' or new String()

The String type refers to the String object (wrapper object) in JavaScript, whereas string refers to the string primitive type. It is recommended to use the string type when working with string values as it is more natural and straightforward.

25. What is Type Erasure in TypeScript?

Type Erasure in TypeScript refers to the process of removing type annotations during the compilation process, leaving only the JavaScript code with no explicit type information. This is done to maintain compatibility with ECMAScript, which doesn’t support static types.

Example:

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

const result: number = addNumbers(5, 10);
console.log(result); // Output: 15

In this example, the TypeScript code contains type annotations, but after compilation, the resulting JavaScript code will have no type information:

TypeScript
function addNumbers(a, b) {
  return a + b;
}

const result = addNumbers(5, 10);
console.log(result); // Output: 15

The type information (: number) has been erased in the compiled JavaScript code.

26. How do we create an enum with string values?

To create an enum with string values in TypeScript, we can assign string literals to each enum member.

Example:

TypeScript
enum Color {
  Red = "RED",
  Green = "GREEN",
  Blue = "BLUE",
}

function printColor(color: Color): void {
  console.log("Selected color:", color);
}

printColor(Color.Green); // Output: Selected color: GREEN

In this example, we create an enum Color with string values using string literals as the enum members. The printColor function takes an argument of type Color and logs the selected color.

27. What does the pipe (|) mean in TypeScript?

In TypeScript, the pipe (|) symbol is used to create a union type. A union type allows a variable or parameter to have multiple types.

Example:

TypeScript
function printResult(value: string | number): void {
  console.log("Result:", value);
}

printResult("Hello"); // Output: Result: Hello
printResult(42); // Output: Result: 42

In this example, the printResult function takes an argument of type string or number, allowing us to pass either a string or a number.

28. Describe what conditional types are in TypeScript?

Conditional types in TypeScript are used to create a type that depends on a condition. They use the infer keyword to infer the type based on the condition. Conditional types are often used with generic types to create more flexible and reusable type definitions.

Example:

TypeScript
type IsNumber<T> = T extends number ? true : false;

const result1: IsNumber<42> = true;
const result2: IsNumber<"Hello"> = false;

console.log(result1); // Output: true
console.log(result2); // Output: false

In this example, we define a conditional type IsNumber, which checks if the given type T is a number or not. If T is a number, the type IsNumber<T> will be true; otherwise, it will be false.

29. How to make Arrays that can only be read, TypeScript?

In TypeScript, you can create read-only arrays using the ReadonlyArray type or the readonly modifier.

Example 1: Using ReadonlyArray type

TypeScript
const readOnlyNumbers: ReadonlyArray<number> = [1, 2, 3, 4, 5];
readOnlyNumbers[0] = 10; // Error: Index signature in type 'readonly number[]' only permits reading

Example 2: Using readonly modifier

TypeScript
const readOnlyColors: readonly string[] = ["red", "green", "blue"];
readOnlyColors[0] = "yellow"; // Error: Index signature in type 'readonly string[]' only permits reading

In both examples, we create read-only arrays using either ReadonlyArray<number> or readonly string[]. Attempting to modify the elements of these arrays will result in a compilation error.

30. How can we use optional chaining in TypeScript?

Optional chaining is a feature introduced in TypeScript 3.7, allowing you to safely access nested properties or methods without causing a runtime error if any intermediate value is null or undefined. It uses the ?. operator.

Example:

TypeScript
interface Address {
  city?: string;
  postalCode?: string;
}

interface Person {
  name: string;
  age?: number;
  address?: Address;
}

function getPostalCode(person: Person): string | undefined {
  return person?.address?.postalCode;
}

const person1: Person = {
  name: "Alice",
  age: 30,
  address: {
    city: "New York",
    postalCode: "10001",
  },
};

const person2: Person = {
  name: "Bob",
  age: 25,
};

console.log(getPostalCode(person1)); // Output: 10001
console.log(getPostalCode(person2)); // Output: undefined

In this example, we define interfaces Address and Person. The function getPostalCode safely accesses the postalCode property using optional chaining. If any intermediate value (person, address, or postalCode) is null or undefined, the function returns undefined.

31. What is Optional Chaining in TypeScript?

Optional chaining is a feature introduced in TypeScript 3.7 that allows you to safely access nested properties or methods of an object without causing a runtime error if any intermediate value is null or undefined.

Example:

TypeScript
interface Address {
  city?: string;
  postalCode?: string;
}

interface Person {
  name: string;
  age?: number;
  address?: Address;
}

function getPostalCode(person: Person): string | undefined {
  return person?.address?.postalCode;
}

const person1: Person = {
  name: "Alice",
  age: 30,
  address: {
    city: "New York",
    postalCode: "10001",
  },
};

const person2: Person = {
  name: "Bob",
  age: 25,
};

console.log(getPostalCode(person1)); // Output: 10001
console.log(getPostalCode(person2)); // Output: undefined

In this example, we define interfaces Address and Person. The function getPostalCode safely accesses the postalCode property using optional chaining (?.). If any intermediate value (person, address, or postalCode) is null or undefined, the function returns undefined.

32. What is the purpose of the Nullish Coalescing operator?

The Nullish Coalescing operator (??) is a feature introduced in TypeScript (and JavaScript) to provide a default value for variables that are null or undefined. It returns the right-hand side operand if the left-hand side operand is null or undefined; otherwise, it returns the left-hand side operand.

Example:

TypeScript
const value1 = null;
const value2 = undefined;
const value3 = 0;

const result1 = value1 ?? "default"; // Output: "default"
const result2 = value2 ?? "default"; // Output: "default"
const result3 = value3 ?? "default"; // Output: 0

In this example, we use the Nullish Coalescing operator to provide default values for variables value1 and value2, which are null and undefined, respectively. The operator returns the right-hand side operand "default" for those cases.

33. What are assertion functions?

Assertion functions in TypeScript are user-defined functions used to assert or tell the TypeScript compiler that a certain type constraint is satisfied for a particular variable. They are useful when you know more about the type of a value than the TypeScript compiler does.

Example:

TypeScript
function assertNumber(val: any): asserts val is number {
  if (typeof val !== "number") {
    throw new Error("Assertion failed: Value is not a number");
  }
}

function multiplyByTwo(value: unknown): number {
  assertNumber(value); // Assert that 'value' is a number
  return value * 2; // TypeScript knows 'value' is a number here
}

const result1 = multiplyByTwo(5); // Output: 10
const result2 = multiplyByTwo("hello"); // Runtime error: Assertion failed: Value is not a number

In this example, we define an assertion function assertNumber that checks if the provided value is a number. If the condition is not met, it throws an error. The multiplyByTwo function uses this assertion function to assert that the value parameter is a number. This allows TypeScript to know that value is a number when performing the multiplication operation.

34. Which access modifiers are implied when not specified?

In TypeScript, if an access modifier is not specified for a class member, it is implicitly considered public. For constructors, it is implied to be public.

Example:

TypeScript
class Person {
  name: string; // Implied 'public'

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

const person = new Person("Alice");
console.log(person.name); // Output: Alice

In this example, the name property of the Person class is not specified with an access modifier, so it is implicitly public. This means that the name property can be accessed and modified from outside the class instance.

35. Explain how and why we could use property decorators in TypeScript?

Property decorators in TypeScript are used to add extra behavior or metadata to class properties. They are applied to properties of a class and receive two parameters: the target object (prototype of the class) and the property name.

Example:

TypeScript
function uppercase(target: any, propertyName: string) {
  let value: string;

  const getter = function () {
    return value;
  };

  const setter = function (newValue: string) {
    value = newValue.toUpperCase();
  };

  Object.defineProperty(target, propertyName, {
    get: getter,
    set: setter,
    enumerable: true,
    configurable: true,
  });
}

class Person {
  @uppercase
  name: string;

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

const person = new Person("Alice");
console.log(person.name); // Output: ALICE

person.name = "Bob";
console.log(person.name); // Output: BOB

In this example, we define a property decorator uppercase, which converts the assigned value to uppercase. When applied to the name property of the Person class, it transforms any assigned name to uppercase automatically.

36. What is Typings in TypeScript?

Typings in TypeScript refer to the declaration files (.d.ts) that describe the shape of JavaScript libraries or modules. They provide type information for external code that does not have TypeScript typings by default.

Example:

custom-library.d.ts

TypeScript
declare module "custom-library" {
  export function greet(name: string): string;
}

app.ts

TypeScript
import { greet } from "custom-library";

const greeting: string = greet("Alice");
console.log(greeting); // Output: Hello, Alice!

In this example, we have a custom library without TypeScript typings. We create a .d.ts file, custom-library.d.ts, where we declare the types for the functions or modules of the library. This allows TypeScript to provide type checking and better IDE support for the greet function imported in app.ts.

37. How can you allow classes defined in a module to be accessible outside of the module?

In TypeScript, by default, classes defined in a module are not accessible outside of the module. To allow classes to be accessible, you can use the export keyword before the class declaration.

Example:

math-utils.ts

TypeScript
export class MathUtils {
  static add(a: number, b: number): number {
    return a + b;
  }
}

app.ts

TypeScript
import { MathUtils } from "./math-utils";

const result: number = MathUtils.add(5, 10);
console.log(result); // Output: 15

In this example, we define a MathUtils class in math-utils.ts and use the export keyword to make it accessible outside the module. We then import the class in app.ts using the import statement and use it to perform addition.

38. How TypeScript is optionally a statically typed language?

TypeScript is optionally a statically typed language because it allows developers to choose whether to use static types or not. TypeScript provides a way to define types for variables, parameters, and return values, but it also allows dynamic typing when types are not explicitly specified.

Developers can choose to:

  1. Explicitly Specify Types: By providing type annotations, TypeScript enforces static typing and performs type checking during development.

Example:

TypeScript
function add(a: number, b: number): number {
  return a + b;
}
  1. Let TypeScript Infer Types: If type annotations are not provided, TypeScript can infer types from the assigned values and perform type checking based on the context.

Example:

TypeScript
function add(a, b) {
  return a + b; // TypeScript infers the return type as 'number'
}

39. What is the default access modifier for members of a class in TypeScript?

In TypeScript, if an access modifier is not explicitly specified for a class member, it is implicitly considered public.

Example:

TypeScript
class Person {
  name: string; // Implied 'public'

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

const person = new Person("Alice");
console.log(person.name); // Output: Alice

In this example, the name property of the Person class is not specified with an access modifier, so it is implicitly considered public. This means that the name property can be accessed and modified from outside the class instance.

40. What are different components of TypeScript?

The different components of TypeScript are as follows:

  1. TypeScript Compiler (tsc): The TypeScript compiler, tsc, converts TypeScript code (.ts files) into JavaScript code (.js files) and optionally generates source map files (.map) for debugging purposes.
  2. TypeScript Language Service: The TypeScript Language Service provides rich IDE support, offering features like code completion, code navigation, and error checking while typing. It enhances the development experience in TypeScript-aware editors and IDEs.
  3. Declaration Files (.d.ts): Declaration files (.d.ts) contain type information for existing JavaScript libraries and modules. They allow TypeScript to understand and provide type checking for external JavaScript code.
  4. TypeScript Language Specification: The TypeScript language specification defines the syntax, semantics, and behavior of the TypeScript language. It serves as the official reference for the language features and rules.
  5. TypeScript Playground: The TypeScript Playground is an online tool provided by the TypeScript team that allows developers to write and experiment with TypeScript code in a web-based environment. It includes a code editor and shows the resulting JavaScript output.

Intermediate Questions

41. How to use external plain JavaScript libraries in TypeScript?

To use external plain JavaScript libraries in TypeScript, you typically need to install the TypeScript type definitions for those libraries using DefinitelyTyped or other community-driven type definition repositories. These type definitions provide TypeScript with information about the library’s types, allowing for better type checking and IntelliSense support.

Here’s a step-by-step example using the popular library “lodash”:

  1. Install the library and its type definitions:
TypeScript
npm install lodash
npm install @types/lodash --save-dev
  1. Create a TypeScript file (e.g., “app.ts”) and import the library as follows:
TypeScript
import * as _ from 'lodash';

// Now you can use lodash functions with type support
const numbers: number[] = [1, 2, 3, 4, 5];
const sum = _.sum(numbers);
console.log(sum); // Output: 15

42. Does TypeScript support function overloading?

Yes, TypeScript supports function overloading. Function overloading allows you to define multiple function signatures for a single function based on different parameter types or numbers of parameters.

Here’s an example of function overloading:

TypeScript
function greet(name: string): string;
function greet(age: number): string;
function greet(arg: any): string {
  if (typeof arg === 'string') {
    return `Hello, ${arg}!`;
  } else if (typeof arg === 'number') {
    return `You are ${arg} years old.`;
  } else {
    throw new Error('Invalid argument type.');
  }
}

console.log(greet('John')); // Output: Hello, John!
console.log(greet(30)); // Output: You are 30 years old.

In this example, we have two function signatures for the greet function: one that takes a string argument and another that takes a number argument. The actual function implementation handles both cases.

43. What is the difference between Private and Protected variables in TypeScript?

In TypeScript, private and protected are access modifiers used to control the visibility of class members.

  1. private: When a member is marked as private, it can only be accessed within the class where it is defined. It is not accessible from outside the class, including its subclasses.
TypeScript
class MyClass {
  private privateVar: number = 10;

  private privateMethod() {
    console.log('This is a private method.');
  }
}

const instance = new MyClass();
console.log(instance.privateVar); // Error: Property 'privateVar' is private and only accessible within class 'MyClass'.
instance.privateMethod(); // Error: Property 'privateMethod' is private and only accessible within class 'MyClass'.
  1. protected: When a member is marked as protected, it can be accessed within the class where it is defined and its subclasses.
TypeScript
class ParentClass {
  protected protectedVar: number = 20;

  protected protectedMethod() {
    console.log('This is a protected method.');
  }
}

class ChildClass extends ParentClass {
  public accessProtected() {
    console.log(this.protectedVar); // Accessible in subclass
    this.protectedMethod(); // Accessible in subclass
  }
}

const childInstance = new ChildClass();
childInstance.accessProtected(); // Output: 20, This is a protected method.
console.log(childInstance.protectedVar); // Error: Property 'protectedVar' is protected and only accessible within class 'ParentClass' and its subclasses.
childInstance.protectedMethod(); // Error: Property 'protectedMethod' is protected and only accessible within class 'ParentClass' and its subclasses.

44. What is the difference between enum and const enums?

In TypeScript, both enum and const enum are used to define a set of named constants. However, there is a difference in how they are emitted to JavaScript code.

  1. enum: When you define an enum in TypeScript, it generates a JavaScript object at runtime that maps the enum members to their numeric values. Enums support both numeric and string values.
TypeScript
enum Color {
  Red,
  Green,
  Blue,
}

console.log(Color.Red); // Output: 0
console.log(Color[0]); // Output: Red
  1. const enum: When you define a const enum, it is removed during the transpilation process, and its values are inlined directly into the generated JavaScript code. This results in more efficient code but removes the object mapping present in regular enums. const enum can only have numeric values.
TypeScript
const enum Fruit {
  Apple = 1,
  Orange = 2,
  Banana = 3,
}

console.log(Fruit.Apple); // Output: 1
// console.log(Fruit[1]); // Error: 'Fruit' only refers to a value, but is being used as a type here.

The const enum example will not have an object mapping like enum does, and accessing enum members using reverse lookup (e.g., Fruit[1]) will result in a compilation error.

45. Why do we need to use the abstract keyword for classes and their methods in TypeScript?

The abstract keyword is used to define abstract classes and methods in TypeScript. An abstract class cannot be instantiated directly; instead, it serves as a blueprint for its subclasses to implement their own versions of abstract methods.

Here’s an example of using the abstract keyword:

TypeScript
abstract class Shape {
  abstract calculateArea(): number;

  sayHello(): void {
    console.log('Hello from Shape!');
  }
}

class Circle extends Shape {
  constructor(private radius: number) {
    super();
  }

  calculateArea(): number {
    return Math.PI * this.radius ** 2;
  }
}

const circle = new Circle(5);
console.log(circle.calculateArea()); // Output: 78.53981633974483
circle.sayHello(); // Output: Hello from Shape!

In this example, the Shape class is marked as abstract, and it has an abstract method calculateArea(). The Circle class extends Shape and provides its implementation of the calculateArea() method. The Circle class can be instantiated, but the Shape class cannot, as it is abstract.

46. What is Structural Typing?

Structural Typing, also known as “duck typing,” is a type system used by TypeScript where the type compatibility is based on the shape or structure of the types, rather than their explicit declaration or inheritance. If two types have compatible structures, TypeScript considers them to be assignable to each other, regardless of their names.

Here’s an example of Structural Typing:

TypeScript
interface Point2D {
  x: number;
  y: number;
}

interface

 Shape {
  name: string;
  draw(): void;
}

function printCoordinates(point: Point2D) {
  console.log(`Coordinates: x=${point.x}, y=${point.y}`);
}

const point: Point2D = { x: 10, y: 20 };
printCoordinates(point); // Output: Coordinates: x=10, y=20

const circle: Shape = {
  name: 'Circle',
  draw: () => console.log('Drawing a circle.'),
};

printCoordinates(circle); // Output: Coordinates: x=undefined, y=undefined

In this example, even though circle doesn’t strictly conform to the Point2D interface, it has the required x and y properties. TypeScript allows circle to be passed to printCoordinates because they have compatible shapes.

47. What is Mixin Class in TypeScript?

In TypeScript, a Mixin is a way to add behavior to a class by combining the features of multiple classes without using traditional inheritance. It’s achieved by creating classes with specific behaviors and then mixing those behaviors into other classes.

Here’s an example of a Mixin:

TypeScript
// Mixin classes
class Printable {
  print(): void {
    console.log('Printing...');
  }
}

class Loggable {
  log(): void {
    console.log('Logging...');
  }
}

// Target class
class Document implements Printable, Loggable {
  print: () => void;
  log: () => void;

  constructor(private content: string) {}

  // Implement methods from mixins
  print: Printable['print'] = Printable.prototype.print;
  log: Loggable['log'] = Loggable.prototype.log;

  getContent(): string {
    return this.content;
  }
}

const document = new Document('This is a document.');
document.print(); // Output: Printing...
document.log(); // Output: Logging...
console.log(document.getContent()); // Output: This is a document.

In this example, we define two Mixin classes, Printable and Loggable, each providing specific behavior (print and log, respectively). The Document class implements both mixins, effectively combining their functionality into the Document class.

48. What is the unique symbol used for?

Unique symbols in TypeScript are used to create unique and immutable values that can be used as property keys in objects. They help prevent accidental name collisions when defining properties on objects.

Here’s an example of using unique symbols:

TypeScript
// Create a unique symbol
const userIdSymbol = Symbol('user_id');

// Define an object with a property using the unique symbol
const user = {
  name: 'John Doe',
  [userIdSymbol]: 12345,
};

console.log(user.name); // Output: John Doe
console.log(user[userIdSymbol]); // Output: 12345

// Trying to access the property using a string will not work
console.log(user['user_id']); // Output: undefined

// Using a for...in loop won't show the symbol property
for (const key in user) {
  console.log(key); // Output: name
}

In this example, we create a unique symbol userIdSymbol and use it as a property key in the user object. The symbol property is accessible using the symbol itself but not with the string 'user_id'. Also, the for...in loop does not include the symbol property, protecting it from accidental enumeration.

49. How to make a read-only tuple type in TypeScript?

In TypeScript, you can create a read-only tuple type by using the readonly modifier in combination with square brackets [] to define a tuple type. Once defined as readonly, the tuple elements cannot be modified after initialization.

Here’s an example of a read-only tuple type:

TypeScript
// Define a read-only tuple type
type Point = readonly [number, number];

// Create a read-only tuple
const point: Point = [10, 20];

// Attempt to modify the tuple (will result in a compilation error)
// point[0] = 5; // Error: Index signature in type 'readonly [number, number]' only permits reading
// point.push(30); // Error: Property 'push' does not exist on type 'readonly [number, number]'

// Access elements
console.log(point[0]); // Output: 10
console.log(point[1]); // Output: 20

In this example, the Point type is defined as a read-only tuple type with two elements of type number. When creating the point variable, we initialize it with a specific tuple. Any attempt to modify the tuple, like assigning new values or using methods like push, will result in a compilation error.

50. Explain Project References and their benefits.

Project References is a feature in TypeScript that allows breaking up large TypeScript projects into smaller, manageable pieces and improves build times and developer experience. The benefits of using Project References include:

  • Improved Build Times: By splitting the project into smaller sub-projects, TypeScript can perform incremental compilation for individual sub-projects, reducing build times when only specific parts of the codebase change.
  • Parallel Compilation: When using Project References, TypeScript can compile sub-projects in parallel, leveraging multiple CPU cores, further speeding up the build process.
  • Better Isolation: Each sub-project can have its own configuration and dependencies, making it easier to manage and isolate concerns.
  • Avoiding Duplicate Work: If multiple sub-projects depend on the same codebase, TypeScript intelligently reuses the compiled output, avoiding duplicate compilation.
  • Enhanced Editor Experience: Editors like Visual Studio Code can benefit from Project References to provide accurate IntelliSense and type checking within each sub-project.
  • Incremental Refactoring: Project References facilitate incremental refactoring, as changes in one sub-project can be reflected across dependent sub-projects, ensuring consistency.
  • Clearer Project Structure: The use of Project References enforces a clear project structure and dependency hierarchy, leading to better organization and maintainability.

51. What are the use cases for a const assertion?

A const assertion is used in TypeScript to indicate that a value should be treated as a literal constant when inferring types. It’s denoted by appending as const to a value or expression.

Use cases for const assertions include:

  1. Creating Immutable Arrays:
TypeScript
const colors = ['red', 'green', 'blue'] as const;
// colors: readonly ['red', 'green', 'blue']
  1. Creating Readonly Objects:
TypeScript
const person = {
  name: 'John',
  age: 30,
} as const;
// person: { readonly name: "John"; readonly age: 30; }
  1. Preventing Narrowing of Literal Types:
TypeScript
const value = 'Hello' as const;
let newValue: typeof value; // Without as const, newValue would be of type "string".
  1. Enhancing Discriminated Unions:
TypeScript
type Shape =
  | { type: 'circle'; radius: number }
  | { type: 'rectangle'; width: number; height: number }
  | { type: 'square'; sideLength: number };

const

 circle: Shape = { type: 'circle', radius: 5 } as const;

In this example, the const assertion ensures that the circle object is treated as a literal type with the property values 'circle' and 5. Without as const, TypeScript would infer a wider type for circle.

52. How to choose between never, unknown, and any in TypeScript?

In TypeScript, the choice between never, unknown, and any depends on the desired level of type safety and information provided by the type.

  • any: Use any when you want to opt-out of type checking and provide maximum flexibility. It essentially turns off TypeScript’s type checking for the variable.
TypeScript
let data: any = 10;
data = 'Hello';
data = true;
  • unknown: Use unknown when you have data of an unknown type and need type safety. You have to perform a type check or assertion before using the value.
TypeScript
let data: unknown = 10;
if (typeof data === 'number') {
  const result = data + 5;
}
  • never: Use never when you want to represent a value that will never occur, like in the case of functions that throw errors or never return.
TypeScript
function throwError(message: string): never {
  throw new Error(message);
}

function infiniteLoop(): never {
  while (true) {}
}

The choice depends on the specific use case and how much type safety you want. any provides the least type safety but allows maximum flexibility. unknown provides better type safety compared to any but requires explicit type checks. never is used to represent unreachable or error-throwing code paths.

53. What is the fundamental difference between Optional Chaining (?.) and Non-null assertion operator (!) in TypeScript?

The fundamental difference between Optional Chaining (?.) and the Non-null Assertion Operator (!) in TypeScript lies in their behavior when dealing with potentially undefined or null values.

  • Optional Chaining (?.): The optional chaining operator allows you to safely access properties or methods on an object that may be undefined or null. If the property or method doesn’t exist, the expression will return undefined instead of throwing an error.
TypeScript
interface Person {
  name: string;
  address?: {
    city: string;
    zipcode?: string;
  };
}

const person: Person = {
  name: 'John',
};

const city = person.address?.city;
console.log(city); // Output: undefined
  • Non-null Assertion Operator (!): The non-null assertion operator is used to tell TypeScript that you are certain that a value will not be null or undefined at a specific point in the code. It is a way to bypass the type checker’s nullability check.
TypeScript
const name: string | undefined = 'John';
const nonNullName: string = name!;
console.log(nonNullName); // Output: John

The ! operator can be risky if misused since it bypasses type safety checks. It should only be used when you are sure that a value is not null or undefined.

54. What does Short-Circuiting mean in TypeScript?

In TypeScript (and other programming languages), short-circuiting refers to the behavior of logical operators (&& and ||) to stop evaluating expressions as soon as the result can be determined. It means that if the result of an expression can be determined early, based on the value of the first operand, the second operand is not evaluated at all.

Here are examples of short-circuiting:

  1. Using && (Logical AND):
TypeScript
function doSomething() {
  console.log('doSomething function called.');
  return true;
}

const result = false && doSomething();
console.log(result); // Output: false

In this example, since the first operand (false) in the && expression is false, the second operand (doSomething()) is not evaluated, and the function doSomething is not called.

  1. Using || (Logical OR):
TypeScript
function doSomething() {
  console.log('doSomething function called.');
  return true;
}

const result = true || doSomething();
console.log(result); // Output: true

In this example, since the first operand (true) in the || expression is true, the second operand (doSomething()) is not evaluated, and the function doSomething is not called.

55. List a few rules of private fields in TypeScript.

In TypeScript, private fields have the following rules:

  1. Private fields can only be accessed within the class where they are defined. They are not accessible from outside the class, including its subclasses.
  2. Private fields cannot be accessed or modified by external code, ensuring encapsulation.
  3. Private fields are denoted with the private keyword followed by the field name.
  4. Private fields cannot be used in interfaces, as they are implementation details.
  5. Private fields are not inherited by subclasses. A subclass can define its own private fields.
  6. Private fields do not participate in structural typing, meaning two classes with identical private fields are not considered the same type.

Here’s an example illustrating these rules:

TypeScript
class MyClass {
  private privateVar: number = 10;

  private privateMethod() {
    console.log('This is a private method.');
  }

  public accessPrivate() {
    console.log(this.privateVar); // Allowed within the class
    this.privateMethod(); // Allowed within the class
  }
}

const instance = new MyClass();
console.log(instance.privateVar); // Error: Property 'privateVar' is private and only accessible within class 'MyClass'.
instance.privateMethod(); // Error: Property 'privateMethod' is private and only accessible within class 'MyClass'.
instance.accessPrivate(); // Output: 10, This is a private method.

56. What are some use cases of template literal types in TypeScript?

Template Literal Types in TypeScript allow you to create complex types by using template literal syntax. They are especially useful in scenarios where you want to generate types based on existing types or combine strings in a type-safe manner.

Use cases of template literal types include:

  1. Generating Union Types:
TypeScript
type StatusCode = 200 | 404 | 500;
type ApiResponse<T> = { status: StatusCode; data: T };
type ResponseType<T> = ApiResponse<T> | `${StatusCode} Error`;

In this example, the ResponseType is a union type combining ApiResponse<T> with string literals representing error status codes.

  1. Creating Mapped Types:
TypeScript
type RequestPayload = {
  endpoint: string;
  method: string;
  body: any;
};

type ApiResponse<T> = {
  data: T;
  statusCode: number;
};

type ApiRequest<T> = {
  [K in keyof RequestPayload]: ApiResponse<T>;
};

Here, ApiRequest<T> creates a mapped type by using keyof RequestPayload to map each property of RequestPayload to ApiResponse<T>.

  1. Formatting Strings Based on Types:
TypeScript
type EventTypes = 'click' | 'input' | 'submit';
type EventPayloads = {
  [T in EventTypes]: { type: T; data: string };
};

In this example, EventPayloads maps each event type to an object containing the event type and corresponding data.

57. How to check the type of a variable or constant in TypeScript?

In TypeScript, you can use the typeof operator or the instanceof operator to check the type of a variable or constant.

  1. Using typeof:
TypeScript
const x = 42;

if (typeof x === 'number') {
  console.log('x is a number.');
} else {
  console.log('x is not a number.');
}
  1. Using instanceof:
TypeScript
class Person {
  name: string;
  age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}

const person = new Person('John', 30);

if (person instanceof Person) {
  console.log('person is an instance of Person.');
} else {
  console.log('person is not an instance of Person.');
}

Both methods are valid ways to check the type of a variable or constant. Use typeof when you want to check primitive types, and use instanceof when you want to check for custom class instances.

58. How to add types to an interface from another interface or extend types in TypeScript?

In TypeScript, you can add types to an interface from another interface using the extends keyword. This allows you to create new interfaces that inherit properties and methods from other interfaces.

Here’s an example of extending interfaces:

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

interface Employee extends Person {
  department: string;
}

const employee: Employee = {
  name: 'John Doe',
  age: 30,
  department: 'HR',
};

In this example, the Employee interface extends the Person interface, inheriting its properties name and age. The employee object conforms to the Employee interface, containing all the properties defined in both Person and Employee.

You can also extend multiple interfaces:

TypeScript
interface Shape {
  color: string;
}

interface Circle extends Shape {
  radius: number;
}

interface Square extends Shape {
  sideLength: number;
}

const circle: Circle = {
  color: 'red',
  radius: 5,
};

const square: Square = {
  color: 'blue',
  sideLength: 10,
};

In this example, both the Circle and Square interfaces extend the Shape interface, sharing its color property while adding their own specific properties (radius for Circle and sideLength for Square).

59. What is the difference between type and interface in TypeScript?

FeatureTypeInterface
Use keywordtypeinterface
Extending other typesSupportedSupported
Merging declarationsNot supportedSupported
Declaration mergingNot supportedSupported
Multiple declarationsNot allowed for the same type nameAllowed for the same interface name
Function signaturesCan define function typesCan define function signatures
Object literal shapesCan define object literalsCan define object literals
Implementing by classesNot possiblePossible (with optional methods)
AugmentationNot possiblePossible (through declaration merging)

60. What is the difference between interface vs type statements?

FeatureInterfaceType
Syntaxinterface keywordtype keyword
Object ShapeDefines the shape of an objectCan define complex types like unions, intersections, and tuples
Extending other typesSupportedSupported
Declaration mergingSupportedNot supported
Merging declarationsCan merge declarations with the same nameCannot merge declarations
Implementing by classesPossible (with optional methods)Not possible
AugmentationPossible (through declaration merging)Not possible
Function signaturesCan define function signaturesCan define function types
Best Used forDefining object shapes and contractsDefining complex and utility types
Convention and UsageCommonly used for objects and classesCommonly used for unions and intersections

61. How does the override keyword work in TypeScript?

The override keyword in TypeScript is used to explicitly indicate that a method in a derived class is intended to override a method with the same signature in the base class. It helps prevent accidental method hiding when the base class method is meant to be overridden.

Here’s an example of using the override keyword:

TypeScript
class Animal {
  makeSound() {
    console.log('Animal makes a sound.');
  }
}

class Dog extends Animal {
  override makeSound() {
    console.log('Dog barks.');
  }
}

const dog = new Dog();
dog.makeSound(); // Output: Dog barks.

In this example, the Dog class extends the Animal class and overrides the makeSound() method. The override keyword ensures that if the method signature in the base class changes, the TypeScript compiler will raise an error in the derived class if the override keyword is not present.

62. What is Mixin Constructor Type?

A Mixin Constructor Type is a type representing a constructor function of a mixin. It allows you to define a type that can be used to create instances of a class that has mixed in multiple behaviors from different mixin classes.

Here’s an example of a Mixin Constructor Type:

TypeScript
class Printable {
  print() {
    console.log('Printing...');
  }
}

class Loggable {
  log() {
    console.log('Logging...');
  }
}

type Constructor<T = {}> = new (...args: any[]) => T;

function Timestamped<TBase extends Constructor>(Base: TBase) {
  return class extends Base {
    timestamp = new Date();

    printTimestamp() {
      console.log(`Timestamp: ${this.timestamp}`);
    }
  };
}

class Document {
  constructor(public content: string) {}
}

const TimestampedDocument = Timestamped(Document);
const doc = new TimestampedDocument('This is a document.');
doc.printTimestamp(); // Output: Timestamp: <current timestamp>

In this example, we have Printable, Loggable, and Timestamped mixins. The Timestamped mixin uses a Mixin Constructor Type (Constructor<TBase>) to specify that it takes a constructor as an argument and returns a new constructor with additional properties and methods.

63. What is a dynamic import expression?

A dynamic import expression is a way to import modules in TypeScript and JavaScript asynchronously. It allows you to load modules on-demand, only when they are needed, which can significantly improve performance for large applications.

Here’s an example of a dynamic import expression:

TypeScript
async function fetchData() {
  const module = await import('./module');
  const data = module.getData();
  console.log(data);
}

fetchData(); // Dynamically imports the 'module' and calls its 'getData' function.

In this example, the import() function is used with the await keyword to dynamically import the module asynchronously. The getData function from the imported module is then called to fetch and process the data.

64. Explain what `never` datatype in TypeScript is?

In TypeScript, the never type represents a value that will never occur. It is used to indicate that a function will not return any value or that a variable cannot have a value.

Here are some examples of never:

  1. Function that never returns:
TypeScript
function throwError(message: string): never {
  throw new Error(message);
}

const result = throwError('An error occurred.'); // Function throws an error and never returns a value.
  1. Function with an infinite loop:
TypeScript
function infiniteLoop(): never {
  while (true) {}
}

const loopResult = infiniteLoop(); // Function runs indefinitely and never returns a value.
  1. Exhaustive switch statement:
TypeScript
function exhaustiveCheck(value: string): never {
  throw new Error(`Unexpected value: ${value}`);
}

function processValue(value: 'A' | 'B' | 'C') {
  switch (value) {
    case 'A':
      // Do something for 'A'
      break;
    case 'B':
      // Do something for 'B'
      break;
    case 'C':
      // Do something for 'C'
      break;
    default:
      exhaustiveCheck(value); // Compiler knows this line will never be reached.
  }
}

In this example, the exhaustiveCheck function is used in the default case of a switch statement to indicate that the default case should never be reached. The TypeScript compiler can infer that if none of the explicit cases match, the exhaustiveCheck function will be called, and therefore the switch statement is exhaustive.

65. Explain what Currying is in TypeScript?

Currying is a functional programming technique where a function that takes multiple arguments is transformed into a series of functions, each taking a single argument. The returned function from each step can be called with the next argument until all the arguments are provided.

Here’s an example of currying in TypeScript:

TypeScript
// Non-curried function
function add(a: number, b: number): number {
  return a + b;
}

console.log(add(2, 3)); // Output: 5

// Curried function
function curriedAdd(a: number): (b: number) => number {
  return function (b: number) {
    return a + b;
  };
}

const add2 = curriedAdd(2);
console.log(add2(3)); // Output: 5

In this example, we have a non-curried function add, which takes two arguments and returns their sum. The curried version, curriedAdd, takes one argument a and returns a function that takes another argument b and returns the sum of a and b.

By calling curriedAdd(2), we get a new function add2, which is pre-configured to add 2 to any number passed as an argument. This allows for more flexible and reusable function composition.

66. Why is the `infer` keyword needed in TypeScript?

The infer keyword in TypeScript is used in conditional types to allow the inference of a type parameter within a generic type declaration. It helps capture the type of an argument passed to a generic function or class and use it to define the return type or

other type relationships.

Here’s an example of using the infer keyword:

TypeScript
type ExtractReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

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

type Result = ExtractReturnType<typeof add>;
// Result is inferred as 'number' because 'add' returns a number.

In this example, we define a ExtractReturnType type, which uses conditional types with infer R to capture the return type of a function T. When ExtractReturnType is applied to the add function, it infers the return type as number because the add function returns a number.

67. What is the difference between the `unknown` and `any` types?

The unknown and any types in TypeScript are both used to represent values of any type, but they have different behaviors and levels of type safety.

  • any: The any type allows values of any type and turns off type checking for that variable or expression. It provides maximum flexibility but sacrifices type safety.
TypeScript
let data: any = 10;
data = 'Hello';
data = true;
  • unknown: The unknown type is similar to any, allowing values of any type. However, it requires explicit type checking or assertion before using the value. It provides better type safety compared to any.
TypeScript
let data: unknown = 10;
if (typeof data === 'number') {
  const result = data + 5;
}

68. How to exclude a property from a type in TypeScript?

In TypeScript, you can exclude a property from a type by using the Omit utility type. The Omit type allows you to create a new type by excluding one or more properties from the original type.

Here’s an example of using Omit:

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

type PersonWithoutAge = Omit<Person, 'age'>;
// Resulting type: { name: string; city: string; }

const person: PersonWithoutAge = {
  name: 'John Doe',
  city: 'New York',
};

In this example, we have an Person interface with properties name, age, and city. We create a new type PersonWithoutAge by using Omit<Person, 'age'>, which excludes the age property from the original Person interface. The resulting type has only name and city properties.

69. Why do we need Index Signature in TypeScript?

In TypeScript, an index signature is used to define the types of properties that are not known at compile-time but can be accessed using a dynamic key, typically a string or a number. It allows you to create objects or maps with dynamic keys and ensure type safety when accessing their values.

Here’s an example of using an index signature:

TypeScript
interface DynamicObject {
  [key: string]: any;
}

const person: DynamicObject = {
  name: 'John Doe',
  age: 30,
};

console.log(person.name); // Output: John Doe
console.log(person.age); // Output: 30

person.city = 'New York';
console.log(person.city); // Output: New York

In this example, the DynamicObject interface has an index signature [key: string]: any, which allows the person object to have properties with any string key and any value type. This allows us to add new properties to the person object dynamically.

70. How to define a TypeScript class that has an index signature?

To define a TypeScript class that has an index signature, you need to include the index signature in the class definition. The index signature allows you to access properties of the class using a dynamic key.

Here’s an example of a class with an index signature:

TypeScript
class DynamicObject {
  [key: string]: any;

  constructor() {
    this['name'] = 'John Doe';
    this['age'] = 30;
  }
}

const person = new DynamicObject();
console.log(person.name); // Output: John Doe
console.log(person.age); // Output: 30

person.city = 'New York';
console.log(person.city); // Output: New York

In this example, the DynamicObject class has an index signature [key: string]: any, which allows the class instances to have properties with any string key and any value type. We create a new person instance of the DynamicObject class and set some properties dynamically using the index signature.

71. What is one thing you would change about TypeScript?

As an AI language model, I don’t have personal opinions or desires, but one common request from the TypeScript community is to improve the handling of circular references in type definitions. Currently, TypeScript struggles to infer certain circular types, and developers often need to use type assertions or explicitly annotate types to resolve circular dependencies.

TypeScript’s team is continually working on improving the language and addressing community feedback. It’s essential to keep track of the language’s development and submit issues or feature requests on the TypeScript GitHub repository to contribute to its improvement.

72. Explain when to use the `declare` keyword in TypeScript.

The declare keyword in TypeScript is used to tell the compiler that a variable, function, class, or module has been defined elsewhere and will be provided at runtime by an external source, such as a JavaScript library or a global variable.

Here’s an example of using the declare keyword:

TypeScript
// Declare a global variable that will be available at runtime
declare var myGlobalVar: string;

// Declare a global function that will be available at runtime
declare function myGlobalFunction(name: string): void;

// Declare a module that will be available at runtime
declare module 'my-module' {
  export function doSomething(): void;
  export const value: number;
}

In this example, we use the declare keyword to inform TypeScript that myGlobalVar, myGlobalFunction, and the my-module module will be available at runtime from external sources. This allows TypeScript to compile without throwing errors for undefined variables or functions.

73. Is it possible to generate TypeScript declaration files from a JavaScript library?

Yes, it is possible to generate TypeScript declaration files (also known as .d.ts files) from a JavaScript library that lacks type information. TypeScript provides a tool called “dts-gen” (TypeScript Declaration File Generator) to automatically generate declaration files for existing JavaScript libraries.

Here’s how to use dts-gen to generate declaration files:

  1. Install dts-gen globally (if not already installed):
TypeScript
npm install -g dts-gen
  1. Navigate to the root directory of your JavaScript library.
  2. Run dts-gen with the following command:
TypeScript
dts-gen -o ./

dts-gen will analyze your JavaScript library and attempt to generate corresponding .d.ts files. Note that the generated declaration files may not be perfect and may require manual adjustments to ensure type safety.

74. What are Ambients in TypeScript and when to use them?

In TypeScript, “Ambients” refer to declarations that describe types and values defined outside of the TypeScript environment. Ambients are typically used to provide type information for external JavaScript libraries or global variables that are not natively written in TypeScript.

Ambient declarations are declared using the declare keyword, and they allow TypeScript to understand the types and APIs of these external entities during development without requiring the actual implementation.

When to use Ambients:

  1. When using external JavaScript libraries without existing TypeScript type definitions.
  2. When working with global variables or functions that are not natively defined in TypeScript.

For example, if you are using the jQuery library, which is written in JavaScript, you can use an ambient declaration to provide TypeScript with the necessary type information:

TypeScript
declare var jQuery: (selector: string) => any;

By declaring the jQuery variable with a function signature, TypeScript can provide type checking and autocompletion for code that uses the jQuery library.

75. Explain the difference between `declare enum` and `declare const enum` in TypeScript.

In TypeScript, both declare enum and declare const enum are used to declare enums that exist at runtime but do not have a specific implementation in TypeScript. However, there are some key differences between the two:

  • declare enum: When you use declare enum, the enum values will be available at runtime. Additionally, the generated JavaScript output will include the enum object with property assignments for each enum member.
TypeScript
declare enum Direction {
  Up,
  Down,
  Left,
  Right,
}

const direction: Direction = Direction.Up;
  • declare const enum: On the other hand, when you use declare const enum, the enum values will be inlined directly at compile-time. There will be no JavaScript object generated for the enum, and the enum members’ values will be replaced by their numeric literals in the compiled JavaScript code.
TypeScript
declare const enum Direction {
  Up,
  Down,
  Left,
  Right,
}

const direction: Direction = Direction.Up;

In the second example, the declare const enum will not generate a JavaScript object for Direction and will directly replace Direction.Up with 0 in the compiled output.

76. How can the `never` datatype be useful?

The never datatype in TypeScript is useful in several scenarios:

  1. Function that never returns:
TypeScript
function throwError(message: string): never {
  throw new Error(message);
}

const result = throwError('An error occurred.'); // Function throws an error and never returns a value.
  1. Function with an infinite loop:
TypeScript
function infiniteLoop(): never {
  while (true) {}
}

const loopResult = infiniteLoop(); // Function runs indefinitely and never returns a value.
  1. Exhaustive switch statement:
TypeScript
function exhaustiveCheck(value: string): never {
  throw new Error(`Unexpected value: ${value}`);
}

function processValue(value: 'A' | 'B' | 'C') {
  switch (value) {
    case 'A':
      // Do something for 'A'
      break;
    case 'B':
      // Do something for 'B'
      break;
    case 'C':
      // Do something for 'C'
      break;
    default:
      exhaustiveCheck(value); // Compiler knows this line will never be reached.
  }
}

In this example, the exhaustiveCheck function is used in the default case of a switch statement to indicate that the default case should never be reached. The TypeScript compiler can infer that if none of the explicit cases match, the exhaustiveCheck function will be called, and therefore the switch statement is exhaustive.

77. What is the need for the `–incremental` flag in TypeScript? give code examples

The --incremental flag in TypeScript is used to enable incremental compilation. Incremental compilation is a feature that caches information about the project’s structure between compilations, making subsequent builds faster by only recompiling the changed parts of the project.

When you use the --incremental flag, TypeScript stores information about the project in the node_modules/.cache directory by default.

Here’s how to use the --incremental flag:

TypeScript
tsc --incremental

When the --incremental flag is used, TypeScript will generate and update the necessary files in the cache directory to speed up subsequent compilations.

78. What are the differences between the `private` keyword and private fields in TypeScript?

In TypeScript, the private keyword and private fields are both used to encapsulate data and restrict access to certain class members, but they have some differences:

  • private keyword: When you declare a class member as private, it is only accessible within the class where it is defined. It cannot be accessed or modified from outside the class, including its subclasses.
TypeScript
class MyClass {
  private privateVar: number = 10;

  private privateMethod() {
    console.log('This is a private method.');
  }

  public accessPrivate() {
    console.log(this.privateVar); // Allowed within the class
    this.privateMethod(); // Allowed within the class
  }
}

const instance = new MyClass();
console.log(instance.privateVar); // Error: Property 'privateVar' is private and only accessible within class 'MyClass'.
instance.privateMethod(); // Error: Property 'privateMethod' is private and only accessible within class 'MyClass'.
instance.accessPrivate(); // Output: 10, This is a

 private method.
  • Private Fields: Private fields are a feature introduced in TypeScript 3.8 that allows you to define private class members using the # symbol. Private fields provide true encapsulation, and unlike private members, they are not accessible even from within the class.
TypeScript
class MyClass {
  #privateVar: number = 10;

  #privateMethod() {
    console.log('This is a private method.');
  }

  public accessPrivate() {
    console.log(this.#privateVar); // Allowed within the class
    this.#privateMethod(); // Allowed within the class
  }
}

const instance = new MyClass();
console.log(instance.#privateVar); // Error: Private identifiers are not accessible outside class 'MyClass'.
instance.#privateMethod(); // Error: Private identifiers are not accessible outside class 'MyClass'.
instance.accessPrivate(); // Output: 10, This is a private method.

The key difference between private and private fields is that private fields provide stronger encapsulation, and their names are truly private and cannot be accessed even from within the class, whereas private members are still accessible within the class.

79. What is the benefit of import assertions features in TypeScript?

The import assertions feature in TypeScript allows you to explicitly specify the shape or type of the imported module or value. It provides additional type safety and enables better tooling support for imported values.

There are two types of import assertions in TypeScript:

  1. Asserting the existence of a property:
TypeScript
import { someValue } from './module' assert { someValue: number };

Here, we are asserting that the imported someValue from the module has the type number.

  1. Asserting the type of the entire module:
TypeScript
import * as myModule from './module' assert { type: 'json' };

In this case, we are asserting that the entire myModule imported from the module is a JSON-like object.

The benefits of import assertions are:

  • Improved type safety: By explicitly specifying the types or shape of imported values, TypeScript can provide more accurate type checking and prevent potential bugs due to incorrect usage.
  • Better tooling support: Import assertions provide valuable information to IDEs and code editors, enabling enhanced autocompletion, type inference, and documentation for imported modules.

80. How to make a union type from type alias or interface properties in TypeScript?

To create a union type from type alias or interface properties in TypeScript, you can use the keyof operator in conjunction with the typeof operator.

Here’s an example:

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

type PersonProperties = keyof Person;
// Resulting type: 'name' | 'age' | 'city'

function printProperty(property: PersonProperties, person: Person) {
  console.log(person[property]);
}

const person: Person = {
  name: 'John Doe',
  age: 30,
  city: 'New York',
};

printProperty('name', person); // Output: John Doe
printProperty('age', person); // Output: 30
printProperty('city', person); // Output: New York

In this example, we define an Person interface with properties name, age, and city. We then create a type alias PersonProperties using keyof Person, which results in a union type of all property names of the Person interface.

Advanced Questions

81. What does the tsconfig option lib do?

The tsconfig.json file is used to configure the TypeScript compiler. The lib option allows you to specify the set of JavaScript libraries that are available for your TypeScript code to use. By default, TypeScript includes a standard set of libraries based on the ECMAScript version you are targeting, but you can explicitly include or exclude certain libraries using the lib option.

For example, if you want to target ES6 and use the Promise object, you can include the "es6" library in your tsconfig.json like this:

TypeScript
{
  "compilerOptions": {
    "target": "es6",
    "lib": ["es6"]
  }
}

83. Are strongly-typed functions as parameters possible in TypeScript?

Yes, TypeScript allows you to define strongly-typed functions as parameters. You can use the arrow function syntax to define the function type.

Example:

TypeScript
// Define a function type that takes two numbers and returns a number
type MathOperation = (a: number, b: number) => number;

// Function that uses the MathOperation type as a parameter
function performOperation(operation: MathOperation, num1: number, num2: number): number {
  return operation(num1, num2);
}

// Example usage:
const add: MathOperation = (a, b) => a + b;
const subtract: MathOperation = (a, b) => a - b;

const result1 = performOperation(add, 5, 3); // Result: 8
const result2 = performOperation(subtract, 10, 4); // Result: 6

86. How to make an array with a specific length or array elements in TypeScript?

To create an array with a specific length or pre-populate it with specific elements, you can use the Array constructor and fill method in TypeScript.

Example 1: Creating an array with a specific length

TypeScript
const length = 5;
const arrWithLength = new Array(length); // This creates an array of length 5, but the elements are all undefined
console.log(arrWithLength); // Output: [ undefined, undefined, undefined, undefined, undefined ]

Example 2: Creating an array with pre-populated elements

TypeScript
const arrWithElements = new Array(3).fill(0); // This creates an array of length 3 with all elements initialized to 0
console.log(arrWithElements); // Output: [ 0, 0, 0 ]

87. Is there a way to check for both null and undefined in TypeScript?

Yes, there are multiple ways to check for both null and undefined in TypeScript.

  1. Using a union type with null and undefined:
TypeScript
function checkValue(value: string | null | undefined) {
  if (value === null || value === undefined) {
    console.log("Value is null or undefined");
  } else {
    console.log("Value is:", value);
  }
}

checkValue("Hello"); // Output: Value is: Hello
checkValue(null);    // Output: Value is null or undefined
checkValue(undefined); // Output: Value is null or undefined
  1. Using the == null comparison:
TypeScript
function checkValue(value: string | null | undefined) {
  if (value == null) {
    console.log("Value is null or undefined");
  } else {
    console.log("Value is:", value);
  }
}

Both approaches will correctly handle values that are either null or undefined.

89. How would you overload a class constructor in TypeScript?

In TypeScript, you can use constructor overloads to define multiple constructor signatures for a class. This allows you to provide different ways to create instances of the class with varying parameters.

Example:

TypeScript
class Person {
  name: string;
  age: number;

  constructor(name: string, age: number);
  constructor(name: string) {
    this.name = name;
    this.age = 0;
  }

  // Additional constructor overload
  constructor(name: string, age?: number) {
    this.name = name;
    this.age = age || 0;
  }
}

// Creating instances using different constructor overloads
const person1 = new Person("Alice");
console.log(person1); // Output: Person { name: 'Alice', age: 0 }

const person2 = new Person("Bob", 30);
console.log(person2); // Output: Person { name: 'Bob', age: 30 }

In this example, we have defined two constructor overloads for the Person class—one that takes only the name as a parameter and initializes the age to 0, and the other that takes both name and age as parameters.

90. What does the const assertion mean in TypeScript?

The const assertion is a feature in TypeScript that allows you to indicate to the compiler that the variable should have a literal type. When a variable is declared with a const assertion, TypeScript treats it as a constant with a specific, unchangeable value.

Example:

TypeScript
let myNumber = 42; // TypeScript infers myNumber as a number type
let myConstNumber = 42 as const; // TypeScript infers myConstNumber as a literal type 42

// Trying to modify the const assertion will result in an error
// myConstNumber = 43; // Error: Cannot assign to '42' because it is a constant.

In the example above, myConstNumber is inferred as a literal type 42, which means it can only have the value 42, and any attempt to change its value will result in a compilation error.

91. In a?.b.c, if a.b is null, then a.b.c will evaluate to undefined, right?

Yes, that’s correct. In TypeScript (and JavaScript), if you use the optional chaining operator (?.), the expression will short-circuit and evaluate to undefined if any intermediate property is null or undefined.

Example:

TypeScript
const obj = {
  a: null // Set 'a' to null
};

// Without optional chaining:
console.log(obj.a.b.c); // Error: Cannot read property 'b' of null

// With optional chaining:
console.log(obj.a?.b.c); // Output: undefined

In the first case without optional chaining, attempting to access obj.a.b.c results in an error since obj.a is null. However, when using optional chaining obj.a?.b.c, the expression short-circuits at obj.a and evaluates to undefined.

92. What is the difference between “interface” and “type” in TypeScript?

InterfaceType
Primarily used for defining object shapes and contracts.Used for creating aliases for complex types or unions.
Can be extended by other interfaces.Cannot be extended or combined with other types.
Can be implemented by classes to provide a contract.Cannot be implemented by classes.
Allows declaration merging, where multiple interfacesDoes not support declaration merging.
with the same name can be merged into a single interface.
Has better support for declaration merging andMore suitable for defining complex types and unions.
augmentation when working with libraries.

93. How is TypeScript different from ES6?

TypeScript is a superset of ECMAScript 6 (ES6) and includes all the features of ES6 while adding static typing and other advanced features on top. Here are the main differences:

  1. Static Typing: TypeScript introduces static typing, enabling developers to specify the data types of variables, function parameters, and return values. This helps catch type-related errors during development.
  2. Optional Typing: TypeScript allows developers to mark variables or function parameters as optional by using ? after the variable name. This is not present in ES6.
  3. Interfaces and Type Annotations: TypeScript provides interfaces and type annotations to define complex types and enforce specific structures on objects. This is not available in ES6.
  4. Type Inference: TypeScript’s compiler infers types whenever possible, reducing the need for explicit type annotations while maintaining strong typing.
  5. Access Modifiers: TypeScript supports access modifiers like public, private, and protected, providing more control over class members’ visibility.
  6. Strict Null Checks: TypeScript has strict null checks that help prevent null and undefined errors during runtime.
  7. Enums: TypeScript has enums, which allow developers to define a set of named constants, making code more expressive.
  8. Decorators: TypeScript introduces decorators, which are used to modify class declarations or properties at design time. This is useful in various scenarios, including building frameworks or code transformation.

94. What are the different ways to define a variable in TypeScript?

In TypeScript, you can define variables using different keywords:

  1. Using let: Used to declare variables that can be reassigned.
TypeScript
let message: string = "Hello, TypeScript!";
  1. Using const: Used to declare constants with a fixed value.
TypeScript
const PI: number = 3.14;
  1. Using var: The older way to declare variables. Generally avoided in favor of let and const.
TypeScript
var count: number = 10;

95. What are the different data types available in TypeScript?

In TypeScript, there are several built-in data types:

  1. Boolean: Represents true/false values.
TypeScript
let isDone: boolean = false;
  1. Number: Represents numeric values (both integers and floating-point numbers).
TypeScript
let age: number = 30;
let price: number = 19.99;
  1. String: Represents textual data.
TypeScript
let name: string = "John";
  1. Array: Represents a collection of elements of the same type.
TypeScript
let numbers: number[] = [1, 2, 3, 4];
let fruits: Array<string> = ["apple", "banana", "orange"];
  1. Tuple: Represents an array with fixed-length and mixed data types.
TypeScript
let person: [string, number] = ["John", 30];
  1. Enum: Represents a set of named constants.
TypeScript
enum Color {
  Red,
  Green,
  Blue,
}

let backgroundColor: Color = Color.Red;
  1. Any: Represents a dynamic or unknown type, allowing variables to hold values of any type.
TypeScript
let dynamicValue: any = "Hello";
dynamicValue = 42; // Valid
  1. Void: Represents the absence of any type, often used for functions that return nothing.
TypeScript
function logMessage(message: string): void {
  console.log(message);
}
  1. Null and Undefined: Represents null and undefined values, respectively.
TypeScript
let someValue: null = null;
let otherValue: undefined = undefined;

96. What is a class in TypeScript? How do you define it?

In TypeScript, a class is a blueprint for creating objects with a specific structure and behavior. It encapsulates data (properties) and operations (methods) related to the objects. Here’s how you define a class:

TypeScript
class Person {
  // Properties
  name: string;
  age: number;

  // Constructor - used to initialize the class properties
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  // Method
  sayHello(): void {
    console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`);
  }
}

// Creating an instance of the class
const person1 = new Person("John", 30);
person1.sayHello(); // Output: Hello, my name is John and I'm 30 years old.

In the example above, we define a Person class with properties name and age, a constructor to initialize these properties, and a sayHello method to log a greeting.

97. What is a namespace in TypeScript? How can it be used?

A namespace in TypeScript is a way to organize code by encapsulating variables, functions, classes, and interfaces under a unique identifier. It helps avoid naming collisions in large applications. Namespaces are also known as internal modules.

Example:

TypeScript
namespace MathUtils {
  export function add(a: number, b: number): number {
    return a + b;
  }

  export function subtract(a: number, b: number): number {
    return a - b;
  }
}

const sum = MathUtils.add(5, 3); // Using the function from the namespace
console.log(sum); // Output: 8

In this example, we define a namespace MathUtils and export two functions, add and subtract. We can access these functions using the MathUtils namespace.

98. What are decorators in TypeScript?

Decorators are a TypeScript feature used to modify the behavior of classes, methods, or properties at design time. They are denoted by the @ symbol and can be used to add metadata, apply mixins, or transform class properties or methods.

Example:

TypeScript
// Decorator function
function uppercase(target: any, propertyKey: string) {
  let value = target[propertyKey];

  // Redefine the property to return the value in uppercase
  Object.defineProperty(target, propertyKey, {


 get: function () {
      return value.toUpperCase();
    },
    set: function (newValue: string) {
      value = newValue.toUpperCase();
    },
    enumerable: true,
    configurable: true,
  });
}

class Person {
  @uppercase
  name: string;

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

const person = new Person("John");
console.log(person.name); // Output: JOHN

person.name = "Alice";
console.log(person.name); // Output: ALICE

In this example, we define a decorator uppercase, which converts the assigned value to uppercase for the name property of the Person class.

99. How can you create and use a module in TypeScript?

In TypeScript, you can create a module by using the export keyword to expose functionalities, variables, classes, or interfaces from a file. Then, you can use the import keyword to import and use the exported items in another file.

Example:

utils.ts (module file):

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

export const PI = 3.14;

main.ts (file using the module):

TypeScript
import { add, PI } from './utils';

const result = add(5, 3);
console.log(result); // Output: 8

console.log(PI); // Output: 3.14

In this example, we create a module in utils.ts by exporting the add function and the PI constant. Then, we import these items into main.ts using the import statement.

100. What is the use of the “any” data type in TypeScript?

The any data type in TypeScript is used to represent a value of any type. When a variable is declared as any, TypeScript disables type checking for that variable, allowing it to hold values of any type without raising type-related errors.

Example:

TypeScript
let dynamicValue: any;

dynamicValue = "Hello"; // Valid, can assign a string
dynamicValue = 42; // Valid, can assign a number
dynamicValue = true; // Valid, can assign a boolean

As you can see in the example, dynamicValue can hold values of any type without any type checking. While using any provides flexibility, it sacrifices type safety, which is one of TypeScript’s primary benefits.

101. What is a tuple in TypeScript?

In TypeScript, a tuple is a fixed-size array that allows you to specify the type of each element at specific positions. Tuples are useful when you want to represent a collection of values with a precise order and type.

Example:

TypeScript
// Declare a tuple type
type Point = [number, number];

// Create a tuple variable
let coordinate: Point = [10, 20];

// Access elements of the tuple
let x = coordinate[0]; // 10
let y = coordinate[1]; // 20

In this example, we define a Point tuple type that represents a two-dimensional coordinate. We then create a variable coordinate of type Point and access its elements using array-like indexing.

102. How can you implement inheritance in TypeScript?

In TypeScript, you can implement inheritance using the extends keyword to create a subclass (derived class) that inherits from a base class (superclass).

Example:

TypeScript
class Animal {
  name: string;

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

  makeSound(): void {
    console.log("Animal makes a sound.");
  }
}

class Dog extends Animal {
  breed: string;

  constructor(name: string, breed: string) {
    super(name);
    this.breed = breed;
  }

  makeSound(): void {
    console.log("Dog barks.");
  }

  displayInfo(): void {
    console.log(`Name: ${this.name}, Breed: ${this.breed}`);
  }
}

const dog1 = new Dog("Buddy", "Labrador");
dog1.displayInfo(); // Output: Name: Buddy, Breed: Labrador
dog1.makeSound();   // Output: Dog barks.

In this example, Animal is the base class, and Dog is the subclass that extends Animal. The Dog class inherits the name property and makeSound() method from the Animal class. It also overrides the makeSound() method and adds its own breed property and displayInfo() method.

103. What are generics in TypeScript and how do you use them?

Generics in TypeScript allow you to create reusable components that can work with different types while preserving type safety. They enable you to define types or functions that can operate on a range of types, making code more flexible and maintainable.

Example:

TypeScript
// Generic function to return the first element of an array
function getFirstElement<T>(arr: T[]): T | undefined {
  return arr[0];
}

const numbers = [1, 2, 3];
const firstNumber = getFirstElement(numbers); // 'firstNumber' is inferred as 'number'

const names = ["Alice", "Bob", "Charlie"];
const firstName = getFirstElement(names); // 'firstName' is inferred as 'string'

In this example, the getFirstElement function is a generic function that takes an array of type T and returns the first element of the array. The type of T is inferred based on the type of the input array.

105. How can you handle errors in TypeScript?

In TypeScript, you can handle errors using try-catch blocks to catch and handle exceptions that might occur during the execution of your code.

Example:

TypeScript
function divide(a: number, b: number): number {
  if (b === 0) {
    throw new Error("Cannot divide by zero.");
  }

  return a / b;
}

try {
  const result = divide(10, 0);
  console.log("Result:", result);
} catch (error) {
  console.error("Error occurred:", error.message);
}

In this example, the divide function attempts to divide two numbers but throws an Error if the second number is 0. The try block calls the divide function, and if an error occurs, the catch block catches the error and logs the error message.

106. What is the use of the “never” data type in TypeScript?

The never data type in TypeScript represents values that never occur. It is typically used for functions that never return or throw an error, as well as for exhaustiveness checking in discriminated unions.

Example:

TypeScript
// Function that never returns
function throwError(message: string): never {
  throw new Error(message);
}

// Function that never ends (infinite loop)
function infiniteLoop(): never {
  while (true) {
    console.log("This loop never ends.");
  }
}

// Function with exhaustiveness checking
type Shape = "circle" | "square" | "triangle";

function getArea(shape: Shape): number

 {
  switch (shape) {
    case "circle":
      return Math.PI;
    case "square":
      return 4;
    // 'triangle' is not covered, so this function returns 'never' here
    default:
      throw new Error("Unknown shape.");
  }
}

In this example, the throwError function throws an error and never returns. The infiniteLoop function runs indefinitely, and thus, it never returns either. The getArea function has exhaustiveness checking, and since all possible cases are covered except for “triangle,” it returns never when the shape parameter is “triangle.”

107. How can you implement an interface in TypeScript?

In TypeScript, you can implement an interface by using the implements keyword. When a class implements an interface, it must provide definitions for all the properties and methods declared in that interface.

Example:

TypeScript
interface Animal {
  name: string;
  makeSound(): void;
}

class Dog implements Animal {
  name: string;
  breed: string;

  constructor(name: string, breed: string) {
    this.name = name;
    this.breed = breed;
  }

  makeSound(): void {
    console.log("Dog barks.");
  }
}

const dog = new Dog("Buddy", "Labrador");
dog.makeSound(); // Output: Dog barks.

In this example, the Animal interface defines a contract with a name property and a makeSound() method. The Dog class implements this interface, providing the required definitions for the name property and the makeSound() method.

108. What is the difference between “let” and “var” in TypeScript?

In TypeScript, both let and var are used to declare variables, but they have some differences in scope and hoisting behavior.

  1. Scope:
  • Variables declared with let have block-level scope. They are limited to the block or statement in which they are defined.
TypeScript
function demoLet() {
  if (true) {
    let x = 10; // x is scoped to this block
  }
  // console.log(x); // Error: Cannot find name 'x'
}
  • Variables declared with var have function-level scope. They are accessible throughout the entire function, regardless of where they are declared.
TypeScript
function demoVar() {
  if (true) {
    var y = 20; // y is scoped to the function
  }
  console.log(y); // Output: 20
}
  1. Hoisting:
  • Variables declared with let are not hoisted. They do not get moved to the top of their scope during compilation, so trying to access them before their declaration will result in a ReferenceError.
TypeScript
function demoLetHoisting() {
  console.log(z); // Error: Cannot access 'z' before initialization
  let z = 30;
}
  • Variables declared with var are hoisted, which means they are moved to the top of their scope during compilation. However, their value is undefined until they are assigned a value.
TypeScript
function demoVarHoisting() {
  console.log(w); // Output: undefined
  var w = 40;
  console.log(w); // Output: 40
}

109. How can you define an array in TypeScript?

To define an array in TypeScript, you can use the [] syntax or the Array<ElementType> syntax.

Example:

TypeScript
// Using '[]' syntax
let numbers: number[] = [1, 2, 3, 4, 5];

// Using 'Array<ElementType>' syntax
let fruits: Array<string> = ["apple", "banana", "orange"];

Both syntaxes are interchangeable and create an array of a specific type. In the example, numbers is an array of numbers, and fruits is an array of strings.

110. What is a union type and how can it be used in TypeScript?

A union type in TypeScript allows a variable or parameter to hold values of multiple types. It is denoted by the | symbol.

Example:

TypeScript
// Union type
let age: number | string;

age = 30;     // Valid, 'age' can hold a number
age = "thirty"; // Valid, 'age' can hold a string

// Function with union type parameter
function displayResult(result: number | string): void {
  console.log("Result:", result);
}

displayResult(42);       // Output: Result: 42
displayResult("success"); // Output: Result: success

In the example, the variable age is declared with a union type number | string, which means it can hold either a number or a string value. The displayResult function accepts a parameter with a union type, allowing it to take either a number or a string as an argument.

111. What are the different types of access modifiers in TypeScript?

In TypeScript, there are three types of access modifiers:

  1. public: Members with this modifier are accessible from anywhere, both inside and outside the class.
TypeScript
class Person {
  public name: string;

  constructor(name: string) {
    this.name = name;
  }
}
  1. private: Members with this modifier are only accessible within the class that defines them.
TypeScript
class Person {
  private age: number;

  constructor(age: number) {
    this.age = age;
  }
}
  1. protected: Members with this modifier are accessible within the class and its subclasses but not from outside.
TypeScript
class Animal {
  protected name: string;

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

class Dog extends Animal {
  displayInfo(): void {
    console.log("Name:", this.name); // Valid, 'name' is accessible from the subclass
  }
}

112. What is a lambda or arrow function in TypeScript?

Lambda or arrow functions in TypeScript are concise syntax for defining functions. They are often used for short functions or callbacks. Arrow functions preserve the lexical value of this, making them convenient for using within classes.

Example:

TypeScript
// Regular function
function add(a: number, b: number): number {
  return a + b;
}

// Arrow function
const multiply = (a: number, b: number): number => a * b;

console.log(add(5, 3));      // Output: 8
console.log(multiply(5, 3)); // Output: 15

In this example, add is a regular function, and multiply is an arrow function with a shorter syntax. Both functions take two parameters and return a result.

113. How can you define a function in TypeScript?

You can define a function in TypeScript using the regular function syntax or the arrow function syntax.

Example:

TypeScript
// Regular function
function add(a: number, b: number): number {
  return a + b;
}

// Arrow function
const multiply = (a: number, b: number): number => a * b;

console.log(add(5, 3));      // Output: 8
console.log(multiply(5, 3)); // Output: 15

In this example, add is a regular function defined with the function keyword, and multiply is an arrow function defined using the => syntax.

114. What is the use of the “unknown” data type in TypeScript?

The unknown data type in TypeScript is similar to any, but with stricter type-checking. Variables of type unknown can hold values of any type, but you must perform type checking or type assertions before using them to avoid type-related errors.

Example:

TypeScript
let userInput: unknown = "Hello";

// Type assertion
let messageLength = (userInput as string).length;
console.log(messageLength); // Output: 5

In this example, we declare userInput as type unknown. Before using it as a string and accessing its length property, we use a type assertion (userInput as string) to tell TypeScript that we know the actual type of the value.

115. How can you implement polymorphism in TypeScript?

Polymorphism in TypeScript refers to the ability of a class to take multiple forms. It allows you to create classes with the same method names but different implementations. This can be achieved through inheritance and method overriding.

Example:

TypeScript
class Animal {
  makeSound(): void {
    console.log("Animal makes a sound.");
  }
}

class Dog extends Animal {
  makeSound(): void {
    console.log("Dog barks.");
  }
}

class Cat extends Animal {
  makeSound(): void {
    console.log("Cat meows.");
  }
}

// Polymorphism in action
const dog = new Dog();
const cat = new Cat();

dog.makeSound(); // Output: Dog barks.
cat.makeSound(); // Output: Cat meows.

In this example, the Animal class has a makeSound method. The Dog and Cat classes are subclasses of Animal and override the makeSound method with their own implementations. When we call makeSound on instances of Dog and Cat, polymorphism allows the correct method to be called based on the object’s actual type.

116. What is the difference between “==” and “===” operators in TypeScript?

OperatorDescriptionExampleResult
==Equality operator. Compares values, allowing type coercion.5 == "5"true
===Identity operator. Compares values and types without coercion.5 === "5"false

In the example above, == returns true because the values 5 and "5" are considered equal when using type coercion. On the other hand, === returns false because it performs strict equality checking, considering both the values and their types, which are different in this case.

117. How can you define a constant in TypeScript?

In TypeScript, you can define a constant using the const keyword. Constants are read-only and cannot be reassigned once they are declared.

Example:

TypeScript
const PI = 3.14;
// PI = 3.14159; // Error: Cannot assign to 'PI' because it is a constant.

In this example, we define a constant PI with a value of 3.14. Any attempt to reassign PI will result in a compilation error.

118. What is the use of the “as” keyword in TypeScript?

The as keyword in TypeScript is used for type assertion or type casting. It allows you to explicitly tell the TypeScript compiler the type of a value when the type cannot be inferred automatically.

Example:

TypeScript
let userInput: unknown = "Hello";

// Type assertion
let messageLength = (userInput as string).length;
console.log(messageLength); // Output: 5

In this example, we use as string to assert that the userInput variable is of type string. This allows us to access the length property without causing a type error.

119. How can you implement abstraction in TypeScript?

Abstraction in TypeScript refers to the concept of hiding the internal details of a class from its outside users. It involves creating abstract classes or interfaces with method signatures but no implementation. Subclasses are then required to implement these abstract methods.

Example:

TypeScript
abstract class Shape {
  abstract getArea(): number;
}

class Circle extends Shape {
  radius: number;

  constructor(radius: number) {
    super();
    this.radius = radius;
  }

  getArea(): number {
    return

 Math.PI * this.radius * this.radius;
  }
}

class Square extends Shape {
  side: number;

  constructor(side: number) {
    super();
    this.side = side;
  }

  getArea(): number {
    return this.side * this.side;
  }
}

const circle = new Circle(5);
const square = new Square(4);

console.log(circle.getArea()); // Output: 78.53981633974483
console.log(square.getArea()); // Output: 16

In this example, we define an abstract class Shape with an abstract method getArea(). The Circle and Square classes extend Shape and must provide their own implementations of the getArea() method.

120. What is the difference between “null” and “undefined” in TypeScript?

NullUndefined
Represents an intentional absence of a value.Represents an uninitialized or missing value.
Must be explicitly assigned.Usually automatically assigned when a variable is declared but not initialized.
Can be assigned to a variable of any type (unless strictNullChecks is enabled).Has its own distinct type and cannot be assigned to variables of other types.
Evaluates to false in boolean contexts.Evaluates to false in boolean contexts when using strict equality (==) but not when using loose equality (==).

121. How can you implement encapsulation in TypeScript?

In TypeScript, encapsulation can be achieved by using access modifiers (public, private, and protected) to control the visibility of class members (properties and methods).

  1. Public: Public members are accessible from anywhere, both inside and outside the class.
TypeScript
class Person {
  public name: string;

  constructor(name: string) {
    this.name = name;
  }
}
  1. Private: Private members are only accessible within the class that defines them.
TypeScript
class Person {
  private age: number;

  constructor(age: number) {
    this.age = age;
  }
}
  1. Protected: Protected members are accessible within the class and its subclasses but not from outside.
TypeScript
class Animal {
  protected name: string;

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

class Dog extends Animal {
  displayInfo(): void {
    console.log("Name:", this.name); // Valid, 'name' is accessible from the subclass
  }
}

MCQ Questions

1. What is TypeScript?

a) A superset of JavaScript
b) A programming language developed by Microsoft
c) A scripting language for server-side applications
d) A markup language for web development

Answer: a) A superset of JavaScript

2. TypeScript compiles to which of the following?

a) Machine code
b) JavaScript
c) HTML
d) C#

Answer: b) JavaScript

3. Which of the following is NOT a key feature of TypeScript?

a) Optional static typing
b) Classes and interfaces
c) Garbage collection
d) Type inference

Answer: c) Garbage collection

4. TypeScript supports which of the following programming paradigms?

a) Object-oriented programming
b) Functional programming
c) Imperative programming
d) All of the above

Answer: d) All of the above

5. Which command is used to compile TypeScript code?

a) tsc
b) gcc
c) javac
d) python

Answer: a) tsc

6. TypeScript was developed by:

a) Microsoft
b) Apple
c) Google
d) Mozilla

Answer: a) Microsoft

7. TypeScript supports which ECMAScript version?

a) ES5
b) ES6
c) ES7
d) All of the above

Answer: d) All of the above

8. What is the file extension used for TypeScript files?

a) .js
b) .ts
c) .html
d) .css

Answer: b) .ts

9. Which keyword is used to define a variable with explicit type in TypeScript?

a) var
b) let
c) const
d) type

Answer: d) type

10. Which of the following is NOT a TypeScript primitive type?

a) number
b) boolean
c) string
d) object

Answer: d) object

11. TypeScript supports which of the following access modifiers?

a) public
b) private
c) protected
d) All of the above

Answer: d) All of the above

12. Which of the following is used to define a function with a specific return type in TypeScript?

a) function
b) void
c) return
d) =>

Answer: d) =>

13. Which symbol is used to denote the optional property in TypeScript?

a) ?
b) !
c) *
d) #

Answer: a) ?

14. Which of the following is used to import a module in TypeScript?

a) require
b) import
c) load
d) include

Answer: b) import

15. Which of the following is NOT a valid TypeScript data type?

a) null
b) undefined
c) any
d) void

Answer: a) null

16. TypeScript supports which of the following type annotations for variables?

a) number
b) string
c) boolean
d) All of the above

Answer: d) All of the above

17. What is the output of the following TypeScript code?

TypeScript
let x = 10;
x = "hello";
console.log(x);

a) 10
b) “hello”
c) Error

d) undefined

Answer: c) Error

18. Which of the following is used to define an array type in TypeScript?

a) Array
b) List
c) []
d) {}

Answer: c) []

19. TypeScript supports which of the following type assertions?

a) as syntax
b) angle-bracket syntax
c) both as syntax and angle-bracket syntax
d) none of the above

Answer: c) both as syntax and angle-bracket syntax

20. Which of the following is used to specify multiple possible types for a variable in TypeScript?

a) union type
b) intersection type
c) any type
d) void type

Answer: a) union type

21. What is the purpose of the “readonly” modifier in TypeScript?

a) It makes a property read-only.
b) It prevents the declaration of mutable variables.
c) It enables runtime type checking.
d) It restricts the use of “any” type.

Answer: a) It makes a property read-only.

22. Which of the following is NOT a TypeScript decorator?

a) @Component
b) @Injectable
c) @Route
d) @NgModule

Answer: c) @Route

23. TypeScript is a strongly typed or weakly typed language?

a) Strongly typed
b) Weakly typed

Answer: a) Strongly typed

24. Which of the following is used to define a class in TypeScript?

a) class
b) interface
c) struct
d) module

Answer: a) class

25. What is the output of the following TypeScript code?

TypeScript
console.log(2 + "2");

a) 4
b) “22”
c) 22
d) Error

Answer: b) “22”

26. TypeScript supports which of the following loop constructs?

a) for loop
b) while loop
c) do-while loop
d) All of the above

Answer: d) All of the above

27. What is the purpose of the “this” keyword in TypeScript?

a) It refers to the current class instance.
b) It refers to the global object.
c) It refers to the parent class instance.
d) It refers to the child class instance.

Answer: a) It refers to the current class instance.

28. Which of the following is used to define a type alias in TypeScript?

a) type
b) alias
c) typedef
d) define

Answer: a) type

29. TypeScript supports which of the following module systems?

a) CommonJS
b) AMD
c) ES6 Modules
d) All of the above

Answer: d) All of the above

30. What is the purpose of the “never” type in TypeScript?

a) It represents a value that will never occur.
b) It represents an empty value.
c) It represents an unknown type.
d) It represents a nullable value.

Answer: a) It represents a value that will never occur.

Avatar Of Deepak Vishwakarma
Deepak Vishwakarma

Founder

RELATED Articles

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.