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
- Practice coding: Type these examples yourself
- Understand the “why”: Know when to use each feature
- Real-world examples: Relate concepts to actual projects
- Stay updated: TypeScript evolves rapidly (check 5.0+ features)
- Know the config: Understand
tsconfig.jsonoptions likestrict,noImplicitAny
Good luck with your TypeScript interview!