Skip to main contentOpteroAIBeta

TypeScript interview questions

TypeScript interview questions covering the type system, generics, utility types, compiler options, and practical patterns for large codebases.

12 questions
3 easy6 medium3 hard

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

easy
How to approach thisBoth define object shapes. Interfaces support declaration merging (re-opening) and extends. Types support unions, intersections, mapped types, and conditional types. For object shapes, either works. For complex type manipulation (discriminated unions, mapped types), use type. For library APIs where consumers might need to extend, use interface. The difference has narrowed significantly in recent TypeScript versions.

2.Explain generics in TypeScript. Why are they useful?

medium
How to approach thisGenerics let you write functions, classes, and types that work with multiple types while preserving type safety. Example: function identity<T>(arg: T): T returns the same type it receives. Without generics, you would use 'any' (losing type safety) or write duplicate code for each type. Constrain generics with extends: <T extends HasId> ensures T has an id property.

3.What are discriminated unions, and when would you use them?

medium
How to approach thisA discriminated union is a union of types that share a common literal field (the discriminant). Example: type Shape = { kind: 'circle'; radius: number } | { kind: 'square'; side: number }. Switching on the 'kind' field narrows the type in each branch. Use them for: state machines, event handling, API response types, and any situation with multiple variants.

4.Explain the 'unknown' type vs. 'any'. When should you use each?

easy
How to approach thisBoth accept any value, but unknown requires type narrowing before you can use it (safer). any disables type checking entirely (unsafe). Use unknown for: function parameters that accept anything, JSON.parse results, catch clause errors. Use any only as a last resort when wrapping untyped libraries. Prefer unknown + type guards over any.

5.What are mapped types, and how do you use them?

hard
How to approach thisMapped types transform the properties of an existing type. Syntax: { [K in keyof T]: NewType }. Built-in examples: Partial<T> makes all properties optional, Required<T> makes them required, Readonly<T> makes them readonly, Record<K, V> creates a type with keys K and values V. You can add/remove modifiers (+/- readonly, +/- optional) and filter keys with as clauses.

6.How do you handle null and undefined in TypeScript?

easy
How to approach thisEnable strict null checks (strictNullChecks: true). This makes null and undefined their own types, not assignable to other types. Use optional chaining (?.) and nullish coalescing (??) for safe access. Use type guards (if (x !== null)) to narrow types. Prefer explicit null checks over the non-null assertion operator (x!) which bypasses the compiler.

7.What are conditional types in TypeScript?

hard
How to approach thisConditional types select one of two types based on a condition: T extends U ? X : Y. They enable: extracting types (Extract, Exclude), inferring nested types (infer keyword), and building complex type transformations. Example: type Flatten<T> = T extends Array<infer U> ? U : T; extracts the element type from arrays. They are the 'if statements' of the type system.

8.Explain the 'as const' assertion and how it differs from regular const.

medium
How to approach thisA regular const variable prevents reassignment but TypeScript still widens literal types (const x = 'hello' is typed as string in an object). 'as const' makes the entire value deeply readonly and preserves literal types: const x = { name: 'hello' } as const gives x.name the type 'hello' (not string). Use it for: configuration objects, string literal tuples, and enum-like constants.

9.How do you type a function that can return different types based on its input?

hard
How to approach thisUse function overloads or conditional return types. Overloads: declare multiple signatures, then implement with a broad signature. Conditional: function get<T extends 'string' | 'number'>(type: T): T extends 'string' ? string : number. Overloads are more readable for simple cases; conditional types are more powerful for complex mappings.

10.What is the 'satisfies' operator in TypeScript?

medium
How to approach thissatisfies (TypeScript 4.9+) validates that an expression matches a type without widening it. Unlike 'as', it preserves the narrow type. Example: const config = { color: 'red' } satisfies Record<string, string> still knows config.color is 'red', not just string. Use it to validate config objects, theme definitions, and any value where you want type checking without losing specificity.

11.How would you handle types for a REST API client?

medium
How to approach thisDefine request and response types for each endpoint. Use generics for the fetch wrapper: function api<TResponse>(url: string): Promise<TResponse>. Consider using Zod for runtime validation that also generates types. For large APIs, generate types from OpenAPI specs using tools like openapi-typescript. Keep request types separate from response types; they often differ.

12.What is declaration merging, and when does it happen?

medium
How to approach thisDeclaration merging combines multiple declarations with the same name into a single definition. Interfaces merge automatically (adding properties). Namespaces merge with classes, functions, and enums (adding static members). Module augmentation uses merging to extend third-party types. It is how TypeScript's global type augmentation works (declare global). Be aware that type aliases do NOT merge.

Prepare further

More interview topics