50 TypeScript Interview Questions and Answers (2025)
ID | EN

50 TypeScript Interview Questions and Answers (2025)

Kamis, 16 Jan 2025

Whether you’re preparing for a frontend, backend, or full-stack position, TypeScript knowledge is increasingly essential. This comprehensive guide covers 50 TypeScript interview questions organized by difficulty level.

Basic Questions (1-15)

1. What is TypeScript and why use it?

TypeScript is a statically typed superset of JavaScript that compiles to plain JavaScript. Benefits include:

  • Catch errors at compile time instead of runtime
  • Better IDE support with autocompletion and refactoring
  • Improved code documentation through type annotations
  • Easier maintenance of large codebases

2. What’s the difference between type and interface?

// Interface - extendable, can be merged
interface User {
  name: string;
  age: number;
}

interface User {
  email: string; // Declaration merging
}

// Type - more flexible, can't be merged
type UserType = {
  name: string;
  age: number;
};

type ID = string | number; // Union types only with type

Key differences:

  • Interfaces can be merged (declaration merging)
  • Types can represent unions, primitives, and tuples
  • Use interfaces for objects, types for everything else

3. What are the basic types in TypeScript?

let isDone: boolean = false;
let decimal: number = 6;
let color: string = "blue";
let list: number[] = [1, 2, 3];
let tuple: [string, number] = ["hello", 10];
let notSure: unknown = 4;
let nothing: void = undefined;
let u: undefined = undefined;
let n: null = null;
let neverReturn: never; // Function that never returns

4. What is the any type and when should you avoid it?

any disables type checking for a variable. Avoid it because:

// Bad - loses type safety
let data: any = fetchData();
data.nonExistentMethod(); // No error at compile time!

// Better - use unknown
let data: unknown = fetchData();
if (typeof data === "string") {
  console.log(data.toUpperCase()); // Type narrowed
}

5. What is type inference?

TypeScript automatically infers types when you don’t explicitly annotate:

let x = 3; // inferred as number
let arr = [1, 2, 3]; // inferred as number[]
let obj = { name: "John" }; // inferred as { name: string }

6. What are union and intersection types?

// Union: can be either type
type StringOrNumber = string | number;

// Intersection: must be both types
type Employee = Person & { employeeId: number };

7. What is type assertion?

// Two syntaxes (angle-bracket doesn't work in JSX)
let value: unknown = "hello";
let strLength: number = (value as string).length;
let strLength2: number = (<string>value).length;

8. What are optional properties?

interface Config {
  name: string;
  debug?: boolean; // Optional
}

const config: Config = { name: "app" }; // Valid

9. What is the readonly modifier?

interface Point {
  readonly x: number;
  readonly y: number;
}

const p: Point = { x: 10, y: 20 };
p.x = 5; // Error: Cannot assign to 'x'

10. What are literal types?

type Direction = "north" | "south" | "east" | "west";
type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6;

function move(direction: Direction) {
  // Only accepts exact values
}

11. What is an enum?

enum Status {
  Pending,    // 0
  Approved,   // 1
  Rejected    // 2
}

enum HttpStatus {
  OK = 200,
  NotFound = 404,
  ServerError = 500
}

const status: Status = Status.Approved;

12. What is the difference between null and undefined?

let a: undefined = undefined; // Variable declared but not assigned
let b: null = null;           // Intentional absence of value

// With strictNullChecks enabled:
let name: string = null; // Error!
let name: string | null = null; // OK

13. What is a tuple?

// Fixed-length array with specific types at each position
let tuple: [string, number, boolean] = ["hello", 42, true];

// Named tuples (TypeScript 4.0+)
type Point = [x: number, y: number];

14. What is the never type?

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

// Infinite loop
function infiniteLoop(): never {
  while (true) {}
}

15. How do you define function types?

// Function type expression
type GreetFunction = (name: string) => string;

// Call signature
type GreetCallSignature = {
  (name: string): string;
};

// With optional and default parameters
function greet(name: string, greeting: string = "Hello"): string {
  return `${greeting}, ${name}!`;
}

Intermediate Questions (16-35)

16. What are generics?

function identity<T>(arg: T): T {
  return arg;
}

const num = identity<number>(42);
const str = identity("hello"); // Type inferred

// Generic interface
interface Box<T> {
  value: T;
}

17. What are generic constraints?

interface Lengthwise {
  length: number;
}

function logLength<T extends Lengthwise>(arg: T): T {
  console.log(arg.length);
  return arg;
}

logLength("hello");     // OK
logLength([1, 2, 3]);   // OK
logLength(123);         // Error: number doesn't have length

18. Explain the keyof operator

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

type PersonKeys = keyof Person; // "name" | "age"

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

19. What is the typeof operator in TypeScript?

const user = { name: "John", age: 30 };
type UserType = typeof user; // { name: string; age: number }

function greet() {
  return "Hello";
}
type GreetReturn = ReturnType<typeof greet>; // string

20. What are mapped types?

type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

type Partial<T> = {
  [P in keyof T]?: T[P];
};

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

type ReadonlyUser = Readonly<User>;
type PartialUser = Partial<User>;

21. Explain conditional types

type IsString<T> = T extends string ? true : false;

type A = IsString<string>;  // true
type B = IsString<number>;  // false

// Practical example
type NonNullable<T> = T extends null | undefined ? never : T;

22. What is the infer keyword?

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

type ArrayElementType<T> = T extends (infer E)[] ? E : never;

type Str = ArrayElementType<string[]>; // string

23. What are utility types?

// Partial - all properties optional
type PartialUser = Partial<User>;

// Required - all properties required
type RequiredUser = Required<User>;

// Pick - select specific properties
type NameOnly = Pick<User, "name">;

// Omit - exclude specific properties
type WithoutAge = Omit<User, "age">;

// Record - construct object type
type UserMap = Record<string, User>;

// Exclude - remove types from union
type NotString = Exclude<string | number | boolean, string>;

// Extract - extract types from union
type OnlyString = Extract<string | number | boolean, string>;

24. What is a type guard?

// typeof guard
function printValue(value: string | number) {
  if (typeof value === "string") {
    console.log(value.toUpperCase());
  } else {
    console.log(value.toFixed(2));
  }
}

// Custom type guard
function isUser(obj: any): obj is User {
  return obj && typeof obj.name === "string";
}

if (isUser(data)) {
  console.log(data.name); // TypeScript knows it's User
}

25. What is discriminated union?

interface Circle {
  kind: "circle";
  radius: number;
}

interface Square {
  kind: "square";
  sideLength: number;
}

type Shape = Circle | Square;

function getArea(shape: Shape): number {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2;
    case "square":
      return shape.sideLength ** 2;
  }
}

26. What are index signatures?

interface StringDictionary {
  [key: string]: string;
}

interface NumberDictionary {
  [index: number]: string;
  length: number;
}

const dict: StringDictionary = {
  name: "John",
  city: "NYC"
};

27. What is function overloading?

function greet(person: string): string;
function greet(persons: string[]): string[];
function greet(personOrPersons: string | string[]): string | string[] {
  if (Array.isArray(personOrPersons)) {
    return personOrPersons.map(p => `Hello, ${p}!`);
  }
  return `Hello, ${personOrPersons}!`;
}

28. What are abstract classes?

abstract class Animal {
  abstract makeSound(): void;
  
  move(): void {
    console.log("Moving...");
  }
}

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

// const animal = new Animal(); // Error: Cannot instantiate abstract class
const dog = new Dog();

29. What is the difference between private and # private fields?

class Example {
  private tsPrivate: string = "TypeScript private";
  #jsPrivate: string = "JavaScript private";
}

// tsPrivate: Compile-time only, accessible at runtime
// #jsPrivate: True runtime privacy (ES2022)

30. What are decorators?

function log(target: any, key: string, descriptor: PropertyDescriptor) {
  const original = descriptor.value;
  descriptor.value = function (...args: any[]) {
    console.log(`Calling ${key} with`, args);
    return original.apply(this, args);
  };
}

class Calculator {
  @log
  add(a: number, b: number) {
    return a + b;
  }
}

31. What is declaration merging?

interface Box {
  height: number;
}

interface Box {
  width: number;
}

// Merged into:
// interface Box {
//   height: number;
//   width: number;
// }

32. What is a namespace?

namespace Validation {
  export interface StringValidator {
    isValid(s: string): boolean;
  }
  
  export class EmailValidator implements StringValidator {
    isValid(s: string): boolean {
      return s.includes("@");
    }
  }
}

const validator = new Validation.EmailValidator();

33. What is the satisfies operator?

type Colors = "red" | "green" | "blue";
type RGB = [number, number, number];

const palette = {
  red: [255, 0, 0],
  green: "#00ff00",
  blue: [0, 0, 255]
} satisfies Record<Colors, string | RGB>;

// palette.green is still typed as string (not string | RGB)
palette.green.toUpperCase(); // OK!

34. What are template literal types?

type EventName = "click" | "scroll" | "mousemove";
type EventHandler = `on${Capitalize<EventName>}`;
// "onClick" | "onScroll" | "onMousemove"

type Getter<T extends string> = `get${Capitalize<T>}`;
type NameGetter = Getter<"name">; // "getName"

35. What is the as const assertion?

const config = {
  endpoint: "/api",
  timeout: 3000
} as const;

// type is { readonly endpoint: "/api"; readonly timeout: 3000 }
// Without as const: { endpoint: string; timeout: number }

Advanced Questions (36-50)

36. Explain covariance and contravariance

// Covariant: T can be subtype
type Getter<T> = () => T;

// Contravariant: T can be supertype
type Setter<T> = (value: T) => void;

// Invariant: T must be exact type
type GetterSetter<T> = {
  get: () => T;
  set: (value: T) => void;
};

37. What are higher-order types?

type HigherOrderType<T, U> = T extends (...args: infer A) => any
  ? (...args: A) => U
  : never;

type Original = (a: string, b: number) => boolean;
type Modified = HigherOrderType<Original, string>;
// (a: string, b: number) => string

38. Explain recursive types

type JSONValue =
  | string
  | number
  | boolean
  | null
  | JSONValue[]
  | { [key: string]: JSONValue };

type DeepPartial<T> = T extends object
  ? { [P in keyof T]?: DeepPartial<T[P]> }
  : T;

39. What is the NoInfer utility type?

function createState<T>(initial: T, defaults: NoInfer<T>) {
  return { value: initial, defaults };
}

// Without NoInfer, T would be inferred from both parameters
// With NoInfer, T is only inferred from 'initial'

40. How do you handle this in TypeScript?

interface ClickHandler {
  onClick(this: HTMLButtonElement, event: Event): void;
}

function handleClick(this: HTMLButtonElement, event: Event) {
  console.log(this.textContent); // 'this' is typed
}

41. What are branded/nominal types?

type UserId = string & { readonly brand: unique symbol };
type OrderId = string & { readonly brand: unique symbol };

function createUserId(id: string): UserId {
  return id as UserId;
}

function getUser(id: UserId) { /* ... */ }

const userId = createUserId("123");
const orderId = "456" as OrderId;

getUser(userId);  // OK
getUser(orderId); // Error!

42. Explain distributive conditional types

type ToArray<T> = T extends any ? T[] : never;

type StrOrNumArray = ToArray<string | number>;
// string[] | number[]  (not (string | number)[])

// Prevent distribution with tuple
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never;
type StrOrNumArrayNonDist = ToArrayNonDist<string | number>;
// (string | number)[]

43. What is a module augmentation?

// Extending existing module
declare module "express" {
  interface Request {
    user?: { id: string; name: string };
  }
}

// Now req.user is available in Express handlers

44. How do you create a type-safe event emitter?

type EventMap = {
  login: { userId: string };
  logout: undefined;
  error: Error;
};

class TypedEventEmitter<T extends Record<string, any>> {
  on<K extends keyof T>(event: K, listener: (data: T[K]) => void): void {
    // implementation
  }
  
  emit<K extends keyof T>(event: K, data: T[K]): void {
    // implementation
  }
}

const emitter = new TypedEventEmitter<EventMap>();
emitter.on("login", (data) => console.log(data.userId));

45. What is a variadic tuple type?

type Concat<T extends unknown[], U extends unknown[]> = [...T, ...U];

type Result = Concat<[1, 2], [3, 4]>; // [1, 2, 3, 4]

function concat<T extends unknown[], U extends unknown[]>(
  arr1: T,
  arr2: U
): [...T, ...U] {
  return [...arr1, ...arr2];
}

46. Explain the extends constraint with multiple types

function merge<T extends object, U extends object>(obj1: T, obj2: U): T & U {
  return { ...obj1, ...obj2 };
}

type Keys<T extends Record<string, unknown>> = keyof T;

47. What is a const type parameter?

function identity<const T>(value: T): T {
  return value;
}

const result = identity({ x: 1, y: 2 });
// type is { readonly x: 1; readonly y: 2 }
// Without const: { x: number; y: number }

48. How do you implement a type-safe builder pattern?

class QueryBuilder<T extends object = {}> {
  private query: T = {} as T;

  select<K extends string>(field: K): QueryBuilder<T & Record<K, true>> {
    return this as any;
  }

  where<K extends keyof T>(field: K, value: any): this {
    return this;
  }

  build(): T {
    return this.query;
  }
}

const query = new QueryBuilder()
  .select("name")
  .select("age")
  .where("name", "John") // Only "name" | "age" allowed
  .build();

49. What are assertion functions?

function assertIsString(value: unknown): asserts value is string {
  if (typeof value !== "string") {
    throw new Error("Not a string!");
  }
}

function processValue(value: unknown) {
  assertIsString(value);
  // TypeScript now knows value is string
  console.log(value.toUpperCase());
}

50. How do you handle async type inference?

type Awaited<T> = T extends Promise<infer U> ? Awaited<U> : T;

async function fetchData(): Promise<{ name: string }> {
  return { name: "John" };
}

type Data = Awaited<ReturnType<typeof fetchData>>; // { name: string }

// With Promise.all
async function fetchAll() {
  const [user, posts] = await Promise.all([
    fetchUser(),
    fetchPosts()
  ]);
  // Both user and posts are correctly typed
}

Interview Tips

  1. Practice coding: Type these examples yourself
  2. Understand the “why”: Know when to use each feature
  3. Real-world examples: Relate concepts to actual projects
  4. Stay updated: TypeScript evolves rapidly (check 5.0+ features)
  5. Know the config: Understand tsconfig.json options like strict, noImplicitAny

Good luck with your TypeScript interview!