PapersAdda
2026 Placement Season is LIVE12,000+ students preparing now

Top 40 TypeScript Interview Questions 2026 — Complete Guide with Solutions

23 min read
Interview Questions
Last Updated: 30 Mar 2026
Verified by Industry Experts
3,940 students found this helpful
Advertisement Placement

TypeScript isn't optional anymore — it's the price of admission. With 60%+ adoption among JavaScript developers, companies like Microsoft, Atlassian, Flipkart, Zepto, Groww, and CRED won't even consider you for mid-level roles without strong TypeScript skills. The payoff? TypeScript-proficient React/Node.js developers earn ₹18-40 LPA at product startups; senior TS architects pull ₹40-65 LPA at top companies.

TypeScript 5.x brought game-changing features — const type parameters, variadic tuple types, satisfies operator, and new decorator standards — and interviewers are already testing them. This guide covers 40 battle-tested questions compiled from real interviews at Microsoft, Atlassian, Flipkart, CRED, and Groww — from basics to the advanced type-level patterns that only senior engineers survive.

Related: React Interview Questions 2026 | Golang Interview Questions 2026 | Microservices Interview Questions 2026


Interview Format at Top Companies

Company TierTypeScript DepthTopics
FAANG/UnicornDeepAdvanced generics, conditional types, inference
Product StartupModeratePractical types, utility types, patterns
Service/ITBasicInterfaces, enums, basic generics

BEGINNER LEVEL — The Foundation (Questions 1–12)

Q1. What is TypeScript and why should you use it over JavaScript?

TypeScript is a statically-typed superset of JavaScript that compiles to plain JavaScript. Key advantages:

FeatureJavaScriptTypeScript
Type checkingRuntimeCompile-time
IDE supportBasicFull IntelliSense
RefactoringError-proneSafe
DocumentationManualTypes as docs
Null safetyNoneStrict null checks

TypeScript catches entire categories of bugs before they reach production: undefined property access, wrong argument types, exhaustiveness errors in switch statements.


Q2. What is the difference between interface and type?

Both define the shape of objects, but they differ in capabilities:

// Interface — extendable via declaration merging
interface User {
  id: number;
  name: string;
}

interface User {
  email: string; // merged with above — works!
}

// Type alias — no declaration merging, but more powerful
type User = {
  id: number;
  name: string;
};

// type can represent things interface cannot
type StringOrNumber = string | number;
type Callback = (event: MouseEvent) => void;
type Tuple = [string, number, boolean];

Rule of thumb: Use interface for object shapes (especially public APIs/libraries). Use type for unions, intersections, and complex type aliases.


Q3. What are TypeScript utility types? List and explain the most important ones.

TypeScript ships with built-in generic utility types that transform existing types.

interface User {
  id: number;
  name: string;
  email: string;
  password: string;
}

// Partial — all properties optional
type UpdateUserDTO = Partial<User>;
// { id?: number; name?: string; email?: string; password?: string; }

// Required — all properties required
type RequiredUser = Required<Partial<User>>;

// Pick — select specific properties
type PublicUser = Pick<User, 'id' | 'name' | 'email'>;

// Omit — exclude specific properties
type SafeUser = Omit<User, 'password'>;

// Readonly — prevent mutation
type ImmutableUser = Readonly<User>;

// Record — map keys to values
type RolePermissions = Record<'admin' | 'editor' | 'viewer', string[]>;

// ReturnType — extract function return type
async function fetchUser(): Promise<User> { /* ... */ }
type FetchResult = ReturnType<typeof fetchUser>; // Promise<User>
type AwaitedUser = Awaited<FetchResult>; // User

// Parameters — extract function parameter types
function createUser(name: string, email: string, role: string) {}
type CreateUserParams = Parameters<typeof createUser>;
// [name: string, email: string, role: string]

// Extract / Exclude
type Role = 'admin' | 'editor' | 'viewer' | 'guest';
type PrivilegedRole = Extract<Role, 'admin' | 'editor'>; // 'admin' | 'editor'
type PublicRole = Exclude<Role, 'admin' | 'editor'>; // 'viewer' | 'guest'

// NonNullable
type MaybeString = string | null | undefined;
type DefiniteString = NonNullable<MaybeString>; // string

Q4. What is type narrowing? Explain the different narrowing techniques.

Type narrowing refines a union type to a more specific type within a code block.

function process(value: string | number | null) {
  // typeof narrowing
  if (typeof value === 'string') {
    console.log(value.toUpperCase()); // string here
  }

  // null/undefined check
  if (value != null) {
    console.log(value + 1); // string | number here
  }

  // instanceof narrowing
  if (value instanceof Date) {
    console.log(value.getFullYear()); // Date here
  }
}

// 'in' narrowing
interface Circle { kind: 'circle'; radius: number; }
interface Square { kind: 'square'; side: number; }
type Shape = Circle | Square;

function getArea(shape: Shape) {
  if ('radius' in shape) {
    return Math.PI * shape.radius ** 2; // Circle here
  }
  return shape.side ** 2; // Square here
}

// Type predicate (custom type guard)
function isCircle(shape: Shape): shape is Circle {
  return shape.kind === 'circle';
}

// Discriminated union — exhaustiveness check
function describeShape(shape: Shape): string {
  switch (shape.kind) {
    case 'circle': return `Circle r=${shape.radius}`;
    case 'square': return `Square s=${shape.side}`;
    default:
      const _exhaustive: never = shape; // compile error if new shape added
      return _exhaustive;
  }
}

Q5. What are discriminated unions and why are they powerful?

Discriminated unions combine multiple types with a common literal property (the discriminant). They enable exhaustive pattern matching at compile time.

type ApiState<T> =
  | { status: 'idle' }
  | { status: 'loading' }
  | { status: 'success'; data: T }
  | { status: 'error'; error: string };

function render<T>(state: ApiState<T>) {
  switch (state.status) {
    case 'idle': return <EmptyState />;
    case 'loading': return <Spinner />;
    case 'success': return <DataView data={state.data} />; // T is available
    case 'error': return <ErrorView message={state.error} />; // string is available
  }
}

This pattern eliminates impossible states like { status: 'success', error: 'Something failed' } — which is the core benefit.


Q6. What are generics and how do you constrain them?

Generics enable writing reusable, type-safe code that works with multiple types.

// Basic generic
function identity<T>(value: T): T {
  return value;
}

// With constraint
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const user = { id: 1, name: 'Aditya', email: '[email protected]' };
const name = getProperty(user, 'name'); // type: string
// getProperty(user, 'phone') — compile error!

// Multiple constraints
function merge<T extends object, U extends object>(obj1: T, obj2: U): T & U {
  return { ...obj1, ...obj2 };
}

// Generic with default
interface Paginated<T, Meta = { total: number; page: number }> {
  data: T[];
  meta: Meta;
}

// Conditional generic
type Unwrap<T> = T extends Promise<infer U> ? U : T;
type StringResult = Unwrap<Promise<string>>; // string
type NumberResult = Unwrap<number>; // number (not wrapped)

Q7. What is the satisfies operator (TypeScript 4.9+)?

satisfies validates that a value matches a type without widening the inferred type.

const palette = {
  red: [255, 0, 0],
  green: '#00ff00',
  blue: [0, 0, 255],
} satisfies Record<string, string | number[]>;

// Without satisfies, TypeScript would infer string | number[] for all values.
// With satisfies, each value retains its specific type:
const r = palette.red;   // number[] — not string | number[]
const g = palette.green; // string — not string | number[]

// Compare to type annotation which widens the type:
const palette2: Record<string, string | number[]> = { red: [255, 0, 0] };
const r2 = palette2.red; // string | number[] — loses specific type info

Q8. What are template literal types?

TypeScript 4.1+ supports template literal types for powerful string manipulation.

type Direction = 'top' | 'right' | 'bottom' | 'left';
type CSSProperty = `margin-${Direction}` | `padding-${Direction}`;
// 'margin-top' | 'margin-right' | 'margin-bottom' | 'margin-left' | 'padding-...'

// Extract route params
type ExtractParams<Route extends string> =
  Route extends `${string}:${infer Param}/${infer Rest}`
    ? Param | ExtractParams<`/${Rest}`>
    : Route extends `${string}:${infer Param}`
    ? Param
    : never;

type RouteParams = ExtractParams<'/users/:userId/posts/:postId'>;
// 'userId' | 'postId'

// Event handler naming
type EventName = 'click' | 'focus' | 'blur';
type HandlerName = `on${Capitalize<EventName>}`;
// 'onClick' | 'onFocus' | 'onBlur'

// API endpoint builder
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
type ApiEndpoint = `${Lowercase<HttpMethod>}${Capitalize<string>}`;

Q9. What are mapped types?

Mapped types transform the properties of an existing type.

// Manual implementation of Partial
type MyPartial<T> = {
  [K in keyof T]?: T[K];
};

// Readonly with nested
type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];
};

// Remapping keys with 'as'
type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};

interface Person { name: string; age: number; }
type PersonGetters = Getters<Person>;
// { getName: () => string; getAge: () => number; }

// Filtering properties by type
type PickByValue<T, V> = {
  [K in keyof T as T[K] extends V ? K : never]: T[K];
};

interface Mixed {
  id: number;
  name: string;
  active: boolean;
  count: number;
}
type StringProps = PickByValue<Mixed, string>; // { name: string }
type NumberProps = PickByValue<Mixed, number>; // { id: number; count: number }

Q10. What are conditional types and infer?

// Basic conditional type
type IsArray<T> = T extends any[] ? true : false;
type A = IsArray<string[]>; // true
type B = IsArray<string>;   // false

// Distributive conditional types
type Flatten<T> = T extends Array<infer Item> ? Item : T;
type Str = Flatten<string[]>;  // string
type Num = Flatten<number>;    // number

// Extract promise value
type AwaitedValue<T> = T extends Promise<infer R>
  ? R extends Promise<any>
    ? AwaitedValue<R>
    : R
  : T;

// infer in function types
type FirstArg<T> = T extends (arg: infer A, ...rest: any[]) => any ? A : never;

function doSomething(id: number, name: string): void {}
type FirstParam = FirstArg<typeof doSomething>; // number

// Conditional type for deep pick
type DeepPartial<T> = T extends object
  ? { [K in keyof T]?: DeepPartial<T[K]> }
  : T;

Q11. What is declaration merging?

TypeScript merges multiple declarations with the same name. Useful for augmenting third-party types.

// Augmenting Express Request type
declare global {
  namespace Express {
    interface Request {
      user?: {
        id: string;
        email: string;
        role: 'admin' | 'user';
      };
    }
  }
}

// Augmenting module types
declare module '*.svg' {
  const content: string;
  export default content;
}

declare module 'some-library' {
  interface SomeInterface {
    newProperty: string; // adds to existing interface
  }
}

Q12. What is the difference between any, unknown, and never?

TypeDescriptionCan assign toRequires narrowing
anyOpt-out of type systemAnythingNo
unknownType-safe unknownOnly unknown/anyYes
neverEmpty type, never occursNothingN/A
// unknown forces you to narrow before use
function processValue(value: unknown) {
  // value.toUpperCase() — compile error!
  if (typeof value === 'string') {
    value.toUpperCase(); // safe
  }
}

// never for exhaustiveness
function assertNever(x: never): never {
  throw new Error('Unexpected value: ' + x);
}

// never in unreachable code
function infiniteLoop(): never {
  while (true) {}
}

Confident on Q1-Q12? You've cleared the TypeScript screening round. The intermediate section covers decorators, module augmentation, and advanced patterns — the topics that separate ₹12 LPA from ₹25 LPA+ offers.

INTERMEDIATE LEVEL — Proven Patterns That Get You Hired (Questions 13–28)

Q13. How do TypeScript decorators work? (TypeScript 5.x standard)

TypeScript 5.0 introduced the new standard ECMAScript decorator proposal, replacing the legacy experimentalDecorators.

// Class decorator
function sealed(target: typeof Base) {
  Object.seal(target);
  Object.seal(target.prototype);
}

// Method decorator
function log(
  target: any,
  context: ClassMethodDecoratorContext
) {
  const methodName = String(context.name);
  return function (this: any, ...args: any[]) {
    console.log(`Calling ${methodName}(${args.join(', ')})`);
    const result = target.call(this, ...args);
    console.log(`${methodName} returned ${result}`);
    return result;
  };
}

class Calculator {
  @log
  add(a: number, b: number) {
    return a + b;
  }
}

// Property decorator for validation
function validate(min: number, max: number) {
  return function(target: any, context: ClassFieldDecoratorContext) {
    return function(this: any, value: number) {
      if (value < min || value > max) {
        throw new RangeError(`${String(context.name)} must be between ${min} and ${max}`);
      }
      return value;
    };
  };
}

Q14. Explain module augmentation and ambient declarations.

// ambient.d.ts — declare types for non-TypeScript files
declare module '*.png' {
  const src: string;
  export default src;
}

declare module '*.css' {
  const styles: { [className: string]: string };
  export default styles;
}

// Global augmentation
declare global {
  interface Window {
    analytics: {
      track(event: string, props?: Record<string, unknown>): void;
    };
  }

  interface Array<T> {
    first(): T | undefined;
    last(): T | undefined;
  }
}

// Module augmentation
import 'express';
declare module 'express-serve-static-core' {
  interface Request {
    requestId: string;
  }
}

Q15. What is the keyof and typeof operator in types?

const config = {
  host: 'localhost',
  port: 3000,
  debug: true,
} as const;

type Config = typeof config;
// { readonly host: "localhost"; readonly port: 3000; readonly debug: true }

type ConfigKey = keyof typeof config; // "host" | "port" | "debug"

// Combine to create safe config getter
function getConfig<K extends keyof typeof config>(key: K): typeof config[K] {
  return config[key];
}

const port = getConfig('port'); // type: 3000 (literal, not number)
// getConfig('timeout') — compile error!

// Indexed access type
interface ApiResponse {
  user: { id: number; name: string };
  posts: { id: number; title: string }[];
}

type User = ApiResponse['user']; // { id: number; name: string }
type Post = ApiResponse['posts'][number]; // { id: number; title: string }

Q16. How do you write type-safe event emitters?

type EventMap = {
  'user:login': { userId: string; timestamp: Date };
  'user:logout': { userId: string };
  'order:created': { orderId: string; amount: number };
  'order:paid': { orderId: string };
};

class TypedEmitter<Events extends Record<string, unknown>> {
  private listeners = new Map<keyof Events, Function[]>();

  on<K extends keyof Events>(
    event: K,
    listener: (data: Events[K]) => void
  ): this {
    const existing = this.listeners.get(event) ?? [];
    this.listeners.set(event, [...existing, listener]);
    return this;
  }

  emit<K extends keyof Events>(event: K, data: Events[K]): void {
    this.listeners.get(event)?.forEach(fn => fn(data));
  }

  off<K extends keyof Events>(event: K, listener: Function): this {
    const existing = this.listeners.get(event) ?? [];
    this.listeners.set(event, existing.filter(fn => fn !== listener));
    return this;
  }
}

const emitter = new TypedEmitter<EventMap>();

emitter.on('user:login', ({ userId, timestamp }) => {
  console.log(`${userId} logged in at ${timestamp}`);
});

// emitter.emit('user:login', { userId: '123' }) — compile error! Missing timestamp
emitter.emit('user:login', { userId: '123', timestamp: new Date() }); // OK

Q17. What are variadic tuple types (TypeScript 4.0+)?

// Prepend/append to tuple
type Prepend<T, Tuple extends unknown[]> = [T, ...Tuple];
type Append<Tuple extends unknown[], T> = [...Tuple, T];

type WithId<T extends unknown[]> = Prepend<string, T>;
type WithCallback<T extends unknown[]> = Append<T, () => void>;

type Args = [number, string];
type WithIdArgs = WithId<Args>; // [string, number, string]

// Spreading tuples in function signatures
function concat<T extends unknown[], U extends unknown[]>(
  arr1: [...T],
  arr2: [...U]
): [...T, ...U] {
  return [...arr1, ...arr2];
}

const result = concat([1, 2], ['a', 'b']); // [number, number, string, string]

// Rest elements in middle of tuple
type MiddleRest = [string, ...number[], boolean];
const valid: MiddleRest = ['hello', 1, 2, 3, true];

Q18. How do you implement a type-safe API client?

// Define your API schema as types
interface ApiEndpoints {
  'GET /users': {
    query: { page?: number; limit?: number };
    response: { users: User[]; total: number };
  };
  'GET /users/:id': {
    params: { id: string };
    response: User;
  };
  'POST /users': {
    body: { name: string; email: string };
    response: User;
  };
  'PUT /users/:id': {
    params: { id: string };
    body: Partial<Pick<User, 'name' | 'email'>>;
    response: User;
  };
}

type Method = 'GET' | 'POST' | 'PUT' | 'DELETE';
type EndpointKey = keyof ApiEndpoints;

async function apiCall<K extends EndpointKey>(
  endpoint: K,
  options: Omit<ApiEndpoints[K], 'response'>
): Promise<ApiEndpoints[K]['response']> {
  const [method, path] = (endpoint as string).split(' ');
  // resolve params in path, attach query/body...
  const response = await fetch(path, {
    method,
    body: 'body' in options ? JSON.stringify(options.body) : undefined,
  });
  return response.json();
}

Q19. What is const type parameter (TypeScript 5.0)?

// Without const — infers mutable type
function inferType<T>(value: T): T {
  return value;
}
const arr = inferType([1, 2, 3]); // type: number[]

// With const — infers readonly literal type
function inferConst<const T>(value: T): T {
  return value;
}
const arr2 = inferConst([1, 2, 3]); // type: readonly [1, 2, 3]
const obj = inferConst({ x: 10, y: 20 }); // type: { readonly x: 10; readonly y: 20 }

// Useful for typed routes or configs passed to functions
function createRouter<const Routes extends readonly string[]>(routes: Routes) {
  return routes;
}
const routes = createRouter(['/home', '/about', '/contact'] as const);
type Route = typeof routes[number]; // '/home' | '/about' | '/contact'

Q20. How do you type React components in TypeScript?

import React from 'react';

// Props with children
interface CardProps {
  title: string;
  variant?: 'primary' | 'secondary';
  onClick?: (event: React.MouseEvent<HTMLDivElement>) => void;
  children: React.ReactNode;
}

const Card: React.FC<CardProps> = ({ title, variant = 'primary', onClick, children }) => (
  <div className={`card card--${variant}`} onClick={onClick}>
    <h2>{title}</h2>
    {children}
  </div>
);

// Generic component
interface ListProps<T> {
  items: T[];
  renderItem: (item: T, index: number) => React.ReactNode;
  keyExtractor: (item: T) => string;
}

function List<T>({ items, renderItem, keyExtractor }: ListProps<T>) {
  return (
    <ul>
      {items.map((item, index) => (
        <li key={keyExtractor(item)}>{renderItem(item, index)}</li>
      ))}
    </ul>
  );
}

// forwardRef with TypeScript
const Input = React.forwardRef<HTMLInputElement, React.InputHTMLAttributes<HTMLInputElement>>(
  (props, ref) => <input ref={ref} {...props} />
);

// useRef with proper typing
const ref = React.useRef<HTMLDivElement>(null);
// ref.current is HTMLDivElement | null

const mutableRef = React.useRef<number>(0);
// mutableRef.current is number (not null)

Q21. What are TypeScript project references?

Project references allow splitting a large TypeScript project into smaller composable pieces with independent compilation.

// tsconfig.json (root)
{
  "files": [],
  "references": [
    { "path": "./packages/core" },
    { "path": "./packages/utils" },
    { "path": "./packages/ui" }
  ]
}

// packages/ui/tsconfig.json
{
  "compilerOptions": {
    "composite": true,
    "declaration": true,
    "outDir": "dist"
  },
  "references": [
    { "path": "../core" },
    { "path": "../utils" }
  ]
}

Build with tsc --build — only recompiles changed packages.


Q22–Q28: Quick Intermediate Round

Q22. What is as const? Makes an expression deeply readonly with literal types inferred. ['north', 'south'] as const gives type readonly ['north', 'south'] instead of string[].

Q23. Explain the Awaited<T> utility type. Recursively unwraps the type of a Promise. Awaited<Promise<Promise<string>>> gives string. Added in TypeScript 4.5.

Q24. What is structural vs nominal typing? TypeScript uses structural typing — two types are compatible if they have the same shape, regardless of name. Most other typed languages use nominal typing (same name required). You can simulate nominal typing with branded types.

Q25. What are branded/nominal types?

type UserId = string & { readonly __brand: unique symbol };
type OrderId = string & { readonly __brand: unique symbol };
// Now UserId and OrderId are not assignable to each other
function createUserId(id: string): UserId { return id as UserId; }

Q26. What is the difference between export type and export? export type is a type-only export that is erased at compile time. Helps bundlers and type-strippers know they can safely remove the export without runtime impact.

Q27. What is noUncheckedIndexedAccess? A compiler option that adds undefined to the type of any indexed access. arr[0] becomes T | undefined instead of T. Prevents many off-by-one errors.

Q28. How do you type async generators?

async function* paginate<T>(
  fetchPage: (page: number) => Promise<T[]>
): AsyncGenerator<T, void, unknown> {
  let page = 1;
  while (true) {
    const items = await fetchPage(page++);
    if (items.length === 0) break;
    yield* items;
  }
}

Nailed Q1-Q28? You're already ahead of 80% of TypeScript candidates. The advanced section covers recursive types, type-safe builders, and real-world patterns — this is where ₹35 LPA+ offers are decided.

ADVANCED LEVEL — The Type Wizard Round (Questions 29–40)

Q29. How do you implement a recursive type-safe deep merge?

type DeepMerge<T, U> = {
  [K in keyof T | keyof U]:
    K extends keyof U
      ? K extends keyof T
        ? T[K] extends object
          ? U[K] extends object
            ? DeepMerge<T[K], U[K]>
            : U[K]
          : U[K]
        : U[K]
      : K extends keyof T
      ? T[K]
      : never;
};

type A = { x: { a: number; b: string }; z: boolean };
type B = { x: { a: string; c: number }; y: string };
type Merged = DeepMerge<A, B>;
// { x: { a: string; b: string; c: number }; z: boolean; y: string }

Q30. How do you build a type-safe SQL query builder?

interface Tables {
  users: { id: number; name: string; email: string; role: string };
  posts: { id: number; title: string; userId: number; published: boolean };
}

type TableName = keyof Tables;
type TableRow<T extends TableName> = Tables[T];
type ColumnName<T extends TableName> = keyof TableRow<T>;

class QueryBuilder<T extends TableName> {
  private tableName: T;
  private selectedColumns: ColumnName<T>[] = [];
  private whereClause: Partial<TableRow<T>> = {};

  constructor(table: T) {
    this.tableName = table;
  }

  select<K extends ColumnName<T>>(...cols: K[]): QueryBuilder<T> {
    this.selectedColumns = cols;
    return this;
  }

  where(conditions: Partial<TableRow<T>>): this {
    this.whereClause = conditions;
    return this;
  }

  build(): string {
    const cols = this.selectedColumns.length
      ? this.selectedColumns.join(', ')
      : '*';
    const where = Object.entries(this.whereClause)
      .map(([k, v]) => `${k} = ${JSON.stringify(v)}`)
      .join(' AND ');
    return `SELECT ${cols} FROM ${this.tableName}${where ? ` WHERE ${where}` : ''}`;
  }
}

const query = new QueryBuilder('users')
  .select('id', 'name', 'email')
  .where({ role: 'admin' })
  .build();
// SELECT id, name, email FROM users WHERE role = "admin"

Q31. Real-World Scenario: Type-safe Redux with TypeScript

// Define action creators with discriminated union
const increment = (amount: number) =>
  ({ type: 'counter/increment', payload: amount } as const);

const setUser = (user: User) =>
  ({ type: 'user/set', payload: user } as const);

type Action =
  | ReturnType<typeof increment>
  | ReturnType<typeof setUser>;

interface AppState {
  counter: number;
  user: User | null;
}

function reducer(state: AppState, action: Action): AppState {
  switch (action.type) {
    case 'counter/increment':
      return { ...state, counter: state.counter + action.payload };
    case 'user/set':
      return { ...state, user: action.payload };
    // TypeScript will error if a case is missing (with noImplicitReturns)
  }
}

Q32. What is the infer keyword used for in advanced patterns?

// Extract return type of method from class
type MethodReturn<T, M extends keyof T> =
  T[M] extends (...args: any[]) => infer R ? R : never;

class ApiService {
  fetchUser(): Promise<User> { return fetch('/user').then(r => r.json()); }
  fetchPosts(): Promise<Post[]> { return fetch('/posts').then(r => r.json()); }
}

type UserResult = MethodReturn<ApiService, 'fetchUser'>; // Promise<User>

// Unpack nested generic
type UnpackArray<T> = T extends (infer U)[] ? U : T;
type UnpackPromise<T> = T extends Promise<infer U> ? U : T;
type UnpackBoth<T> = UnpackPromise<UnpackArray<T>>;

type Example = UnpackBoth<Promise<string>[]>; // string

Q33–Q40: Advanced Quick Fire

Q33. What is the NoInfer utility type (TypeScript 5.4)? Prevents TypeScript from inferring a type parameter from a specific argument, forcing the developer to provide it explicitly or from another source.

Q34. How does TypeScript handle circular references in types? TypeScript allows recursive type aliases with some limitations. Recursive interface references are always valid. Recursive type aliases need a structural level of indirection (usually an object or array) to avoid infinite expansion.

Q35. What is PropertyKey? A built-in type alias for string | number | symbol — valid keys for object properties.

Q36. What are opaque types / branded types used for in real applications? Password hashing: HashedPassword vs PlainPassword. Currency: USD vs EUR. Primary keys: UserId vs OrderId. Prevents accidentally passing raw IDs where validated ones are expected.

Q37. Explain TypeScript module resolution strategies. node16/nodenext (recommended for ESM): requires explicit .js extensions in imports. bundler: for Vite/webpack users — no extension requirements. classic: legacy, avoid. Configure with moduleResolution in tsconfig.

Q38. What are the performance implications of complex TypeScript types? Deep recursive types, excessively distributed conditional types, and large union types can dramatically slow TypeScript's language server. Strategies: simplify unions, use interface extends instead of & intersections, pre-compute reused complex types.

Q39. What is the difference between override and regular method overriding? The override keyword (TypeScript 4.3+) ensures you can only override methods that exist in the base class. Without it, a typo in the method name creates a new method silently. Enable with noImplicitOverride: true.

Q40. Real-World Scenario: Type-safe feature flags

type FeatureFlags = {
  'dark-mode': boolean;
  'new-checkout': boolean;
  'ai-recommendations': { enabled: boolean; model: string };
};

function isFeatureEnabled<K extends keyof FeatureFlags>(
  flags: FeatureFlags,
  feature: K
): FeatureFlags[K] {
  return flags[feature];
}

Common TypeScript Mistakes — Avoid These and Stand Out

  1. Using any excessively — defeats the purpose; use unknown for truly unknown values
  2. Ignoring strictNullChecks — always enable strict: true in tsconfig
  3. Using ! non-null assertion carelessly — it suppresses errors at compile time, crashes at runtime
  4. interface vs type confusion — use interface for extensible shapes, type for unions
  5. Not using discriminated unions — leads to complex conditional logic that TypeScript can't check
  6. Casting with as instead of narrowingas is an assertion that bypasses type safety
  7. Forgetting readonly on function parameters — prevents accidental mutation
  8. Exporting types from barrel files without export type — breaks bundlers and isolatedModules

FAQ — Your TypeScript Career Questions, Answered

Q: Which TypeScript version should I target? TypeScript 5.4+ for new projects. Know the major features from 4.0 onwards as interviewers frequently ask about specific releases.

Q: Is learning TypeScript worth it for a frontend developer? Absolutely. TypeScript is now the default for React/Vue/Angular projects at any serious company. It's expected at mid-level and above.

Q: How strict should tsconfig be? Enable "strict": true always. Optionally add noUncheckedIndexedAccess, noImplicitReturns, exactOptionalPropertyTypes.

Q: What is the fastest way to learn TypeScript generics? Build a type-safe utility library. Try implementing Partial, Pick, Omit, ReturnType from scratch. Then solve TypeScript challenges at type-challenges.github.io.

Q: How do decorators differ between TypeScript 4.x and 5.x? TypeScript 5.0 implements the finalized ECMAScript decorator proposal, which is fundamentally different from the legacy experimentalDecorators. The new decorators don't use emitDecoratorMetadata and have different parameter signatures.

Q: Is TypeScript needed for Node.js backends? Yes, most Node.js projects at product companies use TypeScript. Fastify, NestJS, and tRPC are built with TypeScript first.

Q: What is tRPC and why does TypeScript matter for it? tRPC enables end-to-end type safety between your backend API and frontend without code generation. TypeScript generics power the type inference that makes this possible.

Q: How do you handle TypeScript in a monorepo? Use project references with composite: true, path aliases in tsconfig, and a shared tsconfig.base.json. Tools: Turborepo, Nx, or pnpm workspaces.


Practice Resources: TypeScript Playground (typescriptlang.org/play), Type Challenges (github.com/type-challenges), Official Docs Handbook.

TypeScript mastery is one of the highest-ROI skills you can build. It makes you better at React, Node.js, and even Go/Rust (type thinking transfers). Bookmark this, solve type challenges daily, and watch your interview confidence skyrocket.

Related Articles:

Advertisement Placement

Explore this topic cluster

More resources in Interview Questions

Use the category hub to browse similar questions, exam patterns, salary guides, and preparation resources related to this topic.

Related Articles

More from PapersAdda

Share this guide: