TypeScript Interview Questions and Answers

TypeScript interview questions in 2026 go past "what is a type?" Hiring teams want you to explain unknown vs any, write generic helpers, use satisfies to validate a value without replacing its inferred type, design discriminated unions, and know what TypeScript does not guarantee at runtime. Interview questions on TypeScript appear in frontend, full-stack, Node.js, and Angular loops—often before framework depth because types are the contract layer for APIs and UI state.

Below are 40+ TypeScript interview questions with elaborate answers; technical sections include a strong answer sample you can say aloud. Pair this guide with CSS interview questions for HTML, cascade, and layout fundamentals, front end developer interviews for HTML, CSS, and browser fundamentals, React interview questions and React JS experienced scenarios for component typing, Angular developer interviews for DI and RxJS with TypeScript, Node.js developer interviews for server-side typing, and full stack developer interviews for shared API contracts.

NOTE
Prep target: Enable strict in a toy project, practice narrowing from unknown, implement Pick/Partial on paper, and explain when runtime validation (Zod) is still required.

Interview context and how to prepare

What TypeScript interviews test

TypeScript interviews test whether you can design type-safe APIs and narrow unknown data—not recite every utility type name.

Layer What interviewers probe
Fundamentals Primitives, unions, interfaces vs types
Safety unknown, narrowing, strict mode
Generics Constraints, inference, reusable helpers
Utility types Partial, Pick, ReturnType, mapped types
Patterns Discriminated unions, type guards, satisfies
Tooling tsconfig, declarations, module resolution
Reality Compile-time vs runtime; validation at boundaries
Role Emphasis
Frontend React props, event handlers, form models
Full stack Shared DTOs, API parsing, error unions
Senior Conditional types, library typings, migration strategy

Typical TypeScript interview loop

Round Duration Focus
Screening 30 min Experience, strict mode, stack
JS/TS fundamentals 45 min Types, narrowing, async
TypeScript depth 45–60 min Generics, utilities, satisfies
Framework 45–60 min React/Angular/Node with TS
Live exercise 30–45 min Type an API client or form model
System (senior) 45 min Shared types monorepo, migration

Expect generics, utility types, and discriminated unions in most TypeScript screens—gaps there show up quickly in API and UI state modeling.

Three- to five-week preparation plan

Week Focus Output
1 Primitives, unions, interfaces, strict flags Enable strict on small project
2 Narrowing, guards, discriminated unions parseUser(input: unknown)
3 Generics + utility types Hand-write Pick, Partial
4 satisfies, mapped/conditional types (read) Config object with literals preserved
5 Framework + mock Type React props or Express handler

Build a typed API client with success/error union—reusable story in interviews.

TypeScript vs JavaScript — why use TypeScript?

What interviewers are testing: whether you justify TypeScript for large teams and API boundaries where compile-time checks catch regressions before deploy.

Aspect JavaScript TypeScript
Types Dynamic only Static checking at compile time
Errors Often runtime Many caught before deploy
IDE Good Rich autocomplete, refactor
Output Runs directly Compiles/transpiles to JS
Cost Lower setup Config, build step, learning curve

TypeScript is JavaScript with optional static types—it erases to JS; no runtime type enforcement unless you add validators. A strong answer is:

"TypeScript catches type errors at compile time, improves refactoring and IDE support, and pays for itself on larger teams—while still compiling to plain JavaScript."


TypeScript fundamentals

What is TypeScript?

What interviewers are testing: whether you define typescript accurately and tie it to a real workflow—not acronym trivia.

TypeScript builds on JavaScript by adding static type syntax and type checking while preserving JavaScript runtime semantics. Source .ts/.tsx files are type-checked then compiled (or stripped via transpilers) to JavaScript for execution in browsers or Node.js.

Key idea: structural typing—shapes matter, not nominal class names.

A strong answer is:

TypeScript builds on JavaScript with static type syntax and checking; types erase at compile time, so I still validate runtime boundaries.

How does TypeScript compile to JavaScript?

What interviewers are testing: Whether you understand the difference between type checking and JavaScript transformation/emission, including why Babel/esbuild/SWC may still require a separate type-check step.

Pipeline:

  1. Lexer/parser builds AST from .ts
  2. Type checker validates types (can run via tsc --noEmit)
  3. Emitter outputs .js (target ES version per tsconfig)

Tools: tsc, esbuild, swc, Babel (type strip only with @babel/preset-typescript—no type checking unless separate tsc).

A strong answer is:

tsc type-checks then emits JS to the target—I run typecheck in CI even when bundlers strip types faster, so errors don't slip through.

type vs interface — when do you use each?

What interviewers are testing: whether you choose interface for declaration merging and extension versus type for unions, tuples, and mapped types.

interface type
Extends extends keyword Intersection &
Declaration merge Yes (same name merges) No
Unions/tuples No Yes
Mapped types No Yes

Convention: interface for object shapes that may be extended; type for unions, tuples, utilities.

A strong answer is:

interface for public object contracts and declaration merging in libraries; type for unions, discriminated results, and mapped utility aliases.

any vs unknown vs never?

What interviewers are testing: whether you prefer unknown over any at boundaries and use never for exhaustiveness and unreachable code.

Type Meaning Safe?
any Opt out of checking No—avoid in app code
unknown Something—we must narrow before use Yes entry for external data
never No possible value Unreachable code, empty union
typescript
function parseJson(raw: string): unknown {
  return JSON.parse(raw);
}

Use unknown at API boundaries; never for exhaustive switch checks.

A strong answer is:

unknown for JSON and user input until narrowed; any only in rare interop escapes; never marks impossible branches and powers exhaustiveness checking.

Union and intersection types?

What interviewers are testing: Whether you can model alternatives with unions, combine requirements with intersections, and recognize when incompatible intersections collapse properties to impossible types.

Union (|) — value is one of several types:

typescript
type Status = "idle" | "loading" | "error";
type Id = string | number;

Intersection (&) — value must satisfy all types:

typescript
type Named = { name: string };
type Aged = { age: number };
type Person = Named & Aged;

Unions need narrowing; intersections combine capabilities.

A strong answer is:

Unions model alternatives—I narrow before use; intersections combine requirements, common for mixing mixin-like shapes.

Literal types and as const?

What interviewers are testing: Whether you understand literal widening and when as const is useful for immutable configuration, tuples, and derived unions.

Literal types are exact values ("GET", 42, true).

as const freezes inference to narrow literals:

typescript
const routes = ["home", "settings"] as const;
type Route = (typeof routes)[number]; // "home" | "settings"

Without as const, routes becomes string[].

A strong answer is:

as const preserves literal unions for config and route maps—without it TypeScript widens to string and I lose autocomplete precision.

Optional and readonly modifiers?

What interviewers are testing: Whether you distinguish absence, undefined, and compile-time readonly semantics without assuming either creates runtime validation or deep immutability.

typescript
interface User {
  readonly id: string;
  name: string;
  email?: string;
}
Modifier Effect
? Property may be absent; reading it generally produces `T
readonly Cannot assign after creation

Readonly<T> utility makes all properties readonly shallowly.

A strong answer is:

Optional means a property may be absent; readonly is compile-time only and shallow—I don't confuse either with runtime validation or deep immutability.

Enums vs string union literals?

What interviewers are testing: whether you articulate when to choose enums over string union literals with a concrete production example.

Enum (numeric or string):

typescript
enum Role { Admin = "ADMIN", User = "USER" }

Union literals (preferred in many codebases):

typescript
type Role = "ADMIN" | "USER";

String unions are erased entirely at compile time and align naturally with JSON APIs. Const enums have caveats with bundlers.

Enums are not deprecated; they are runtime-emitting TypeScript syntax rather than purely erasable type syntax.

A strong answer is:

I generally prefer string unions for type-only application domains because they emit no runtime object and align naturally with JSON. I use enums when I genuinely need the runtime enum object, compatibility with an existing API/codebase, or a framework/library convention.

What is structural typing (duck typing)?

What interviewers are testing: whether you define structural typing (duck typing) accurately and tie it to a real workflow—not acronym trivia.

TypeScript compares types by shape, not declaration name:

typescript
type Point = { x: number; y: number };
function draw(p: { x: number; y: number }) { /* ... */ }
const pt: Point = { x: 1, y: 2 };
draw(pt); // OK — structure matches

Contrast with nominal typing (Java classes)—structural assignability normally allows extra properties, but fresh object literals receive additional excess-property checks in contexts such as assignment or function arguments:

typescript
type Point = { x: number; y: number };
function draw(p: Point) {}

draw({
  x: 1,
  y: 2,
  color: "red", // excess-property check
});

A strong answer is:

TypeScript cares if the shape fits—not the alias name—which is why fresh object literals get excess-property checks in assignment and argument positions, even though structural assignability is otherwise permissive.

What is type inference?

What interviewers are testing: whether you define type inference accurately and tie it to a real workflow—not acronym trivia.

The compiler infers types when you omit annotations:

typescript
const n = 42;        // number
const arr = [1, 2];  // number[]
function id<T>(x: T) { return x; } // T inferred from argument

Annotate public APIs; let inference handle locals when clear.

A strong answer is:

I lean on inference inside functions but annotate exported boundaries and function parameters where inference would be too wide or unclear.

What does strict mode in tsconfig include?

What interviewers are testing: Whether you know what strict: true enables, that it defaults to true in TypeScript 7, and which additional strictness flags are separate from the strict family.

"strict": true enables a bundle of strict-family flags. In TypeScript 7, strict now defaults to true, following the TS6 default changes.

Flag Effect
strictNullChecks null/undefined distinct
noImplicitAny Error on implicit any
strictFunctionTypes Safer function parameter checking
strictBindCallApply Typed bind/call/apply
strictPropertyInitialization Class fields must be initialized
noImplicitThis this must have an explicit type
useUnknownInCatchVariables catch variables are unknown
strictBuiltinIteratorReturn Stricter iterator return typing

noUncheckedIndexedAccess and exactOptionalPropertyTypes are useful additional strictness options, but they are not simply synonymous with "strict": true. In particular, exactOptionalPropertyTypes is separate from the strict family.

A strong answer is:

strict with null checks is non-negotiable for me—it forces handling undefined from APIs and optional fields instead of surprise runtime nulls. On TS7 I also verify additional strict options such as noUncheckedIndexedAccess when the team wants them.


Narrowing, type guards, and safety

What is type narrowing?

What interviewers are testing: whether you define type narrowing accurately and tie it to a real workflow—not acronym trivia.

Narrowing refines a broad type to a specific one in a branch:

typescript
function printId(id: string | number) {
  if (typeof id === "string") {
    console.log(id.toUpperCase());
  } else {
    console.log(id.toFixed(0));
  }
}

Built-in narrowing: typeof, instanceof, in, truthiness, equality.

A strong answer is:

Narrowing is how I use unions safely—after typeof or a custom guard, the compiler knows which operations are legal.

typeof and instanceof guards?

What interviewers are testing: whether you answer typeof and instanceof guards with specific, production-grounded detail—not generic recall.

typescript
if (typeof value === "string") { /* string */ }
if (value instanceof Date) { /* Date */ }
if (value instanceof Error) { /* Error */ }

typeof null is "object" (JavaScript quirk)—use explicit null checks.

A strong answer is:

typeof for primitives; instanceof for class instances—I remember typeof null is object and pair with value !== null when needed.

What is a user-defined type guard?

What interviewers are testing: Whether you can safely narrow unknown with a runtime check and a type predicate rather than asserting untrusted input.

A function returning param is Type tells TypeScript to narrow after true:

typescript
type User = {
  id: number;
  name: string;
};

function isUser(value: unknown): value is User {
  if (typeof value !== "object" || value === null) {
    return false;
  }

  return (
    "id" in value &&
    typeof value.id === "number" &&
    "name" in value &&
    typeof value.name === "string"
  );
}

function greet(input: unknown) {
  if (!isUser(input)) {
    return "unknown";
  }

  return input.name;
}

A hand-written guard is only as trustworthy as its runtime checks. For complex untrusted payloads, a schema validator is usually easier to maintain.

A strong answer is:

User-defined guards bundle runtime validation with a type predicate—I use them at JSON boundaries instead of casting with as, and I reach for schema validation when the payload is complex or untrusted.

What are discriminated unions?

What interviewers are testing: whether you model tagged unions so switch statements narrow safely and fail exhaustively.

Objects share a literal discriminant field for safe switching:

typescript
type Result =
  | { status: "ok"; data: User }
  | { status: "error"; message: string };

function handle(r: Result) {
  switch (r.status) {
    case "ok": return r.data.name;
    case "error": return r.message;
  }
}

Makes illegal states unrepresentable—no data on error branch.

A strong answer is:

Discriminated unions model API results and UI state—I switch on status and the compiler enforces correct fields per branch.

What are assertion functions (asserts)?

What interviewers are testing: Whether you know assertion functions narrow control flow by throwing when an invariant fails.

typescript
function assertIsString(x: unknown): asserts x is string {
  if (typeof x !== "string") throw new Error("not string");
}

Narrows for all code after the call if it doesn't throw—stricter than boolean guard.

A strong answer is:

asserts functions throw on failure and narrow the rest of the scope—useful in test setup and invariant checks.

as vs satisfies — what is the difference?

What interviewers are testing: Whether you understand that satisfies validates compatibility without replacing the expression's inferred type, whereas as asserts a type and can suppress useful checking.

as satisfies
Role Assertion (override compiler) Validate shape without replacing inferred type
Safety Can lie to compiler Safer for config maps
Inference Widens to asserted type Retains the expression's more specific inferred information
typescript
const palette = {
  primary: "#3178c6",
  danger: "#ef4444",
} satisfies Record<string, string>;
// palette.primary is inferred as string; use `as const` when you need exact literal preservation

A strong answer is:

satisfies checks that an expression conforms to a type while retaining its own inferred shape. as is an assertion and can tell the compiler something that runtime data doesn't guarantee.

What is the non-null assertion operator (!)?

What interviewers are testing: whether you define the non-null assertion operator (!) accurately and tie it to a real workflow—not acronym trivia.

Postfix ! tells compiler "this isn't null/undefined":

typescript
const el = document.getElementById("root")!;

Convenient but unsafe if DOM missing—prefer explicit checks in production code.

A strong answer is:

I rarely use ! in app code—explicit null checks or early return communicate the invariant to humans and the compiler.

What is exhaustiveness checking?

What interviewers are testing: whether you define exhaustiveness checking accurately and tie it to a real workflow—not acronym trivia.

typescript
function assertNever(x: never): never {
  throw new Error("unexpected: " + x);
}

function icon(shape: "circle" | "square") {
  switch (shape) {
    case "circle": return "○";
    case "square": return "□";
    default: return assertNever(shape);
  }
}

If you add "triangle" without a case, shape in default is not never—compile error.

A strong answer is:

assertNever in default catches unhandled union members when the domain grows—cheap insurance on state machines.


Generics and utility types

What are generics?

What interviewers are testing: Whether you preserve relationships between input and output types rather than replacing them with any.

Generics are type parameters for reusable, type-safe code:

typescript
function identity<T>(value: T): T {
  return value;
}
const n = identity(42); // number

Without generics, you'd use any and lose safety.

A strong answer is:

Generics preserve the relationship between input and output types—identity, map, Promise.then all depend on that preservation.

What are generic constraints?

What interviewers are testing: Whether you can constrain generic parameters enough to perform safe operations without destroying useful inference.

T extends U limits type parameter:

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

Enables safe access while staying generic.

A strong answer is:

extends keyof T lets me write one getProperty that type-checks keys at compile time without destroying useful inference.

keyof and indexed access types?

What interviewers are testing: whether you answer keyof and indexed access types with specific, production-grounded detail—not generic recall.

typescript
interface User { id: string; email: string; }
type UserKeys = keyof User;           // "id" | "email"
type Email = User["email"];           // string
type PartialUser = { [K in keyof User]?: User[K] };

Foundation for mapped types and utilities.

A strong answer is:

keyof and indexed access let me derive keys and property types from existing interfaces—base for Pick and Partial implementations.

Explain Partial, Pick, and Omit.

What interviewers are testing: whether you can teach partial, pick, and omit. clearly enough that a junior could follow your explanation.

Utility Effect
Partial<T> All properties optional
Pick<T, K> Subset of keys
Omit<T, K> Remove keys
typescript
type UserPatch = Partial<Pick<User, "name" | "email">>;
type PublicUser = Omit<User, "passwordHash">;

Partial is shallow—nested objects need custom deep partial types deliberately.

A strong answer is:

Pick and Omit shape API DTOs; Partial for PATCH updates—I remember Partial doesn't recurse into nested objects unless I design for that.

ReturnType, Parameters, and Awaited?

What interviewers are testing: whether you answer returntype, parameters, and awaited with specific, production-grounded detail—not generic recall.

typescript
async function fetchUser() {
  return { id: 1, name: "Ada" };
}
type R = Awaited<ReturnType<typeof fetchUser>>; // { id: number; name: string }
type P = Parameters<typeof fetchUser>;         // []

Extract types from existing functions without duplication—great for wrappers and mocks.

A strong answer is:

ReturnType and Awaited unwrap function and Promise types for mocks and middleware—I derive from typeof fn instead of duplicating interfaces.

Record and Readonly utility types?

What interviewers are testing: whether you answer record and readonly utility types with specific, production-grounded detail—not generic recall.

typescript
type Role = "admin" | "user";
type Permissions = Record<Role, string[]>;

type FrozenUser = Readonly<User>;

Record builds object types with known keys; Readonly prevents assignment to top-level properties.

A strong answer is:

Record types dictionaries keyed by union; Readonly guards immutable snapshots—I know Readonly is shallow like Partial.

What are mapped types?

What interviewers are testing: whether you define mapped types accurately and tie it to a real workflow—not acronym trivia.

Transform properties by iterating keys:

typescript
type Flags<T> = { [K in keyof T]: boolean };
type Nullable<T> = { [K in keyof T]: T[K] | null };

Built-in utilities are implemented with mapped types + modifiers (?, readonly).

A strong answer is:

Mapped types are the meta-layer behind Partial and Pick—[K in keyof T] with optional readonly modifiers.

What are conditional types?

What interviewers are testing: Whether you understand T extends U ? X : Y, distributive behavior over unions, and the role of infer in reusable library types.

typescript
type IsString<T> = T extends string ? true : false;
type Flatten<T> = T extends Array<infer U> ? U : T;

infer extracts type inside conditional—used in advanced library typings.

Senior interviews may ask conceptually; daily app code uses them via utilities.

A strong answer is:

Conditional types pick types based on extends checks—infer lets libraries extract Promise resolve types inside Awaited.

Template literal types?

What interviewers are testing: whether you answer template literal types with specific, production-grounded detail—not generic recall.

typescript
type EventName = "click" | "focus";
type HandlerName = `on${Capitalize<EventName>}`; // "onClick" | "onFocus"

Combine string literals at type level—CSS keys, event names, route builders.

A strong answer is:

Template literal types generate string unions from other unions—handy for typed event maps and design tokens.

How would you implement Pick from scratch?

What interviewers are testing: whether you can explain how to implement pick from scratch with the right steps, tools, and common failure modes.

typescript
type MyPick<T, K extends keyof T> = {
  [P in K]: T[P];
};

Same pattern as built-in Pick—interview checks understanding of keyof + mapped types.

A strong answer is:

Pick maps each selected key to its original property type—if I can write that, I understand most utility type machinery.


Tooling, frameworks, and scenarios

Important tsconfig.json options for interviews?

What interviewers are testing: Whether you can align compiler configuration with the runtime/bundler and explain strictness rather than memorizing flags.

Option Purpose
strict Enable strict family; defaults to true in TypeScript 7
module Module emit/interpretation; modern projects commonly use esnext, preserve, or Node modes as appropriate
moduleResolution Usually bundler for bundler-based apps or nodenext for modern Node ESM/CJS interoperability
types Defaults to [] in TS7; projects relying on global packages such as Node/Jest may need to list them explicitly
paths Alias imports (@/components)
declaration Emit .d.ts for libraries
skipLibCheck Faster builds; skip .d.ts check
noUncheckedIndexedAccess Safer array/index access (not part of strict: true)
exactOptionalPropertyTypes Distinguish missing property from { property: undefined } (not part of strict: true)

TypeScript 7 removed old node/node10 module resolution.

A strong answer is:

strict plus moduleResolution matching the runtime/bundler—I use bundler or nodenext as appropriate, list global types explicitly when needed, and enable noUncheckedIndexedAccess or exactOptionalPropertyTypes only when the team wants that extra strictness.

What are .d.ts declaration files?

What interviewers are testing: whether you define .d.ts declaration files accurately and tie it to a real workflow—not acronym trivia.

Declaration files describe types for JavaScript without implementation:

  • .d.ts for libraries consumed by TS
  • declare module for untyped packages
  • DefinitelyTyped (@types/node, @types/react)

Allows type-checking against JS-only code.

A strong answer is:

d.ts files are the contract for JS libraries—I use @types packages or write minimal declarations when a dependency ships without types.

What are TypeScript decorators?

What interviewers are testing: whether you define typescript decorators accurately and tie it to a real workflow—not acronym trivia.

Two decorator systems matter in practice:

System When
Standard decorators TypeScript 5+ supports the standardized decorator proposal
experimentalDecorators Legacy/pre-standard decorator implementation predating standardization

Angular and some existing libraries may have framework/toolchain-specific requirements—verify tsconfig and framework docs before upgrading.

A strong answer is:

Standard decorators are the modern path; experimentalDecorators is the legacy implementation—I know which system our framework and tsconfig target before migrating.

TypeScript with React — what do interviewers ask?

What interviewers are testing: whether you answer typescript with react — what do interviewers ask with specific, production-grounded detail—not generic recall.

Common topics:

  • React.FC<Props> vs explicit props parameter typing
  • Event types — ChangeEvent<HTMLInputElement>
  • useState inference — useState<User | null>(null)
  • Children — ReactNode
  • Generic components — <List<T> items={T[]} />

See React interviews for hooks depth.

A strong answer is:

I usually type the props parameter directly, but React.FC is also valid. What matters more is correctly typing props, children when accepted, refs, events, and generic components.

TypeScript with Node.js — key typing patterns?

What interviewers are testing: whether you answer typescript with node.js — key typing patterns with specific, production-grounded detail—not generic recall.

  • Request / Response types in Express
  • Environment — declare known variable names for ergonomics if useful, but validate required environment variables at startup with runtime checks/schema validation
  • ESM vs CJS — module: "nodenext" / moduleResolution: "nodenext" when modeling Node's ESM/CJS behavior, or bundler-specific settings when a bundler owns module transformation. TypeScript 7 no longer supports old moduleResolution: node / node10.
  • Unknown request body until validated

Pair with Node.js interviews.

A strong answer is:

I type handlers with Request/Response, validate env and body at startup with runtime checks—not declaration merging alone—and align module settings with nodenext or bundler resolution as appropriate.

Why is runtime validation still needed with TypeScript?

What interviewers are testing: Whether you understand the compile-time/runtime boundary and treat network, storage, environment, and user input as untrusted.

Types erase at compile time—APIs, JSON.parse, and localStorage return untrusted shapes.

Libraries: Zod, Valibot, io-ts parse at runtime and infer TS types:

typescript
const UserSchema = z.object({ id: z.string(), name: z.string() });
type User = z.infer<typeof UserSchema>;
const user = UserSchema.parse(unknownInput);

A strong answer is:

TypeScript doesn't validate wire data—I parse unknown through Zod at boundaries and let inferred types flow inward.

Scenario: Type a fetch wrapper for a REST API.

What interviewers are testing: Whether you keep external JSON as unknown, validate before producing T, and expose success/failure states that callers must handle.

Design:

typescript
type ApiResult<T> =
  | { ok: true; data: T }
  | { ok: false; status: number; error: string };

async function apiGet<T>(
  url: string,
  parse: (value: unknown) => T
): Promise<ApiResult<T>> {
  const res = await fetch(url);
  let body: unknown;
  try {
    body = await res.json();
  } catch {
    return { ok: false, status: res.status, error: "Invalid JSON response" };
  }
  if (!res.ok) {
    const message = typeof body === "string" ? body : "Request failed";
    return { ok: false, status: res.status, error: message };
  }
  try {
    return { ok: true, data: parse(body) };
  } catch {
    return {
      ok: false,
      status: res.status,
      error: "Response validation failed",
    };
  }
}

The generic T does not make fetch() JSON safe. T only becomes trustworthy after the provided runtime parser validates unknown. Production wrappers should also decide how to model invalid JSON, validation errors, network errors, and non-2xx response bodies. Network failures from fetch() itself are another design choice: either allow them to reject the promise or model them as another error variant in the result union.

A strong answer is:

I return ok/data or ok/error unions, keep JSON as unknown, validate before producing T, and never cast fetch JSON directly to an interface.

Scenario: Model UI state to make illegal states unrepresentable.

What interviewers are testing: Whether you replace independent booleans/nullables with a discriminated union whose variants represent only valid application states.

Bad: separate isLoading, error, data booleans/strings.

Good:

typescript
type RequestState<T> =
  | { status: "idle" }
  | { status: "loading" }
  | { status: "success"; data: T }
  | { status: "failure"; error: string };

UI switches on status—can't read data while loading.

A strong answer is:

I replace boolean soup with one discriminated status field so the compiler blocks reading data during loading or idle.

Scenario: Migrate a JavaScript codebase to TypeScript.

What interviewers are testing: Whether you can migrate incrementally without hiding the codebase behind mass any or stopping feature delivery.

Phase Action
1 allowJs: true, checkJs optional
2 Rename leaf modules to .ts
3 Enable strict incrementally per package
4 Type boundaries first—API clients, config
5 Avoid mass any; use unknown + guards
6 CI tsc --noEmit gate on PR

Strangler pattern—no big-bang freeze.

A strong answer is:

I migrate from the outside in—API and shared models first—with strict on new code and allowJs for legacy until we tighten module by module.

What is covariance and contravariance (function types)?

What interviewers are testing: whether you explain why function parameter types are contravariant and return types covariant under strict checking.

Under strictFunctionTypes, function parameters are checked contravariantly and returns covariantly. This strict parameter behavior applies to function types/function syntax; method declarations have a compatibility exception and can remain bivariant.

The unsafe assignment is narrower parameter → wider parameter:

typescript
type Animal = { kind: string };
type Dog = Animal & { breed: string };

const dogOnly = (dog: Dog) => {};
let handlesAnimal: (animal: Animal) => void;

handlesAnimal = dogOnly; // error: caller may pass a Cat

Assigning (animal: Animal) => void to (dog: Dog) => void is the safe direction—a function that accepts any Animal can handle a Dog.

A strong answer is:

strictFunctionTypes rejects assigning a Dog-only handler to an Animal handler because callers may pass other Animal subtypes—I explain contravariance with that unsafe direction.

What is the difference between import type and regular import?

What interviewers are testing: Whether you understand the difference between type-only dependencies and runtime module imports, especially with modern ESM and verbatimModuleSyntax.

typescript
import type { User } from "./models";
import { type User, createUser } from "./models";

import type is erased from JavaScript output and does not create a runtime module import. Useful for verbatimModuleSyntax and avoiding circular value imports.

A strong answer is:

import type prevents a type-only dependency from becoming a runtime import and makes module intent explicit with verbatimModuleSyntax.

What changed from TypeScript 6 to TypeScript 7?

What interviewers are testing: Whether you understand TypeScript 7 as the stable native compiler release, TS6 as the migration bridge, and the ecosystem/tooling compatibility risks around deprecated options and the missing TS7.0 compiler API.

As of July 8, 2026, TypeScript 7.0 is released and stable. It is the native Go port, and TypeScript reports roughly 10× compiler/tooling improvements in many workloads.

Topic Current reality
TS6 Last release based on the JavaScript compiler; migration bridge to TS7
TS7 Stable native compiler/language service implemented in Go
Type checking Designed for compatibility with TS6 semantics
Performance Native implementation and parallelism can deliver major speedups
Defaults Inherits TS6 defaults such as strict: true, module: esnext, newer target, types: []
Deprecated options Options deprecated in TS6 become errors/unsupported in TS7
Compiler API TS7.0 does not ship a programmatic compiler API; TS7.1 is expected to introduce the new API
Tool compatibility Some tools/framework integrations can still need TS6 because they depend on the old compiler/language-service API

The lack of a TS7.0 programmatic API is particularly important. The TypeScript team specifically calls out tooling such as typescript-eslint, Vue, MDX, Astro, Svelte, and specialized Angular tooling as areas that may still require TypeScript 6 integration while the ecosystem catches up.

A strong answer is:

TypeScript 7 is now the stable native Go-based compiler, designed to preserve TypeScript 6 type-checking behavior while making builds and editor tooling much faster. The migration concern is ecosystem compatibility: TS7.0 has no programmatic compiler API, and deprecated TS6 configuration is removed or rejected, so I verify framework and tooling support before switching.


What to rehearse before TypeScript interviews

  • unknown vs any and narrowing flow
  • interface vs type trade-offs
  • Discriminated unions + exhaustiveness
  • User-defined type guards from unknown
  • satisfies vs as
  • Generics with extends keyof T
  • Partial, Pick, Omit, ReturnType, Awaited
  • Mapped type — write Pick by hand
  • strict flags and why they matter
  • Runtime validation at API boundaries
  • One React props and one API wrapper scenario
  • Front end interviews JS refresh
  • Full stack interviews if shared types matter

I practice narrowing unknown JSON, explain satisfies on a config object, implement Pick on a whiteboard, and tie answers to a real strict-mode migration or API client I shipped.


Pattern cheat sheet (quick reference)

Need TypeScript approach
External JSON unknown → validate → narrow
Success/error API Discriminated union on status or ok
Optional PATCH body Partial<Pick<T, keys>>
Validate config shape satisfies
Preserve exact literals as const
Typed literal config as const + satisfies where appropriate
Reuse function shapes ReturnType / Parameters
Safe property access getProperty<T, K extends keyof T>
Impossible branch assertNever(x: never)
React props Explicit object type, ReactNode children
No runtime types Zod parse at boundary

References


Summary

TypeScript interviews test safe boundaries—unknown, discriminated unions, generics, and satisfies—and whether you admit types do not validate runtime data without guards like Zod. Answer aloud, run the guard simulations, and compare your structure to each section. Pair with React, Angular, or full stack prep when the role continues past the type layer.

Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive experience, he excels across development, DevOps, networking, and security, delivering robust and efficient solutions for diverse projects.

  • Go (programming language)
  • Python (programming language)
  • DevOps
  • Computer Security
  • Cloud Computing
  • Kubernetes
  • Linux
  • Ansible (software)