Back to Interview PrepGet interview ready with AILearn each topic the way interviewers ask it, then answer like you would in the interview.

Chapter 13 of 13

Top 45 TypeScript Interview Questions

Types, interfaces, generics, classes, and advanced type-system features - organized by topic and difficulty, with explanations that go beyond a one-line definition and a code example for each one. Search or filter to find what you need, and share a direct link to any question.

Level:

Showing 45 of 45 questions

Questions reviewed0/45 · 0%

The first 15 of 45 questions are free to read. Sign up free to unlock every answer and track your progress.

Unlock all 45

TypeScript is a typed superset of JavaScript created by Microsoft: every valid JavaScript file is also valid TypeScript, and TypeScript adds optional static types, interfaces, and compile-time checks on top. The TypeScript compiler (tsc) type-checks the code and then strips the types away, emitting plain JavaScript that runs anywhere JavaScript already runs. A common mistake is thinking TypeScript is a separate language that needs its own runtime - it has no runtime of its own at all, it only exists at build/compile time.

Example

// person.ts
function greet(name: string): string {
  return `Hello, ${name}`;
}
// compiles down to plain JavaScript with no type annotations left

Type inference is TypeScript's ability to determine a variable's type automatically from its initial value, without an explicit annotation - for example, `let count = 0` is inferred as `number`. A common mistake is over-annotating every single variable out of habit, which adds noise; inference is reliable for local variables with initializers, and explicit annotations matter most at function boundaries where there is nothing to infer from.

Example

let name = 'Ada'; // inferred as string
let scores = [1, 2, 3]; // inferred as number[]

`any` disables type checking entirely for that value - anything can be assigned to or from it with no compiler complaints. `unknown` also accepts any value, but the compiler refuses to let you call methods or access properties on it until you first narrow it to a more specific type via `typeof`, `instanceof`, or a custom type guard. A common mistake is defaulting to `any` for convenience, which quietly reintroduces the exact bugs TypeScript is meant to catch - unknown is the safer choice whenever the real type genuinely is not known ahead of time.

Example

let value: unknown = 'hello';
// value.toUpperCase(); // Error until narrowed
if (typeof value === 'string') {
  value.toUpperCase(); // OK now
}

never represents a value that can never occur - a function that always throws an exception, or that loops forever and never returns control to its caller, has a return type of never. This differs from void, which does return (just without a meaningful value). A common mistake is confusing never with void; never is also useful in exhaustiveness checks, where an unreachable default branch of a switch is typed never to catch any missed union member at compile time.

Example

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

A tuple, such as `[string, number]`, fixes both the length and the type at each specific position, while an array type like `string[]` allows any number of elements, all of the same type. A common mistake is treating a tuple like a loosely-typed array and pushing extra elements onto it without realizing the compiler still enforces the original fixed shape at the declared positions. Tuples are ideal for small, fixed-shape structures like coordinate pairs or key/value pairs.

Example

let point: [number, number] = [10, 20];
// point = [10, 20, 30]; // Error: too many elements

A numeric enum auto-increments values starting from 0 unless given explicit values, and supports reverse lookup from value to name. A string enum requires every member to have its own explicit string value and has no reverse lookup. A union of string literal types (e.g. `"PENDING" | "SHIPPED"`) generates no runtime object at all - it is purely a compile-time construct. A common mistake is defaulting to enum out of habit; many teams now prefer literal unions since they have zero runtime footprint and work more naturally with plain strings from JSON.

Example

enum Status { Pending, Shipped } // numeric
enum Color { Red = 'RED', Blue = 'BLUE' } // string
type Role = 'admin' | 'editor'; // literal union, no runtime object

A const enum is fully inlined at every usage site during compilation, rather than generating a real runtime lookup object - every reference to a member is replaced with its literal value. This produces smaller and faster output, but it also means the enum cannot be iterated over at runtime and has no reverse lookup, since the object never actually exists in the compiled JavaScript. A common mistake is using a const enum across project boundaries with certain bundler/isolated-module configurations, where inlining behavior can be restricted or disallowed.

Both can describe the shape of an object, and for plain object shapes they are largely interchangeable. Interfaces can be extended with `extends` and merged via declaration merging (multiple interface declarations with the same name combine automatically), while type aliases can additionally name things interfaces cannot express directly, like unions, tuples, or primitive aliases. A common mistake is assuming one is strictly better than the other - most style guides recommend interface for object shapes that may be extended, and type for unions or more complex compositions.

Example

interface User { id: number; name: string }
type Id = string | number; // only possible with type

It means objects satisfying that interface may omit the property entirely, and wherever it is read, its type includes `undefined` in addition to the declared type. A common mistake is forgetting to guard against the missing case before using the value, which under strict mode causes a compile error precisely because the type honestly reflects that the property might not be present.

Example

interface Profile { name: string; age?: number }
const p: Profile = { name: 'Ada' }; // age omitted, that's fine

It allows a property to be assigned once (typically at object creation) but prevents reassignment afterward, enforced entirely at compile time - `readonly` has no effect on the emitted JavaScript, so it offers no runtime protection on its own. A common mistake is expecting readonly to deeply freeze an object; it only prevents reassigning the property itself, not mutating a nested object or array that property points to.

Example

interface Point { readonly x: number }
const p: Point = { x: 5 };
// p.x = 10; // Error: cannot assign to readonly property

`interface B extends A` inherits every member from A and adds its own, and the compiler enforces that B satisfies both shapes together. Type aliases achieve a similar composition using intersections (`type B = A & { extra: string }`), which works for any type, not just interfaces. A common mistake is trying to extend two interfaces with conflicting property types, which produces a compile error - the practical behavior in that case is similar to how conflicting members in an intersection collapse to an unusable (often never) type for that property.

Example

interface Animal { name: string }
interface Dog extends Animal { breed: string }
const d: Dog = { name: 'Rex', breed: 'Labrador' };

Declaration merging is the ability to declare the same interface name more than once, with TypeScript automatically combining all the declarations into a single merged interface containing every member from each declaration. Only interfaces support this - declaring the same type alias name twice is a compile error. A common mistake is relying on merging for regular application code, where it usually signals a naming collision bug; its main legitimate use is augmenting types from third-party libraries or global objects.

Example

interface Window { myGlobal: string; }
// elsewhere:
interface Window { anotherGlobal: number; }
// Window now has both myGlobal and anotherGlobal

Yes - an interface can declare a call signature, such as `interface Comparator { (a: number, b: number): number }`, which describes a callable value rather than an object with named properties. A type alias can express the same thing more concisely as `type Comparator = (a: number, b: number) => number`. A common mistake is assuming interfaces are limited to object property shapes; they can describe function and constructor signatures too, though the type-alias arrow-function syntax is generally considered more readable for pure function types.

Example

type Comparator = (a: number, b: number) => number;
const byValue: Comparator = (a, b) => a - b;

Each parameter gets an annotation after its name (`name: string`), and the return type follows the closing parenthesis of the parameter list (`): number`). TypeScript can often infer the return type from the function body, but writing it explicitly on public/exported functions documents intent and prevents an internal change from silently widening the contract for every caller. A common mistake is over-relying on inference for widely-used functions, where an explicit return type acts as a useful guardrail.

Example

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

An optional parameter (`age?: number`) may be omitted by the caller, and its type includes undefined when read inside the function. A default parameter (`age: number = 18`) may also be omitted, but instead of becoming undefined it takes on the given default value, so its type stays as declared without needing to include undefined. A common mistake is assuming they behave identically - checking `if (age)` behaves differently depending on which one you are dealing with, especially around falsy values like 0.

Example

function greet(name: string, greeting: string = 'Hello'): string {
  return `${greeting}, ${name}`;
}
console.log(greet('Ada')); // 'Hello, Ada'
16.What are function overloads and when would you use them?17.How do you type a callback parameter that another function accepts?18.How does TypeScript check compatibility between two function types (structural function typing)?19.What is a "this" parameter in a TypeScript function, and what problem does it solve?20.What are the three main access modifiers in TypeScript classes?21.What is a constructor parameter property, and what does it save you from writing?22.What is an abstract class, and how does it differ from an interface?23.What does `implements` do when a class implements an interface?24.What does `strictPropertyInitialization` enforce on class fields, and how do you satisfy it?25.How do class field access modifiers (private/protected) differ from the JavaScript `#privateField` syntax?26.What is a union type and what can you do with a value typed as one?27.What is a discriminated union, and how does it improve narrowing?28.What are type guards, and what are the common ways to write one?29.What are utility types like Partial, Pick, and Omit, and why do they matter?30.What is a mapped type, and how do utility types like Partial and Readonly rely on them?31.What is a conditional type, and what is a simple example of one?32.What problem do generics solve compared to using `any`?33.How does TypeScript usually determine the type argument for a generic function call?34.What does constraining a generic with `extends` accomplish, e.g. `<T extends { length: number }>`?35.What does `K extends keyof T` mean in a signature like `function pluck<T, K extends keyof T>(items: T[], key: K)`?36.What is a default type parameter, and where is it useful?37.What is the difference between a default export and a named export?38.What does the `strict` flag in tsconfig.json actually control?39.What do the `target` and `module` compiler options each control, and how do they differ?40.What is a `.d.ts` declaration file used for?41.What is the difference between `moduleResolution: "node"` (or "node10") and `"bundler"`?42.Why is relying heavily on `any` considered a bad practice in TypeScript?43.What is the benefit of enabling `strict` mode from the start of a project rather than adding it later?44.How should a large TypeScript project organize its types - one central file, or colocated with the code that uses them?45.What are barrel files (index.ts re-exports), and what is the trade-off in using them heavily?
You're viewing a free lesson. Sign up free to track your progress and unlock the full course.Sign up freeSign in