Typescript Interview Questions: A Comprehensive Guide for Freshers and Experienced Candidates

Hi, am Michael Piper, and I will be talking about preparing for your Typescript interview with this extensive list of questions. From primitive types to advanced concepts like conditional types and inheritance, this article covers various topics to help you succeed in your interview.

Tags: Typescript interview questions, Typescript questions for freshers, Typescript questions for experienced, Typescript interview tips, Typescript interview preparation, Typescript interview guide.

TypeScriptInterview Questions for Freshers:

What are the primitive types in TypeScript?
Primitive Types in TypeScript: In TypeScript, there are six primitive types: number, string, boolean, null, undefined, and symbol. These types represent basic values and are not objects themselves.

Example:

let num: number = 10;
let str: string = 'Hello';
let bool: boolean = true;
let n: null = null;
let u: undefined = undefined;
let sym: symbol = Symbol('key');

console.log(typeof num); // output: "number"
console.log(typeof str); // output: "string"
console.log(typeof bool); // output: "boolean"
console.log(typeof n); // output: "object"
console.log(typeof u); // output: "undefined"
console.log(typeof sym); // output: "symbol"

The different keywords to declare variables in TypeScript are:

  1. var: It is the traditional way of declaring variables in JavaScript and TypeScript. It has function-scoping and is accessible throughout the function or file in which it is declared.

  2. let: Introduced in ECMAScript 6, let allows variables to be block-scoped, meaning they are only accessible within the block where they are defined. It provides more control and avoids potential issues with variable hoisting.

  3. const: Also introduced in ECMAScript 6, const is used to declare variables that have a constant value, meaning their value cannot be reassigned. Similar to let, it is block-scoped but has the additional restriction of immutability.

It is recommended to use let and const instead of var in TypeScript, as they provide better scoping and help avoid common pitfalls. Here is an example code snippet demonstrating the use of different keywords to declare variables:

// Using 'var' keyword
var firstName: string = "John";
console.log(firstName); // Output: John

// Using 'let' keyword
let age: number = 25;
console.log(age); // Output: 25

// Using 'const' keyword
const pi: number = 3.14;
console.log(pi); // Output: 3.14

// Trying to reassign a value to 'const' variable (throws an error)
pi = 3.14159; // Error: Cannot assign to 'pi' because it is a constant

// Block-scoped variable using 'let'
if (true) {
    let lastName: string = "Doe";
    console.log(lastName); // Output: Doe
}

console.log(lastName); // Error: Cannot find name 'lastName'

In this example, we declare the variables firstName using var, age using let, and pi using const. The var variable is accessible throughout the function or file, while let and const variables are block-scoped and have different reassignment capabilities (let allows reassignment, while const does not).

  1. Explain how the arrays work in TypeScript.

Arrays in TypeScript: Arrays in TypeScript can be defined with a specific type using the syntax type[] or Array<type>. The type can be any valid TypeScript type, including primitive types, user-defined types, or a combination of types.

Example:

let numbers: number[] = [1, 2, 3, 4, 5];
let names: Array<string> = ['John', 'Jane', 'Doe'];

numbers.push(6);
names.push('Smith');

console.log(numbers); // output: [1, 2, 3, 4, 5, 6]
console.log(names); // output: ["John", "Jane", "Doe", "Smith"]
  1. What is any type, and when to use it?

Any Type in TypeScript: The any type in TypeScript represents a value that can be of any type. It is useful when you don't know or don't want to specify the type of a variable at compile-time. When using any, the TypeScript compiler will not perform type checks on that variable.

Example:

let variable: any = 'Hello';
console.log(variable.length); // no compiler error since "any" can be of any type

variable = 10;
console.log(variable.toFixed(2)); // no compiler error since "any" can be of any type
  1. What is void, and when to use the void type?

Void Type in TypeScript: The void type in TypeScript represents the absence of any type. It is often used as the return type of functions that don't return a value. When a function is of type void, it cannot return a value or have a return statement.

Example:

function logMessage(message: string): void {
  console.log(message);
}

logMessage('Hello, world!'); // output: "Hello, world!"
  1. What is an unknown type, and when to use it in TypeScript?

Unknown Type in TypeScript: The unknown type in TypeScript represents a value that is of an unknown type. It is similar to the any type, but with stricter usage rules. Variables of type unknown cannot be assigned directly to other types without type checking or assertions.

Example:

let value: unknown = 10;

if (typeof value === 'number') {
  let result: number = value * 2;
  console.log(result); // output: 20
}
  1. Provide the syntax of a function with the type annotations.

Provide the syntax of a function with type annotations: The syntax for declaring a function with type annotations in TypeScript is as follows:

function functionName(param1: type1, param2: type2): returnType {
  // function body
}

Here, functionName is the name of the function. param1 and param2 are the parameters of the function with their respective types type1 and type2. returnType is the type of the value that the function returns.

For example, let's declare a simple function called addNumbers which takes two parameters of type number and returns a value of type number:

function addNumbers(num1: number, num2: number): number {
  return num1 + num2;
}

In this example, addNumbers function takes num1 and num2 as input parameters, both of type number, and returns a value of type number.

  1. How to create objects in TypeScript?

In TypeScript, you can create objects using either object literal syntax or constructor functions. Here are examples of both approaches:

Using Object Literal Syntax:

let person = {
  name: 'John',
  age: 25,
  gender: 'male'
};

In this example, we create an object called person using object literal syntax. The object has properties such as name, age, and gender, with respective values.

Using Constructor Functions:

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

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

let person = new Person('John', 25, 'male');

In this example, we create a Person class with properties name, age, and gender. The constructor function is used to initialize the object with values for these properties. We then create an instance of the Person class and assign it to the variable person.

Both approaches allow you to create objects in TypeScript, but using classes and constructors provides the advantage of creating reusable object blueprints and defining their behavior through methods and additional class members.

  1. Explain the difference between null and undefined in TypeScript.

In TypeScript, both null and undefined represent absence of value, but they have slightly different meanings and use cases:

  • null represents the intentional absence of any object value. It means that a variable has been explicitly assigned the value null to indicate that it has no value or is empty.

  • undefined represents the absence of a value in an uninitialized variable or a missing property in an object. It means that a variable has been declared but has not been assigned a value or a property does not exist or has not been assigned a value in an object.

Here's an example that illustrates the difference:

let x: number | null;
console.log(x); // Output: undefined

let y: number | undefined;
console.log(y); // Output: undefined

let person: { name: string, age?: number };
console.log(person.name); // Output: undefined
console.log(person.age); // Output: undefined

In this example, x and y are both declared with union types that include null and undefined. However, since neither variable has been assigned a value, both will have an initial value of undefined.

On the other hand, the person object has a name property that exists but has not been assigned a value, so accessing person.name will also return undefined. Additionally, the person object has an optional age property, which does not exist in this case, so accessing person.age will also return undefined.

In short, null is used to explicitly indicate the absence of value, while undefined is used when a value is expected but has not been assigned or does not exist.

  1. Explain what a union type is in TypeScript.

In TypeScript, a union type is a way to define a variable or parameter that can hold values of multiple types. It allows you to specify that a value could be of one type or another type, providing more flexibility and expressiveness to your code.

To define a union type, you use the | (pipe) operator between the types you want to include. For example:

let myVariable: string | number;

In this example, myVariable can hold a value that is either a string or a number. This means that you can assign a string or a number to myVariable without any errors:

myVariable = "Hello";
console.log(myVariable); // Output: Hello

myVariable = 123;
console.log(myVariable); // Output: 123

Union types are especially useful when working with APIs that return different types of values or when dealing with optional properties that could be of different types. They allow you to handle different scenarios in a type-safe manner without resorting to type assertions or any runtime checks.

For example, consider a function that takes a value of either a string or a number and performs an operation:

function performOperation(value: string | number): void {
  if (typeof value === "string") {
    console.log("You provided a string:", value);
  } else {
    console.log("You provided a number:", value);
  }
}

performOperation("Hello"); // Output: You provided a string: Hello
performOperation(123); // Output: You provided a number: 123

In this case, the function performOperation can accept either a string or a number, and inside the function, you can safely check the type of the value using typeof and perform the appropriate operation based on the type.

By using union types, you can write code that is more flexible, expressive, and type-safe in TypeScript.

  1. What is undefined in TypeScript?

Undefined in TypeScript represents a value that has not been assigned or has been explicitly assigned the value undefined. It usually indicates that a variable or property has been declared but has not yet been given a value. Here is an example code:

let x: number; // declaring a variable x without assigning a value
console.log(x); // Output: undefined

let y: string | undefined = undefined; // explicitly assigning the value undefined
console.log(y); // Output: undefined

In the above code, the variable x is declared but not assigned a value, so its value is undefined. The variable y is explicitly assigned the value undefined.

  1. Explain the purpose of the never type in TypeScript.

The never type in TypeScript represents a type that will never occur. It is used to indicate that a function will not return a value or that a value cannot be reached. This can be useful in scenarios like exhaustive type checking or when throwing an error. Here is an example code:

function throwError(message: string): never {
  throw new Error(message);
}

function infiniteLoop(): never {
  while (true) {
    // logic here
  }
}

// This function has a return type of never because it never completes normally.
// It either throws an error or enters an infinite loop.

In the above code, the throwError function is declared to have a return type of never because it always throws an error. Similarly, the infiniteLoop function has a return type of never because it enters an infinite loop and never completes normally.

  1. Explain how enums work in TypeScript?
    Enums in TypeScript allow developers to define a set of named constants. Enums make it easier to work with a set of related values and provide more descriptive names for those values. Here's how enums work in TypeScript:

To define an enum, you use the enum keyword followed by the name of the enum and the list of values enclosed in curly braces. Each value in the enum has an associated numeric value called the "enumerator". Here's an example:

enum Color {
  Red,
  Green,
  Blue
}

let myColor: Color = Color.Green;
console.log(myColor); // Output: 1

In this example, we defined an enum called Color with three values: Red, Green, and Blue. By default, the first value of an enum has the enumerator of 0, and subsequent values are assigned incremented enumerator values.

To assign a value of the enum to a variable, you use the enum name followed by the value name. In the example above, myColor is assigned the value Color.Green.

Enums can also have string values or assigned specific numeric values. Here's an example:

enum Direction {
  Up = "UP",
  Down = "DOWN",
  Left = "LEFT",
  Right = "RIGHT"
}

let myDirection: Direction = Direction.Right;
console.log(myDirection); // Output: "RIGHT"

In this case, we defined an enum called Direction with string values. The values of the enum are explicitly assigned string values. myDirection is assigned the value Direction.Right, which is "RIGHT".

Enums in TypeScript are useful when you want to define a collection of related values that have a clear and meaningful representation.

I hope this helps! Let me know if you have any more questions.

  1. What is the typeof operator? How is it used in TypeScript?

The typeof operator in TypeScript is used to determine the type of a value or a variable at runtime. It returns a string representing the type of the operand. It is often used in conditional statements or for type checking at runtime. Here is an example code:

let x: number = 10;
let y: string = "Hello";

console.log(typeof x); // Output: "number"
console.log(typeof y); // Output: "string"
console.log(typeof undefined); // Output: "undefined"
console.log(typeof null); // Output: "object"

In the above code, the typeof operator is used to determine the type of the variables x and y. The first console.log statement outputs "number" because the type of x is number. The second console.log statement outputs "string" because the type of y is string. The third and fourth console.log statements show that typeof can also be used to determine the type of undefined and null, respectively.

It is worth noting that the typeof operator returns a string representation of the type and not the actual TypeScript type. For example, typeof null returns "object" instead of null. This behavior is inherited from JavaScript.

  1. What are the rest parameters and arguments in TypeScript?
    Rest parameters in TypeScript allow you to represent an indefinite number of arguments as an array. By prefixing the parameter name with an ellipsis (...), you indicate that the parameter should be treated as an array of values. Here's an example:
function sum(...numbers: number[]): number {
  let result = 0;
  for (let number of numbers) {
    result += number;
  }
  return result;
}

console.log(sum(1, 2, 3)); // Output: 6
console.log(sum(4, 5, 6, 7, 8)); // Output: 30

In this example, the sum function accepts any number of arguments as an array of numbers. The numbers parameter is a rest parameter that allows you to pass in multiple numbers without explicitly specifying their count.

The arguments object, on the other hand, is a built-in object in JavaScript that allows you to access all the arguments passed to a function. However, in TypeScript, you can explicitly specify the types of the arguments, which is not possible with arguments.

  1. What is parameter destructuring?
    Parameter destructuring in TypeScript allows you to extract individual values from an object or array parameter and assign them to named variables. It provides a convenient way to access and use the properties or elements of complex data structures.

Here's an example of parameter destructuring with an object:

function printUserInfo({ name, age }: { name: string, age: number }) {
  console.log(`Name: ${name}, Age: ${age}`);
}

printUserInfo({ name: "Alice", age: 25 }); // Output: "Name: Alice, Age: 25"

In this example, the printUserInfo function expects an object with name and age properties. Instead of accessing the properties using dot notation (user.name and user.age), we use object parameter destructuring to directly extract the properties and assign them to variables.

Parameter destructuring can also be used with arrays in a similar way.

  1. Explain the TypeScript class syntax.
    In TypeScript, the class syntax is used to define blueprints for creating objects. It provides a way to define the properties and methods that objects created from the class will have.

Here's the syntax for defining a class in TypeScript:

class ClassName {
  // Properties
  propertyName: type;

  // Constructor
  constructor(parameters) {
    // Initialize properties

    // Perform initialization tasks
  }

  // Methods
  methodName(parameters): returnType {
    // Perform actions

    // Return value
  }
}

In this syntax:

  • ClassName is the name of the class.

  • propertyName and methodName are the names of the properties and methods, respectively.

  • type is the type of the property or the return type of the method.

  • parameters are the inputs to the constructor or methods.

  • returnType is the type of the value that the method returns (if any).

Here's an example of a class definition:

class Person {
  name: string;
  age: number;

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

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

let person = new Person("Alice", 25);
person.sayHello(); // Output: "Hello, my name is Alice. I am 25 years old."

In this example, the Person class has name and age properties, a constructor to initialize these properties, and a sayHello method to output a greeting.

  1. Explain the arrow function syntax in TypeScript.
    Arrow function syntax in TypeScript provides a concise way to define functions. Unlike traditional function syntax, arrow functions do not have their own this value and they cannot be used as constructors. Here's the syntax for arrow functions:
const functionName = (parameters) => { 
  // Perform actions

  // Return value
};

In this syntax:

  • functionName is the name of the function (can be omitted for anonymous functions).

  • parameters are the inputs to the function.

  • {} enclose the function body, which contains the actions to be performed.

  • The return value is specified after the =>.

Here's an example of an arrow function:

const sum = (a: number, b: number): number => a + b;

console.log(sum(3, 4)); // Output: 7

In this example, sum is an arrow function that takes two numbers as parameters and returns their sum.

  1. Provide the syntax for optional parameters in TypeScript.
    Optional parameters in TypeScript are marked with a ? symbol after their name in the parameter list. This indicates that the parameter can be omitted when calling the function.

Here's the syntax for optional parameters:

function functionName(param1: type, param2?: type) {
  // Perform actions
}

In this syntax, param1 is a required parameter, while param2 is an optional parameter. If param2 is omitted when calling the function, its value will be undefined.

Here's an example:

function greet(name: string, message?: string) {
  if (message) {
    console.log(`Hello, ${name}! ${message}`);
  } else {
    console.log(`Hello, ${name}!`);
  }
}

greet("Alice"); // Output: "Hello, Alice!"
greet("Bob", "How are you?"); // Output: "Hello, Bob! How are you?"

In this example, the greet function has two parameters, name (required) and message (optional). If message is provided, it is included in the output; otherwise, a default message is used.

  1. What is the purpose of the tsconfig.json file?
    The tsconfig.json file is a configuration file used by TypeScript to specify various compiler options and project settings. It allows you to customize how TypeScript code is compiled and how the project is organized.

The tsconfig.json file typically resides in the root of the project directory. It should contain a JSON object with different properties to configure the TypeScript compiler. Here are some common properties:

  • compilerOptions: Specifies compiler options such as target, module system, output directory, strictness, and more.

  • include: Specifies the files or patterns to include in compilation.

  • exclude: Specifies the files or patterns to exclude from compilation.

  • extends: Specifies a base configuration file to inherit compiler options from.

  • files: Specifies an array of files to be included for compilation, ignoring any include or exclude settings.

By using the tsconfig.json file, you can maintain consistent compilation settings across multiple files and easily manage your TypeScript projects.

TypeScriptInterview Questions for Experienced:

  1. Explain the different variants of the for loop in TypeScript:

There are three variants of the for loop in TypeScript: for loop, for...in loop, and for...of loop.

  1. The for loop is the traditional loop that allows you to iterate over a range of values. It consists of an initializer, a condition, and an increment statement.

Syntax:

for (initializer; condition; increment) {
  // code to be executed
}

Example:

for (let i = 0; i < 5; i++) {
  console.log(i);
}
  1. The for...in loop is used to iterate over the properties of an object or the elements of an array. It iterates over the enumerable properties of an object or array indexes.

Syntax:

for (let prop in object) {
  // code to be executed
}

Example:

const person = { name: 'John', age: 25 };

for (let key in person) {
  console.log(key, person[key]);
}
  1. The for...of loop is used to iterate over the values of an iterable object such as an array, string, or collection.

Syntax:

for (let value of iterable) {
  // code to be executed
}

Example:

const arr = [1, 2, 3];

for (let num of arr) {
  console.log(num);
}
  1. Explain the symbol type in TypeScript:

The symbol type in TypeScript represents a unique identifier that can be used as property keys in objects. Symbols are generated using the Symbol() function, which returns a unique value every time it is called.

Example:

const sym = Symbol();
const obj = {
  [sym]: 'value'
};

console.log(obj[sym]); // Output: value

Symbols are primarily used to avoid naming collisions in JavaScript objects. They ensure that properties with the same name but different symbols are treated as separate properties.

  1. Explain how optional chaining works in TypeScript:

Optional chaining is a feature in TypeScript that allows you to access nested properties or methods of an object without needing to explicitly check if each level exists. If any intermediate property or method is undefined or null, the optional chaining operator (?.) short-circuits the expression and returns undefined.

Syntax:

obj?.prop
obj?.method()

Example:

const user = {
  name: 'John',
  address: {
    city: 'New York'
  }
};

console.log(user?.address?.city); // Output: New York

const user2 = {
  name: 'Alex'
};

console.log(user2?.address?.city); // Output: undefined

Optional chaining helps to reduce verbose null and undefined checks, making code more concise and readable.

  1. Provide the TypeScript syntax to create function overloads:

Function overloads in TypeScript allow you to define multiple function signatures for a single function name. This enables the TypeScript compiler to perform better type checking when calling the function with different arguments.

Syntax:

function functionName(parameter1: type1): returnType;
function functionName(parameter1: type1, parameter2: type2): returnType;
// ...additional overloads

Example:

function greet(name: string): void;
function greet(age: number, name: string): void;

function greet(arg1: number | string, arg2?: string): void {
  if (typeof arg1 === 'number') {
    console.log(`Hello ${arg2}, you are ${arg1} years old.`);
  } else {
    console.log(`Hello ${arg1}!`);
  }
}

greet('John'); // Output: Hello John!
greet(25, 'Jane'); // Output: Hello Jane, you are 25 years old.
  1. What is meant by type inference?

Type inference is a feature in TypeScript that allows the TypeScript compiler to automatically determine the type of a variable based on its initial value or context. When a variable is declared and initialized in the same statement, the compiler infers its type from the value being assigned.

Example:

let name = 'John'; // Type inference: string
let age = 25; // Type inference: number
let isActive = true; // Type inference: boolean

Type inference provides static typing benefits without the need to explicitly specify types for every variable, making the code more concise and easier to read.

  1. What is meant by contextual typing?

Contextual typing is a feature in TypeScript that infers the type of an expression based on the context in which it appears. It allows the compiler to assign a more specific type to a variable or expression based on how it is being used.

Example:

const sum = (a: number, b: number) => a + b; // Contextual typing infers the return type as number

const result = sum(2, 3); // The result variable is inferred as number

Contextual typing is useful when the type of an expression can be determined from its surrounding context, eliminating the need for explicit type annotations and reducing code redundancy.

  1. What is the purpose of noImplicitAny?

The noImplicitAny compiler option in TypeScript is used to enforce explicit type annotations for variables and function return types when TypeScript cannot infer a more specific type.

When noImplicitAny is enabled (by setting it to true in the tsconfig.json file), TypeScript generates an error if a variable has an implicit any type. It helps in finding and eliminating potential bugs by ensuring type safety when the developer forgets to provide an explicit type.

Example:

{
  "compilerOptions": {
    "noImplicitAny": true
  }
}
  1. What is an interface?

An interface in TypeScript is a construct that defines the structure of an object, including the names and types of its properties and methods. It acts as a contract, specifying what properties and methods an object must have to be considered compatible with that interface.

Interfaces provide a way to define reusable types, promote code reusability, and facilitate type checking and documentation.

Example:

interface Person {
  name: string;
  age: number;

  sayHello(): void;
}

class Employee implements Person {
  name: string;
  age: number;

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

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

const emp = new Employee('John', 25);
emp.sayHello(); // Output: Hello, my name is John and I'm 25 years old.
  1. Explain the various ways to control member visibility in TypeScript:

In TypeScript, member visibility can be controlled using access modifiers. TypeScript provides three access modifiers: public, private, and protected.

  1. public: The public access modifier allows access to the member from any location.

    • Public members are accessible from outside the class, through class instances.
  2. private: The private access modifier restricts access to the member within the containing class.

    • Private members are not accessible from outside the class, including class instances.
  3. protected: The protected access modifier allows access to the member within the containing class and its subclasses.

    • Protected members are accessible within the class and its derived classes.

Example:

class MyClass {
  public publicProp: string;
  private privateProp: string;
  protected protectedProp: string;

  constructor(publicProp: string, privateProp: string, protectedProp: string) {
    this.publicProp = publicProp;
    this.privateProp = privateProp;
    this.protectedProp = protectedProp;
  }

  public publicMethod(): void {
    console.log('This is a public method.');
  }

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

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

const instance = new MyClass('public', 'private', 'protected');
console.log(instance.publicProp); // Output: public
instance.publicMethod(); // Output: This is a public method.
  1. Does TypeScript support static classes? If not, why?

No, TypeScript does not have built-in support for static classes. In TypeScript, the static keyword is used to define static members (properties and methods) within a class, but it does not provide the ability to define a class as static.

A static class, as supported by some other languages like C#, is a class that cannot be instantiated and can only have static members. TypeScript does not have a direct equivalent of this concept.

However, you can achieve similar behavior by defining a class with all static members, effectively creating a namespace-like structure. This allows you to organize related functions or constants without the need for instantiation.

Example:

class MathUtils {
  static PI = 3.14159;

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

  private constructor() {
    // private constructor to prevent instantiation
  }
}

console.log(MathUtils.PI); // Output: 3.14159
console.log(MathUtils.add(2, 3)); // Output: 5
  1. What are abstract classes? When should you use one?

An abstract class in TypeScript serves as a blueprint for other classes. It cannot be directly instantiated, but other classes can inherit from it. Abstract classes can define abstract methods, which must be implemented by any derived class.

Abstract classes are useful when we want to define a common structure or behavior that should be shared among multiple classes. They provide a way to enforce consistent implementation across derived classes while allowing customization of specific details.

Example:

abstract class Animal {
  abstract makeSound(): void;

  move(): void {
    console.log('Moving...');
  }
}

class Dog extends Animal {
  makeSound(): void {
    console.log('Woof!');
  }
}

class Cat extends Animal {
  makeSound(): void {
    console.log('Meow!');
  }
}

const dog = new Dog();
dog.makeSound(); // Output: Woof!
dog.move(); // Output: Moving...

const cat = new Cat();
cat.makeSound(); // Output: Meow!
cat.move(); // Output: Moving...
  1. What are anonymous functions? Provide their syntax in TypeScript.

An anonymous function in TypeScript is a function without a name. It can be defined inline or assigned to a variable. Anonymous functions are useful when you only need a function temporarily or as a callback.

Syntax:

const functionName = function(parameters) {
  // function body
}

Example:

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

console.log(sum(2, 3)); // Output: 5
  1. What are union types in TypeScript?

Union types in TypeScript allow a variable to hold values of multiple types. A union type is specified using the | operator between the different types.

Example:

let age: number | string;

age = 25;
console.log(age); // Output: 25
age = '25';
console.log(age); // Output: '25'

Union types are useful when a variable can accept different types of values, allowing flexibility in the code and preventing type errors.

  1. What are intersection types?

Intersection types in TypeScript allow you to combine multiple types into a single type. An intersection type is specified using the & operator between the different types.

Example:

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

interface Employee {
  id: string;
  department: string;
}

type EmployeePerson = Person & Employee;

const emp: EmployeePerson = {
  name: 'John',
  age: 25,
  id: '123',
  department: 'IT'
};

Intersection types combine the properties and methods of multiple types into a single type, allowing you to define more specific types based on the combination of existing types.

  1. What are type aliases? How do you create one?

Type aliases in TypeScript allow you to create a new name for an existing type or to define complex types that can be reused. Type aliases are primarily used to improve code readability and maintainability.

Syntax:

type AliasName = ExistingType;

Example:

type ID = number | string;
type Point = { x: number; y: number; };
type Coordinates = [number, number];

Type aliases can also be used to give a descriptive name to complex type definitions or to create short and concise names for long or repetitive types.

  1. Explain the tuple types in TypeScript.

Tuple types in TypeScript allow you to express an array with a fixed number of elements, where each element has a specific type. The order and length of elements in a tuple are fixed, unlike regular arrays.

Syntax:

let tupleName: [type1, type2, ...];

Example:

let person: [string, number] = ['John', 25];
console.log(person[0]); // Output: John
console.log(person[1]); // Output: 25

Tuple types are useful when you want to represent a collection of related values with different types in a fixed order.

  1. Explain how tuple destructuring works in TypeScript.

Tuple destructuring in TypeScript allows you to extract individual elements from a tuple and assign them to separate variables. With tuple destructuring, you can conveniently access and use the values of a tuple without accessing them through indexing.

Syntax:

let [var1, var2, ...] = tupleName;

Example:

let person: [string, number] = ['John', 25];
let [name, age] = person;

console.log(name); // Output: John
console.log(age); // Output: 25

Tuple destructuring is a convenient feature that simplifies working with tuples and improves code readability.

  1. What are type assertions in TypeScript?

Type assertions in TypeScript allow you to explicitly override the inferred type of an expression. It is a way to tell the TypeScript compiler that you know more about the type than it does.

Type assertions come in two forms: "angle-bracket" syntax and "as" syntax.

  1. Angle-bracket syntax:
let variableName = <Type>expression;
  1. "as" syntax:
let variableName = expression as Type;

Example:

let name: any = 'John';
let nameLength = (name as string).length;

console.log(nameLength); // Output: 4

Type assertions should be used with caution, as incorrect type assertions can lead to runtime errors. It is recommended to use type assertions only when there is prior knowledge of the actual type.

  1. How to enforce strict null checks in TypeScript?

Strict null checks in TypeScript help to catch null and undefined errors at compile-time, offering better type safety. When enabled, the compiler generates errors for common JavaScript bugs related to null and undefined values.

To enable strict null checks, set the strictNullChecks compiler option to true in the tsconfig.json file.

Example:

{
  "compilerOptions": {
    "strictNullChecks": true
  }
}

With strict null checks enabled, variables and functions are not allowed to be assigned null or undefined, unless explicitly allowed by specifying the union type with null or undefined:

let name: string;
name = null; // Error: Type 'null' is not assignable to type 'string'

let age: number | null;
age = 25; // Valid
age = null; // Valid
  1. How to make object properties immutable in TypeScript?

In TypeScript, you can make object properties immutable by using the readonly modifier when defining the property. This prevents the property from being modified after it is assigned a value.

Example:

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

const person: Person = { name: 'John', age: 25 };
person.name = 'Jane'; // Error: Cannot assign to 'name' because it is a read-only property

By marking a property as readonly, you ensure that the property's value stays the same once it is assigned, providing immutability.

TypeScriptInterview Questions for Expert:

  1. A type declaration file is a file with the extension ".d.ts" that is used in TypeScript to describe the shape and type of external JavaScript libraries or modules. It provides type information for use in TypeScript code, allowing for better type checking and code completion.

  2. Triple-slash directives are special comments that start with three slashes (///) and are used to provide instructions to the TypeScript compiler. They are typically used to reference external type declaration files or specify module dependencies.

Example:

/// <reference path="./jquery.d.ts" />

import $ from "jquery";

// Now we can use jQuery functions with type checking
$('button').click(function() {
    console.log('Button clicked');
});
  1. The 'in' operator is used in TypeScript to check if a property or index exists in an object or array. It returns true if the property or index exists and false otherwise.

Example:

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

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

if ('name' in person) {
    console.log('Name property exists');
}

if ('gender' in person) {
    console.log('Gender property exists');
} else {
    console.log('Gender property does not exist');
}

Output:

Name property exists
Gender property does not exist
  1. The 'implements' clause in TypeScript is used to implement an interface in a class. It ensures that the class provides implementations for all the methods and properties defined in the interface.

Example:

interface Printable {
    print(): void;
}

class Document implements Printable {
    print() {
        console.log('Printing document');
    }
}
  1. String literal types allow you to specify that a string can only have a specific set of predefined values. This can be useful in scenarios where you want to restrict the possible values of a string.

Example:

type Color = 'red' | 'green' | 'blue';

function printColor(color: Color) {
    console.log(`Color: ${color}`);
}

printColor('red'); // Valid
printColor('yellow'); // Invalid
  1. Template literal types are a feature introduced in TypeScript 4.1 that allow you to create complex types based on template strings. They can be used to create types that depend on the values of other types.

Example:

type EventName<T> = `${string}.${keyof T}`;

interface Events {
    click: MouseEvent;
    keydown: KeyboardEvent;
}

function handleEvent<T, K extends EventName<T>>(eventName: K, callback: (event: T[K]) => void) {
    // Handle event
}

handleEvent('click', (event) => {
    // event is inferred as MouseEvent
});

handleEvent('keydown', (event) => {
    // event is inferred as KeyboardEvent
});
  1. Inheritance in TypeScript allows a class to inherit properties and methods from another class. It enables code reuse and supports the concept of a class hierarchy. The 'extends' keyword is used to indicate that a class inherits from another class.

Example:

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

    makeSound() {
        console.log('Making sound');
    }
}

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

const dog = new Dog('Buddy');
console.log(dog.name); // Buddy
dog.makeSound(); // Woof
  1. Conditional types in TypeScript allow you to create types that depend on the values of other types. They are used to create flexible and reusable types that can vary based on certain conditions.

Example:

type TypeName<T> = T extends string ? "string" : T extends number ? "number" : "other";

let nameType: TypeName<string>;
nameType = "string";

let numberType: TypeName<number>;
numberType = "number";

let booleanType: TypeName<boolean>;
booleanType = "other";
  1. The Function type in TypeScript represents a JavaScript function. It is an extension of the built-in Function type and includes additional type information for the function's parameters and return type.

Example:

type MathFunction = (x: number, y: number) => number;

const add: MathFunction = (x, y) => {
    return x + y;
};

console.log(add(5, 3)); // 8
  1. Some utility types provided by TypeScript include:
  • Partial<T>: Creates a type with all properties of T set as optional.

  • Required<T>: Creates a type with all properties of T set as required.

  • Pick<T, K>: Creates a type by picking certain properties from T specified by the keys in K.

  • Omit<T, K>: Creates a type by omitting certain properties from T specified by the keys in K.

  • Readonly<T>: Creates a type with all properties of T set as read-only.

Example:

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

type PartialPerson = Partial<Person>;

const partialPerson: PartialPerson = {
    name: 'John'
};

type RequiredPerson = Required<Person>;

const requiredPerson: RequiredPerson = {
    name: 'John',
    age: 30,
    address: '123 Main St'
};

type PickPerson = Pick<Person, 'name' | 'age'>;

const pickPerson: PickPerson = {
    name: 'John',
    age: 30
};

type OmitPerson = Omit<Person, 'address'>;

const omitPerson: OmitPerson = {
    name: 'John',
    age: 30
};

type ReadonlyPerson = Readonly<Person>;

const readonlyPerson: ReadonlyPerson = {
    name: 'John',
    age: 30,
    address: '123 Main St'
};

readonlyPerson.name = 'Jane'; // Error: Property 'name' is read-only

Note: The code examples provided above are for illustrative purposes and may require appropriate imports and configurations to work in a complete TypeScript project.

Sources:

Please note that the above questions are just a sample of the types of questions that you may encounter in a TypeScript interview. It is important to research and prepare for a wide range of topics to ensure that you are well-prepared for the interview. Good luck!

Did you find this article valuable?

Support Michael Piper by becoming a sponsor. Any amount is appreciated!