Top 50 TypeScript Interview Questions
Curated questions covering types, interfaces, generics, decorators, enums, utility types, mapped types, and TypeScript configuration.
What is TypeScript and why use it over JavaScript?
TypeScript is a statically typed superset of JavaScript that compiles to plain JavaScript. Benefits: catch errors at compile time, better IDE support (autocomplete, refactoring), self-documenting code, safer large-scale applications, and first-class support for modern JavaScript features.
What is the difference between type and interface?
- interface - can be extended with extends and merged (declaration merging). Best for object shapes and public APIs.
- type - more flexible; supports unions, intersections, primitives, tuples, and mapped types. Cannot be merged.
- Prefer interface for object shapes; use type for unions, intersections, and complex types.
interface User { name: string; age: number; }\ninterface User { email: string; } // merged - OK\n\ntype ID = string | number; // union - only with type\ntype Point = { x: number } & { y: number }; // intersectionWhat are generics in TypeScript?
Generics allow writing reusable, type-safe code that works with multiple types. They act as type placeholders resolved at usage time.
function identity<T>(value: T): T { return value; }\nidentity<string>("hello"); // T = string\nidentity<number>(42); // T = number\n\ninterface ApiResponse<T> {\n data: T;\n status: number;\n message: string;\n}What is the difference between any, unknown, and never?
- any - disables type checking. Avoid it as it defeats the purpose of TypeScript.
- unknown - type-safe alternative to any. Must narrow the type before using it.
- never - represents values that never occur. Used for exhaustive checks and functions that always throw.
let a: unknown = "hello";\nif (typeof a === "string") a.toUpperCase(); // must narrow first\n\nfunction fail(msg: string): never {\n throw new Error(msg); // never returns\n}What are TypeScript utility types?
Utility types are built-in generic types that transform existing types.
- Partial
- makes all properties optional. - Required
- makes all properties required. - Readonly
- makes all properties read-only. - Pick
- picks a subset of properties. - Omit
- removes specified properties. - Record
- creates an object type with keys K and values V. - ReturnType
- extracts the return type of a function. - NonNullable
- removes null and undefined.
interface User { id: number; name: string; email: string; }\ntype PartialUser = Partial<User>;\ntype UserPreview = Pick<User, "id" | "name">;\ntype UserWithoutEmail = Omit<User, "email">;\ntype StringMap = Record<string, string>;What is type narrowing?
Type narrowing refines a broad type to a more specific one within a conditional block using control flow analysis.
function process(value: string | number) {\n if (typeof value === "string") {\n return value.toUpperCase(); // narrowed to string\n }\n return value.toFixed(2); // narrowed to number\n}\n\nfunction handle(err: unknown) {\n if (err instanceof Error) console.log(err.message);\n}What are enums in TypeScript?
Enums define a set of named constants. TypeScript supports numeric enums (default), string enums, and const enums.
enum Direction { Up, Down, Left, Right } // 0,1,2,3\n\nenum Status { Active = "ACTIVE", Inactive = "INACTIVE" } // string enum\n\nconst enum Color { Red, Green, Blue } // inlined at compile timeWhat is the difference between interface extending and type intersection?
- interface extends - creates a new interface inheriting all members. Errors on conflicting property types.
- type intersection (&) - combines types. Conflicting property types result in never.
interface A { x: number; }\ninterface B extends A { y: number; } // B has x and y\n\ntype C = { x: number } & { y: number }; // same result\ntype Conflict = { x: string } & { x: number }; // x becomes neverWhat are decorators in TypeScript?
Decorators are special declarations that attach metadata or modify classes, methods, properties, or parameters. Widely used in Angular and NestJS. Enable with experimentalDecorators in tsconfig.
function Log(target: any, key: string, descriptor: PropertyDescriptor) {\n const original = descriptor.value;\n descriptor.value = function (...args: any[]) {\n console.log(`Calling ${key} with`, args);\n return original.apply(this, args);\n };\n return descriptor;\n}\n\nclass Service {\n @Log\n greet(name: string) { return `Hello, ${name}`; }\n}What is a union type and an intersection type?
- Union (|) - a value can be one of several types. Use type guards to narrow.
- Intersection (&) - a value must satisfy all combined types simultaneously.
type StringOrNumber = string | number;\ntype AdminUser = User & { adminLevel: number };\n\nfunction format(val: string | number): string {\n return typeof val === "string" ? val : val.toString();\n}What is the readonly modifier?
readonly prevents a property from being reassigned after initialization. It applies at compile time only. Use Readonly
interface Config {\n readonly apiUrl: string;\n readonly timeout: number;\n}\nconst config: Config = { apiUrl: "/api", timeout: 3000 };\n// config.apiUrl = "/new"; // Error: cannot assign to readonly propertyWhat are mapped types?
Mapped types create new types by transforming each property of an existing type. They are the foundation of utility types like Partial, Readonly, and Record.
type Nullable<T> = { [K in keyof T]: T[K] | null };\ntype Optional<T> = { [K in keyof T]?: T[K] };\ntype Mutable<T> = { -readonly [K in keyof T]: T[K] }; // remove readonly\n\ninterface User { id: number; name: string; }\ntype NullableUser = Nullable<User>; // { id: number|null; name: string|null }What is a conditional type?
Conditional types select a type based on a condition: T extends U ? X : Y. They enable powerful type-level logic.
type IsString<T> = T extends string ? true : false;\ntype A = IsString<string>; // true\ntype B = IsString<number>; // false\n\ntype NonNullable<T> = T extends null | undefined ? never : T;\ntype Flatten<T> = T extends Array<infer U> ? U : T;What is the difference between as and satisfies?
- as - type assertion. Overrides TypeScript inference. Can be unsafe.
- satisfies (TS 4.9+) - validates that a value matches a type without widening it. Safer than as.
const palette = {\n red: [255, 0, 0],\n green: "#00ff00",\n} satisfies Record<string, string | number[]>;\n\n// palette.red is still number[], not string | number[]\npalette.red.map(x => x); // OK - type preservedWhat is a discriminated union?
A discriminated union is a union of types that share a common literal property (discriminant). TypeScript uses the discriminant to narrow the type in switch/if statements.
type Shape =\n | { kind: "circle"; radius: number }\n | { kind: "square"; side: number };\n\nfunction area(shape: Shape): number {\n switch (shape.kind) {\n case "circle": return Math.PI * shape.radius ** 2;\n case "square": return shape.side ** 2;\n }\n}What is the keyof operator?
keyof produces a union of all property keys of a type. Combined with generics, it enables type-safe property access.
interface User { id: number; name: string; email: string; }\ntype UserKeys = keyof User; // "id" | "name" | "email"\n\nfunction getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {\n return obj[key]; // fully type-safe\n}What is the typeof operator in TypeScript type positions?
In TypeScript, typeof can be used in type positions to capture the type of a variable or function. Different from JavaScript runtime typeof.
const config = { host: "localhost", port: 3000 };\ntype Config = typeof config; // { host: string; port: number }\n\nfunction createUser(name: string, age: number) { return { name, age }; }\ntype User = ReturnType<typeof createUser>; // { name: string; age: number }What are template literal types?
Template literal types build string types by combining string literals, similar to JavaScript template literals.
type EventName = "click" | "focus" | "blur";\ntype Handler = `on${Capitalize<EventName>}`; // "onClick" | "onFocus" | "onBlur"\n\ntype CSSProperty = `${string}-${string}`;\nconst prop: CSSProperty = "background-color"; // OKWhat is strict mode in TypeScript?
Enabling "strict": true in tsconfig.json activates a set of strict type-checking options.
- strictNullChecks - null and undefined are not assignable to other types.
- noImplicitAny - error when TypeScript infers any.
- strictFunctionTypes - stricter checking of function parameter types.
- strictPropertyInitialization - class properties must be initialized in constructor.
// tsconfig.json\n{\n "compilerOptions": {\n "strict": true,\n "target": "ES2022",\n "module": "ESNext"\n }\n}What is declaration merging?
Declaration merging combines multiple declarations with the same name into a single definition. Works with interfaces, namespaces, and enums.
interface Window { myPlugin: () => void; }\n// Merges with the built-in Window interface\n// Now window.myPlugin is type-safe\n\n// Augmenting a module\ndeclare module "express" {\n interface Request { user?: { id: number; role: string }; }\n}What is the difference between abstract class and interface?
- Abstract class - can have implemented methods, constructors, and state. A class can extend only one.
- Interface - only declarations (no implementation). A class can implement multiple interfaces.
- Use abstract class when sharing implementation; use interface for defining contracts.
abstract class Animal {\n abstract speak(): string;\n breathe() { return "breathing"; } // concrete method\n}\n\ninterface Flyable { fly(): void; }\ninterface Swimmable { swim(): void; }\nclass Duck extends Animal implements Flyable, Swimmable { ... }What are index signatures?
Index signatures allow defining types for objects with dynamic keys.
interface StringMap { [key: string]: string; }\nconst headers: StringMap = { "Content-Type": "application/json" };\n\ninterface Config {\n timeout: number;\n [key: string]: number; // all values must be number\n}What is the infer keyword?
infer is used inside conditional types to capture and reuse a type within the condition. It is how ReturnType and Parameters utility types are implemented.
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;\ntype Parameters<T> = T extends (...args: infer P) => any ? P : never;\n\nfunction greet(name: string): string { return `Hello ${name}`; }\ntype R = ReturnType<typeof greet>; // string\ntype P = Parameters<typeof greet>; // [name: string]What is module augmentation?
Module augmentation extends existing module types without modifying the original source. Useful for adding types to third-party libraries.
// Extend Express Request type\nimport "express";\ndeclare module "express" {\n interface Request {\n user?: { id: number; role: string };\n }\n}\n// Now req.user is type-safe in all Express handlersWhat is the difference between .ts and .d.ts files?
- .ts - regular TypeScript source files. Contain implementation code that compiles to JavaScript.
- .d.ts - declaration files. Contain only type information (no implementation). Used to type JavaScript libraries.
- Generated automatically with tsc --declaration or written manually for JS libraries.
What is the difference between type assertion and type casting?
- Type assertion (as Type or
) - tells TypeScript to treat a value as a specific type. No runtime effect. Can be unsafe. - Type casting - actual runtime conversion (e.g., Number("42")). TypeScript has no built-in casting; use type assertions for compile-time only.
const input = document.getElementById("name") as HTMLInputElement;\nconst value = (input as HTMLInputElement).value;\n\n// Double assertion for incompatible types\nconst x = "hello" as unknown as number; // unsafe!What is the difference between Exclude and Extract utility types?
- Exclude
- removes types from T that are assignable to U. - Extract
- keeps only types from T that are assignable to U.
type T = string | number | boolean;\ntype NoBoolean = Exclude<T, boolean>; // string | number\ntype OnlyString = Extract<T, string>; // string\n\ntype NonNullable<T> = Exclude<T, null | undefined>;What is the difference between interface and type for function signatures?
- Both can describe function types.
- interface uses call signature syntax.
- type uses arrow function syntax.
- Both are equivalent for function types.
// Interface call signature\ninterface Formatter {\n (value: string, options?: object): string;\n}\n\n// Type alias\ntype Formatter = (value: string, options?: object) => string;\n\n// Both work the same way\nconst fmt: Formatter = (v) => v.trim();What is the difference between readonly array and regular array?
- readonly T[] or ReadonlyArray
- cannot push, pop, or mutate. Compile-time only. - T[] - mutable array.
- Use readonly arrays for function parameters to prevent accidental mutation.
function sum(nums: readonly number[]): number {\n // nums.push(1); // Error: push does not exist on readonly\n return nums.reduce((a, b) => a + b, 0);\n}\n\nconst arr: ReadonlyArray<number> = [1, 2, 3];What is the difference between namespace and module in TypeScript?
- Module (ES module) - file-based. Uses import/export. The modern standard. Preferred.
- Namespace - TypeScript-specific. Uses namespace keyword. Useful for organizing code in global scripts or declaration files.
- Avoid namespaces in modern TypeScript projects that use modules.
// Namespace (legacy)\nnamespace Utils {\n export function format(s: string) { return s.trim(); }\n}\nUtils.format("hello");\n\n// Module (modern)\nexport function format(s: string) { return s.trim(); }What is the difference between overloads and union types for function signatures?
- Union types - simpler. Single implementation handles all types.
- Function overloads - provide multiple specific signatures. Better for complex type relationships where return type depends on input type.
// Overloads\nfunction process(x: string): string;\nfunction process(x: number): number;\nfunction process(x: string | number): string | number {\n return typeof x === "string" ? x.toUpperCase() : x * 2;\n}What is the difference between type widening and narrowing?
- Type widening - TypeScript infers a broader type from a literal. let x = "hello" infers string, not "hello".
- Type narrowing - TypeScript refines a broad type to a specific one using type guards (typeof, instanceof, in, discriminant).
- Use as const to prevent widening: const x = "hello" as const infers "hello" (literal type).
let x = "hello"; // widened to string\nconst y = "hello"; // literal type "hello"\nconst z = "hello" as const; // literal type "hello"\n\nconst arr = [1, 2, 3] as const; // readonly [1, 2, 3]What is the difference between Pick and Omit?
- Pick
- creates a type with only the specified keys from T. - Omit
- creates a type with all keys from T except the specified ones. - Both are useful for creating subset types from larger interfaces.
interface User { id: number; name: string; email: string; password: string; }\n\ntype PublicUser = Omit<User, "password">; // id, name, email\ntype UserCredentials = Pick<User, "email" | "password">; // email, passwordWhat is the difference between Parameters and ReturnType utility types?
- Parameters
- extracts the parameter types of a function as a tuple. - ReturnType
- extracts the return type of a function. - Both use the infer keyword internally.
function createUser(name: string, age: number, role: string) {\n return { name, age, role };\n}\n\ntype Params = Parameters<typeof createUser>; // [string, number, string]\ntype Result = ReturnType<typeof createUser>; // { name: string; age: number; role: string }What is the difference between InstanceType and ConstructorParameters?
- InstanceType
- extracts the instance type of a constructor function/class. - ConstructorParameters
- extracts the constructor parameter types as a tuple.
class User {\n constructor(public name: string, public age: number) {}\n}\n\ntype UserInstance = InstanceType<typeof User>; // User\ntype UserParams = ConstructorParameters<typeof User>; // [string, number]What is the difference between Awaited and Promise in TypeScript?
- Promise
- represents a pending async operation that resolves to T. - Awaited
(TS 4.5+) - recursively unwraps Promise types. Awaited > = string. Awaited >> = number.
type A = Awaited<Promise<string>>; // string\ntype B = Awaited<Promise<Promise<number>>>; // number\ntype C = Awaited<string>; // string (non-Promise passthrough)\n\nasync function fetchUser(): Promise<User> { ... }\ntype FetchResult = Awaited<ReturnType<typeof fetchUser>>; // UserWhat is the difference between type-only imports and regular imports?
- import type { T } - imports only the type. Erased at compile time. Cannot be used as a value.
- import { T } - imports the value. Kept in compiled output if used as a value.
- Use import type for types to improve build performance and avoid circular dependency issues.
import type { User } from "./types"; // type-only, erased at runtime\nimport { createUser } from "./utils"; // value import, kept\n\n// TypeScript 5.0+ inline type imports\nimport { type User, createUser } from "./module";What is the difference between satisfies and as const?
- as const - makes all values literal types and readonly. No type validation.
- satisfies - validates the value against a type while preserving the most specific type. Does not widen types.
const config = {\n port: 3000,\n host: "localhost"\n} as const; // { readonly port: 3000; readonly host: "localhost" }\n\nconst config2 = {\n port: 3000,\n host: "localhost"\n} satisfies { port: number; host: string };\n// port is still 3000 (literal), not numberWhat is the difference between TypeScript project references and regular imports?
- Regular imports - all files compiled together. Slow for large projects.
- Project references (tsconfig references) - split large projects into smaller sub-projects. Incremental compilation. Faster builds.
- Used in monorepos and large codebases.
// tsconfig.json\n{\n "references": [\n { "path": "./packages/core" },\n { "path": "./packages/ui" }\n ]\n}What is the difference between TypeScript strict null checks and optional chaining?
- strictNullChecks - compiler option that makes null and undefined separate types. Forces you to handle them explicitly.
- Optional chaining (?.) - runtime operator that short-circuits to undefined if a value is null/undefined.
- Both work together: strictNullChecks catches potential null access at compile time; ?. handles it at runtime.
// With strictNullChecks\nfunction getCity(user: User | null): string | undefined {\n return user?.address?.city; // optional chaining\n}What is the difference between TypeScript path aliases and relative imports?
- Relative imports (./utils) - explicit path. Works everywhere. Can become verbose in deep directory structures.
- Path aliases (@/utils) - configured in tsconfig.json paths. Cleaner imports. Requires bundler configuration to resolve.
// tsconfig.json\n{\n "compilerOptions": {\n "paths": { "@/*": ["./src/*"] }\n }\n}\n\n// Usage\nimport { formatDate } from "@/utils/date"; // instead of "../../../utils/date"What is the difference between TypeScript 4.x and 5.x features?
- TS 4.0 - variadic tuple types, labeled tuple elements.
- TS 4.1 - template literal types, key remapping in mapped types.
- TS 4.5 - Awaited type, type-only imports in regular imports.
- TS 4.9 - satisfies operator, auto-accessors.
- TS 5.0 - decorators (standard), const type parameters.
- TS 5.1 - unrelated types for setters/getters.
- TS 5.2 - using declarations (explicit resource management).
What is the using declaration in TypeScript 5.2?
The using declaration (TypeScript 5.2, ECMAScript proposal) automatically disposes resources when they go out of scope, similar to C# using or Python with. The resource must implement Symbol.dispose.
class DatabaseConnection {\n [Symbol.dispose]() { this.close(); }\n close() { console.log("Connection closed"); }\n}\n\n{\n using conn = new DatabaseConnection();\n // use conn...\n} // conn.close() called automatically hereWhat is the difference between TypeScript const type parameters and regular type parameters?
Const type parameters (TS 5.0) infer literal types instead of widened types, similar to as const but for generic functions.
// Without const: T inferred as string[]\nfunction identity<T>(arr: T): T { return arr; }\nidentity(["a", "b"]); // string[]\n\n// With const: T inferred as ["a", "b"]\nfunction identity<const T>(arr: T): T { return arr; }\nidentity(["a", "b"]); // readonly ["a", "b"]What is the difference between TypeScript declaration files for CommonJS and ESM?
- CommonJS declaration - uses module.exports = ... or export = .... Requires esModuleInterop for default imports.
- ESM declaration - uses export default and named exports. Modern standard.
- Use "moduleResolution": "bundler" or "node16" in tsconfig for correct ESM resolution.
// CommonJS .d.ts\ndeclare module "my-lib" {\n export function helper(): void;\n export default class MyLib { ... }\n}\n\n// ESM .d.ts\nexport function helper(): void;\nexport default class MyLib { ... }What is the difference between TypeScript strict mode options?
- strict: true enables all strict options at once.
- strictNullChecks - null/undefined are separate types.
- strictFunctionTypes - contravariant function parameter checking.
- strictBindCallApply - strict types for bind/call/apply.
- strictPropertyInitialization - class properties must be initialized.
- noImplicitAny - error on implicit any.
- noImplicitThis - error on implicit this.
- alwaysStrict - emit "use strict" in output.
What is the difference between TypeScript enums and const enums?
- Regular enum - compiled to a JavaScript object. Can be iterated. Supports reverse mapping for numeric enums.
- const enum - inlined at compile time. No JavaScript object generated. Cannot be iterated. Smaller bundle.
- Avoid const enum in declaration files (.d.ts) as they cause issues with isolated modules.
enum Color { Red, Green, Blue } // compiled to JS object\nconst enum Direction { Up, Down } // inlined: Direction.Up becomes 0\n\n// Reverse mapping (numeric enums only)\nColor[0]; // "Red"What is the difference between TypeScript abstract methods and regular methods?
- Abstract method - declared in abstract class with no implementation. Subclasses must implement it.
- Regular method - has an implementation. Can be overridden but does not have to be.
- Abstract classes cannot be instantiated directly.
abstract class Shape {\n abstract area(): number; // must implement\n toString() { return `Area: ${this.area()}`; } // concrete\n}\n\nclass Circle extends Shape {\n constructor(private r: number) { super(); }\n area() { return Math.PI * this.r ** 2; } // required\n}What is the difference between TypeScript type guards and assertion functions?
- Type guard (is keyword) - a function that returns a boolean and narrows the type if true.
- Assertion function (asserts keyword) - throws if condition is false. Narrows the type after the call.
// Type guard\nfunction isString(val: unknown): val is string {\n return typeof val === "string";\n}\n\n// Assertion function\nfunction assertString(val: unknown): asserts val is string {\n if (typeof val !== "string") throw new Error("Not a string");\n}\n\nassertString(value); // value is string after this lineWhat is the difference between TypeScript accessor keyword and get/set?
The accessor keyword (TS 4.9+) is shorthand for defining a class field with auto-generated getter and setter. It is cleaner than manually writing get/set pairs.
// Traditional get/set\nclass User {\n private _name = "";\n get name() { return this._name; }\n set name(v: string) { this._name = v; }\n}\n\n// accessor keyword (TS 4.9+)\nclass User {\n accessor name = ""; // auto-generates get/set\n}