Skip to content
SDETRole
SDETRole

TypeScript interview question

Explain any, unknown and never. When should you use each?

intermediate 2–5 yrs #any#unknown#never#type-narrowing#exhaustiveness

Short answer

any switches off type checking and spreads silently. unknown accepts any value but forces you to narrow it before use, which makes it the safe type for JSON, caught errors and external input. never is the type with no values, used for impossible states and exhaustive checks on unions.

Answer

any and unknown are both “top” types that accept every value, but they behave in opposite ways. With any, the compiler lets you do anything with the value, so errors appear at runtime. With unknown, the compiler lets you do nothing until you prove what the value is. never is the bottom type: no value has type never, which makes it useful for marking impossible code paths.

any: an escape hatch that spreads

const data: any = JSON.parse('{"user": null}');
data.user.name.toUpperCase(); // compiles, crashes at runtime
const name: string = data.whatever; // any flows into typed code silently

any is contagious: anything derived from it is also unchecked. Legitimate uses are rare: migrating JavaScript gradually, or working around a broken third-party type. Prefer a narrow cast with a comment, and enable @typescript-eslint/no-explicit-any and noImplicitAny (included in strict).

unknown: accept anything, verify before use

function getErrorMessage(err: unknown): string {
  if (err instanceof Error) return err.message;
  if (typeof err === 'string') return err;
  return JSON.stringify(err);
}

try {
  await page.getByRole('button', { name: 'Pay' }).click();
} catch (err) {
  // With "strict" (TS 4.4+), err is unknown via useUnknownInCatchVariables
  console.error(getErrorMessage(err));
}

Use unknown at trust boundaries: parsed JSON, API responses, catch variables, message events, environment input. Narrow it with typeof, instanceof, in, discriminant checks, user-defined type guards, or a schema validator.

function isUser(v: unknown): v is { id: string; email: string } {
  return typeof v === 'object' && v !== null
    && 'id' in v && typeof v.id === 'string'
    && 'email' in v && typeof v.email === 'string';
}

never: impossible states and exhaustiveness

Functions that always throw or never return have return type never. More usefully, never catches unhandled union members at compile time:

type Browser = 'chromium' | 'firefox' | 'webkit';

function launchArgs(b: Browser): string[] {
  switch (b) {
    case 'chromium': return ['--disable-gpu'];
    case 'firefox':  return [];
    case 'webkit':   return [];
    default: {
      const unreachable: never = b; // error if a new Browser is added and not handled
      throw new Error(`Unhandled browser: ${unreachable}`);
    }
  }
}

If someone adds 'msedge' to Browser, this file stops compiling until it is handled.

never also appears in conditional types to filter members (Exclude<T, U> returns never for removed members) and as the result of impossible intersections such as string & number.

Quick comparison

Assign anything to it Use it without checks Typical use
any yes yes migration, last resort
unknown yes no untrusted input
never no (unreachable) exhaustiveness, impossible branches

What interviewers look for

  • Clear contrast: any disables checking, unknown forces narrowing.
  • Concrete unknown uses: JSON, catch, external input.
  • An exhaustive switch with never.
  • Tooling discipline: strict, no-explicit-any.

A strong answer covers

  • any disables type checking and is contagious
  • unknown is type-safe: must narrow before use
  • Uses unknown for external data and caught errors
  • never represents impossible values and enables exhaustive checks
  • Mentions lint or compiler settings to limit any
Practise answering under time pressure

Type to search across SDETRole.

Full-text search by Pagefind · runs in your browser Open search page →