TypeScript Interview Questions
TypeScript Interview Questions
Section titled “TypeScript Interview Questions”Type System Basics
Section titled “Type System Basics”Q: What is the difference between type and interface?
// interface — extendable, mergeable (declaration merging)interface User { name: string; age: number;}interface User { email: string; // merged with above}
// type — more flexible, supports unions/intersectionstype ID = string | number;type Point = { x: number; y: number };type Named = Point & { name: string };Use interface for object shapes and public APIs. Use type for unions, intersections, and aliases.
Q: What is the difference between any, unknown, and never?
// any — disables type checking (avoid)let x: any = 5;x.foo.bar; // no error
// unknown — type-safe any, must narrow before uselet y: unknown = 5;// y.foo; // Errorif (typeof y === 'string') y.toUpperCase(); // OK after narrowing
// never — represents impossible valuesfunction fail(msg: string): never { throw new Error(msg);}
// Exhaustive checktype Shape = 'circle' | 'square';function area(s: Shape) { if (s === 'circle') return Math.PI; if (s === 'square') return 1; const _exhaustive: never = s; // Error if Shape has unhandled case}Q: What is type narrowing?
function process(value: string | number) { if (typeof value === 'string') { return value.toUpperCase(); // narrowed to string } return value.toFixed(2); // narrowed to number}
// instanceof narrowingfunction handle(err: Error | string) { if (err instanceof Error) { console.log(err.message); }}
// in operator narrowingtype Cat = { meow: () => void };type Dog = { bark: () => void };function speak(animal: Cat | Dog) { if ('meow' in animal) animal.meow(); else animal.bark();}Q: What are type guards?
// typeof guardfunction isString(val: unknown): val is string { return typeof val === 'string';}
// Custom type guardinterface Admin { role: 'admin'; permissions: string[] }interface User { role: 'user'; name: string }
function isAdmin(user: Admin | User): user is Admin { return user.role === 'admin';}
const u: Admin | User = getUser();if (isAdmin(u)) { console.log(u.permissions); // TypeScript knows it's Admin}Generics
Section titled “Generics”Q: What are generics and why use them?
// Without generics — loses type infofunction first(arr: any[]): any { return arr[0]; }
// With generics — type-safefunction first<T>(arr: T[]): T { return arr[0]; }
const n = first([1, 2, 3]); // n: numberconst s = first(['a', 'b']); // s: stringQ: What are generic constraints?
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key];}
const user = { name: "Alice", age: 30 };getProperty(user, 'name'); // stringgetProperty(user, 'age'); // number// getProperty(user, 'foo'); // Error — 'foo' not in keyof UserUtility Types
Section titled “Utility Types”Q: Explain the most useful utility types.
interface User { id: number; name: string; email: string; age?: number;}
// Partial — all properties optionaltype PartialUser = Partial<User>;
// Required — all properties requiredtype RequiredUser = Required<User>;
// Pick — select subset of propertiestype UserPreview = Pick<User, 'id' | 'name'>;
// Omit — exclude propertiestype UserWithoutId = Omit<User, 'id'>;
// Readonly — all properties readonlytype ReadonlyUser = Readonly<User>;
// Record — map typetype Roles = Record<string, User[]>;
// ReturnType — extract return type of functionfunction getUser() { return { id: 1, name: "Alice" }; }type UserType = ReturnType<typeof getUser>;
// Parameters — extract parameter typestype Params = Parameters<typeof getUser>;
// NonNullable — remove null/undefinedtype SafeString = NonNullable<string | null | undefined>; // stringQ: What are mapped types?
// Make all properties optionaltype Optional<T> = { [K in keyof T]?: T[K] };
// Make all properties nullabletype Nullable<T> = { [K in keyof T]: T[K] | null };
// Make all properties readonlytype Immutable<T> = { readonly [K in keyof T]: T[K] };
// Conditional mapped typetype NonFunctions<T> = { [K in keyof T]: T[K] extends Function ? never : K}[keyof T];Q: What are conditional types?
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // truetype B = IsString<number>; // false
// Infer keywordtype UnpackPromise<T> = T extends Promise<infer U> ? U : T;
type Resolved = UnpackPromise<Promise<string>>; // stringtype Plain = UnpackPromise<number>; // numberQ: What are template literal types?
type EventName = 'click' | 'focus' | 'blur';type Handler = `on${Capitalize<EventName>}`;// 'onClick' | 'onFocus' | 'onBlur'
type CSSProperty = 'margin' | 'padding';type CSSDirection = 'Top' | 'Right' | 'Bottom' | 'Left';type CSSProp = `${CSSProperty}${CSSDirection}`;// 'marginTop' | 'marginRight' | ... | 'paddingLeft'Advanced Patterns
Section titled “Advanced Patterns”Q: What is declaration merging?
// Interface merginginterface Window { myCustomProp: string;}window.myCustomProp = "hello"; // now valid
// Namespace merging with functionfunction validate(x: string): boolean { return x.length > 0; }namespace validate { export const minLength = 1;}validate("test");validate.minLength; // 1Q: What is the satisfies operator (TypeScript 4.9)?
type Colors = 'red' | 'green' | 'blue';type ColorMap = Record<Colors, string | [number, number, number]>;
const palette = { red: [255, 0, 0], green: "#00ff00", blue: [0, 0, 255]} satisfies ColorMap;
// satisfies validates the type but preserves the literal typepalette.green.toUpperCase(); // OK — TypeScript knows it's string, not string | number[]Q: What is infer and how is it used?
// Extract element type from arraytype ElementType<T> = T extends (infer E)[] ? E : never;type Num = ElementType<number[]>; // number
// Extract first argument typetype FirstArg<T> = T extends (first: infer F, ...rest: any[]) => any ? F : never;type F = FirstArg<(x: string, y: number) => void>; // string
// Unwrap nested Promisetype Awaited<T> = T extends Promise<infer U> ? Awaited<U> : T;Q: What is the difference between interface extending and type intersection?
interface A { a: string }interface B extends A { b: number } // B has a and b
type C = { c: boolean }type D = A & C // D has a and c
// Key difference: interface extends reports errors at definition// type intersection merges silently (may produce never for conflicting types)interface X { prop: string }interface Y extends X { prop: number } // Error at definition
type P = { prop: string } & { prop: number } // prop: never — no error at definition