Skip to content

TypeScript Developer interview questions

100 real questions with model answers and explanations for Senior TypeScript Developer candidates.

See a TypeScript Developer resume example

Practice with flashcards

Spaced repetition · Hunter Pass

Questions

orm

I would relate the selected keys to the row with K extends keyof T and return Pick<T, K>.

  • The signature accepts readonly K[], so a literal tuple such as ['id', 'email'] preserves the union 'id' | 'email'.
  • Pick<T, K> makes the result change with the requested fields instead of returning Partial<T>, which would incorrectly make every field optional.
  • The runtime implementation must still reject unknown column names from JSON or user input because keyof provides no runtime validation.

Why interviewers ask this: The interviewer is checking whether the candidate can express a concrete input-output relationship with a minimal generic constraint.

I would model an EventMap and bind the event key to its indexed payload type.

  • The core signature is emit<K extends keyof EventMap>(name: K, payload: EventMap[K]), which keeps the two arguments correlated.
  • A similar on<K extends keyof EventMap> callback should consume EventMap[K], so publishers and subscribers use one contract.
  • I would generate or validate the EventMap from the wire schema because the generic only checks TypeScript callers, not messages arriving from Kafka.

Why interviewers ask this: A strong answer preserves correlation between a named event and its payload without an overload explosion.

genericsforms

I would use setField<T, K extends keyof T>(model: T, key: K, value: T[K]).

  • K is inferred from the key argument, so choosing 'age' makes the value position use the declared age type.
  • I would not type the value as T[keyof T], because that union loses the relationship and could accept any other field's value.
  • For nested paths I would expose a bounded path utility or generated field map rather than an unbounded recursive generic.

Why interviewers ask this: The interviewer is evaluating whether the candidate can keep several generic positions correlated instead of widening them independently.

typesapiserialization

I would constrain each stage to the smallest callable capability and carry its output into the next stage.

  • A stage can be modeled as Stage<Input, Output>, while the builder only accepts the next Stage<PreviousOutput, NextOutput>.
  • Separate parser, authorizer, and serializer capabilities keep a plugin from exposing methods that the pipeline never calls.
  • I would cap the supported chain length or generate overloads if variadic tuple inference makes editor latency or diagnostics unacceptable.

Why interviewers ask this: The interviewer wants a practical generic design that enforces composition while avoiding an overgrown base contract.

typescript

I would infer C only from colors and type defaultColor as NoInfer<C>.

  • With colors inferred as 'red' | 'yellow' | 'green', 'blue' is checked against that union instead of enlarging it.
  • NoInfer controls the inference source but still performs assignability checking on the default argument.
  • I would keep the primary source obvious in the API because NoInfer cannot repair a model with genuinely ambiguous ownership of C.

Why interviewers ask this: A strong answer knows the specific inference-direction problem NoInfer solves and does not present it as a general safety operator.

designapi

It should reject the Dog-only function because the host may legally call it with another Animal.

  • Function parameters are checked contravariantly under strictFunctionTypes, so a handler accepting a wider input is safe in the narrower slot.
  • I would declare callbacks as function-valued properties, not methods, because method parameter checking retains historical bivariance.
  • If a plugin truly handles only Dog, the registration key or generic must narrow the host's dispatch domain to Dog as well.

Why interviewers ask this: The interviewer is checking whether the candidate can explain variance through actual data flow and account for TypeScript's method exception.

unionapi

I would use a distributive conditional type over the response union.

  • T extends { status: 'ok' } ? T : never evaluates once per union member when T is a naked type parameter.
  • The failed members become never and disappear from the resulting union, leaving their distinct success payloads intact.
  • I would type-test mixed unions plus never and any because those inputs reveal accidental broadening in public utilities.

Why interviewers ask this: The interviewer is evaluating precise use of conditional distribution on a realistic discriminated protocol.

serializationbatchunion

I would suppress distribution by testing [T] extends [Serializable].

  • Tuple wrapping makes the conditional evaluate the union as one value rather than once per member.
  • That lets a union containing one nonserializable member fail the whole batch contract instead of returning a partial union.
  • I would name the utility around its all-members semantics because the tuple syntax is easy for maintainers to remove accidentally.

Why interviewers ask this: A strong answer distinguishes per-member filtering from a whole-union constraint and can control the behavior deliberately.

async

I would infer the return from the callable shape and normalize it with Awaited.

  • T extends (...args: any[]) => infer R ? Awaited<R> : never extracts the return while handling nested PromiseLike values.
  • Parameters<T> can preserve the original argument tuple when the wrapper forwards calls unchanged.
  • I would avoid any in the public result and test overloaded functions separately because inference from an overload set uses its last signature.

Why interviewers ask this: The interviewer is checking practical use of infer plus awareness of overloaded-function behavior.

advanced-types

I would recursively match the path with template literal types and infer each colon-prefixed segment.

  • A branch matching `${string}:${infer Param}/${infer Rest}` adds Param and recurses into Rest, while the final segment handles one remaining parameter.
  • The route factory needs a const type parameter so the path remains a literal instead of widening to string.
  • I would bound the recursion and fall back to Record<string, string> for dynamic paths that are not authored literals.

Why interviewers ask this: A strong answer combines template inference, literal preservation, and a bounded fallback for a real routing API.

api

ReturnType infers from the last overload signature, so I would not use it to recover each overload's mapping.

  • The implementation signature is not a callable public overload, and the final visible signature is normally the broad catch-all case.
  • For a finite mapping I would publish a SearchResultMap and use a generic key to select SearchResultMap[K].
  • If distinct call shapes are essential, I would test each overload directly with tsd instead of deriving promises from ReturnType.

Why interviewers ask this: The interviewer is checking a known infer edge case and whether the candidate can replace brittle overload introspection with an explicit contract.

types

I would map only the explicitly editable key set and add optionality to those properties.

  • EditableKeys<T> should exclude id, createdAt, and other server-owned keys before producing { [K in EditableKeys<T>]?: T[K] }.
  • I would not apply Partial<T> to the full model because it silently advertises updates the server rejects.
  • With exactOptionalPropertyTypes enabled, omission means unchanged and undefined is not accepted unless the wire contract explicitly allows it.

Why interviewers ask this: A strong answer uses mapped types to encode the actual update surface rather than mechanically weakening the whole entity.

api

I would use key remapping with a template literal over Extract<keyof State, string>.

  • The mapped key can be written as `get${Capitalize<K>}` while the value remains a function returning State[K].
  • Restricting K to string avoids inventing names for numeric or symbol keys that the naming convention cannot represent.
  • I would generate runtime getter functions from the same key list because a mapped type alone creates no JavaScript properties.

Why interviewers ask this: The interviewer is evaluating key remapping, key-domain control, and the boundary between generated types and runtime objects.

callbackstypesadvanced-types

I would derive callback names from the finite EventMap keys and remap each one once.

  • A mapped type can turn checkoutStarted into onCheckoutStarted while retaining the corresponding EventMap[K] payload.
  • I would not combine independent unions for product, action, region, and version because template literals multiply every combination.
  • Once the event catalog reaches hundreds of generated names, code generation gives faster checks and clearer declaration output.

Why interviewers ask this: A strong answer applies template literals to a bounded catalog and recognizes when combinatorial growth outweighs the benefit.

I would derive dot paths with a recursive mapped type but impose a small depth limit.

  • A tuple counter can stop expansion after four or five levels, which matches the catalog convention and bounds instantiation work.
  • Arrays and functions should be terminal values so the utility does not enumerate their methods as translation keys.
  • For a 20,000-key catalog I would generate a union from the source files instead of recomputing it in every consumer program.

Why interviewers ask this: The interviewer is checking whether recursive type programming is constrained by the real data shape and compiler cost.

endpointsopenapi

I would use code generation once the schema expansion is large, recursive, or shared outside TypeScript.

  • Conditional types are useful for small authored relationships, but they cannot validate the runtime document and can make every editor session repeat expensive work.
  • A pinned generator can emit named request and response types once, while CI checks deterministic output against the OpenAPI source.
  • I would keep a thin handwritten adapter around generated transport code so generator changes do not leak into application modules.

Why interviewers ask this: A strong answer sets a concrete boundary between useful type-level computation and build-time schema generation.

migrationsarchitecturemonorepo

I would migrate package boundaries monotonically against one strict base config.

  • I would inventory errors by category, start with dependency leaves, and enable strict in a package only after its public declarations are clean.
  • Project references let strict packages expose checked declarations while legacy packages continue behind explicit boundary adapters.
  • CI should reject new suppressions and any attempt to disable strict in completed packages, with temporary exceptions tracked to removal.

Why interviewers ask this: The interviewer is evaluating a migration that preserves delivery while steadily increasing enforceable compiler guarantees.

fundamentals

I would model optional fields as possibly absent without automatically accepting undefined.

  • With name?: string | null, callers may omit name, send a string, or send null, but name: undefined is rejected.
  • That matches JSON behavior because undefined is not a JSON value and must not become a hidden fourth patch state.
  • The serializer and runtime schema must enforce the same omission and null rules before the patch reaches the server.

Why interviewers ask this: A strong answer connects exact optional property semantics to a concrete wire protocol with distinct update states.

fundamentals

I would make absence explicit at the dynamic lookup boundary instead of asserting every value exists.

  • A getTranslation(key): string | undefined API is honest for arbitrary strings and lets the caller choose fallback behavior.
  • For generated finite keys, a mapped Record<TranslationKey, string> can prove complete coverage and return string.
  • I would reserve non-null assertions for a locally checked invariant, not scatter them across thousands of reads.

Why interviewers ask this: The interviewer is checking whether stricter indexed access leads to truthful container contracts rather than widespread assertions.

I would narrow the caught value before reading library-specific fields.

  • axios.isAxiosError(error) supplies runtime evidence for AxiosError, while error instanceof Error handles ordinary exceptions.
  • Unknown values such as strings still need a safe fallback because JavaScript permits throwing any value.
  • I would normalize all branches into one application error union so the rest of the codebase does not depend on Axios internals.

Why interviewers ask this: A strong answer uses runtime narrowing at the dependency boundary and turns unknown failures into a stable internal contract.

Locked questions

  • 21

    A guard proves config.token is present, but TypeScript rejects its use inside a delayed callback because config is mutable; how would you preserve the narrowing?

    typescriptcallbackstype-narrowing
  • 22

    A checkout workflow has 14 states and new states are added quarterly; how would you make every reducer handle them exhaustively?

  • 23

    PaymentRouter handles 18 method variants, including BankTransfer, CardPayment, and StoredMethod with optional iban and pan; why can an 'iban' check leave StoredMethod on both branches?

    types
  • 24

    A type predicate isUser(value): value is User currently returns true after checking only id; what would you require before trusting it across an authentication SDK?

    authtype-narrowing
  • 25

    A command registry must contain every CommandName while preserving each handler's exact parameter type; why use satisfies instead of an annotation or assertion?

    registries
  • 26

    defineRoutes receives 70 route literals and currently requires callers to write as const; how would a const type parameter improve the API?

    generics
  • 27

    A fetchResource API supports user, invoice, and report, each with a different result; would you choose overloads or a generic resource map?

    genericsapisource-maps
  • 28

    A public npm SDK accidentally emits a 200-line anonymous return type that mentions internal classes; how would you redesign its declaration surface?

    npm
  • 29

    An SDK accepts CreateUserInput, but a variable with extra admin fields passes structural assignment while an inline literal fails; how would you handle exactness?

  • 30

    A plugin ecosystem needs third parties to add entries to FastifyInstance; when is module augmentation appropriate and what can go wrong?

  • 31

    Your npm library ships ESM and CommonJS, and NodeNext consumers report default-import errors; how would you align runtime and declarations?

    npmmodules
  • 32

    A monorepo path alias lets apps import @platform/auth/src/private even though the published package hides it; how would you enforce the real boundary?

    monorepo
  • 33

    Eight services and two mobile clients share an OpenAPI contract; should TypeScript interfaces, Zod schemas, or OpenAPI be the source of truth?

    typescripttypesschema
  • 34

    A patch release tightens T extends object to T extends Record<string, unknown> and consumer builds fail; is this a type-level breaking change?

    versioning
  • 35

    How would you test a public Result<T, E> library across TypeScript 5.4 through 5.8 without relying only on runtime tests?

    typescript
  • 36

    A 120-package monorepo takes 11 minutes to type-check because one tsconfig loads all sources; how would project references change the architecture?

    monorepotsconfig
  • 37

    In that referenced monorepo, what belongs in the root solution tsconfig versus the shared base tsconfig?

    monorepotsconfig
  • 38

    Warm CI builds reuse .tsbuildinfo, but stale results appear when jobs move between runners; how would you key and validate the cache?

    validationcaching
  • 39

    Editor feedback rose from 300 ms to 8 seconds after a type utility landed; which TypeScript compiler measurements would you collect first?

    feedbacktypescript
  • 40

    A permission type combines 20 resources, 12 actions, 6 regions, and 4 scopes into template literals; why can this hurt the checker and what would you change?

  • 41

    A DeepReadonly utility hits excessive type instantiation on 30-level generated schemas; how would you bound it?

    schema
  • 42

    Declaration emit is now half of a library's 6-minute build and produces huge structural types; what changes would you test?

  • 43

    Enabling skipLibCheck cuts CI by 90 seconds in a 120-package repository; what exactly are you giving up?

  • 44

    A Cloudflare Worker package accidentally sees document and window because the monorepo base includes DOM; how would you configure module, moduleResolution, and lib?

    monorepoconfigdom
  • 45

    Two workspaces install different copies of a shared types package, and a unique-symbol brand becomes incompatible; how would you prevent this architecture?

    architecture
  • 46

    A billing service repeatedly mixes UserId and InvoiceId because both are strings; how would you introduce brands without inviting unsafe casts?

  • 47

    An ad platform passes milliseconds into an API expecting cents because both are numbers; would branded numeric units be appropriate?

    api
  • 48

    A transaction builder must not expose commit before begin and validate; how could typestate encode that sequence?

    transactionsvalidation
  • 49

    A library's public DeepMerge type has 11 conditional branches and slows 300 consumer projects; how would you set a type-complexity budget?

    algorithms
  • 50

    A generated GraphQL client still needs five casts around a third-party cache API; how would you contain those escape hatches?

    graphqlcachingdependencies
  • 51

    A refactor in a 900,000-line monorepo produces a 200-line assignability error inside a composed router type; how do you find the first useful mismatch before today's release?

    refactoringmonorepo
  • 52

    A generic cache wrapper makes an SDK call that returned User now infer Promise<unknown> at 140 call sites; how do you isolate the inference break?

    promisesgenericsadvanced-types
  • 53

    After adding withRetry to 30 repository methods, literal result variants widen to unknown only when the callback is async; what do you inspect and change?

    asynccallbacks
  • 54

    A logging decorator applied to an overloaded search function makes Parameters<typeof search> become never for one call form; a release is due in two hours. How do you respond?

    decoratorsloggingforms
  • 55

    CI suddenly reports 430 duplicate identifier errors between two @types packages after a lockfile update, while application files are unchanged; what do you do first?

    packaging
  • 56

    Two workspace packages expose identical-looking branded Session types, but assignments fail after one resolves auth-types 4.2 and the other 4.3; how do you prove and fix the version skew?

    sessions
  • 57

    Upgrading TypeScript in an 80-package monorepo creates 1,700 diagnostics, but security requires the upgrade within five days; how do you separate real defects from declaration churn?

    churnmonorepotypescript
  • 58

    A production 500 occurs because response as User hid a missing permissions array from a partner API; how do you contain the incident and prevent the same cast pattern?

    incidentsapi
  • 59

    An event consumer used payload as unknown as InvoicePaid, accepted 3,200 malformed messages, and corrupted a projection; what is your recovery plan?

    recovery
  • 60

    A 90,000-line payments package contains 1,400 explicit any occurrences, and the team can spare only 10 percent of each sprint; how do you burn the debt down without gaming the count?

    agile
  • 61

    A generated analytics SDK exposes any in 600 event payloads, and product code now relies on misspelled fields; how do you tighten it while teams keep shipping daily?

  • 62

    On release day, 260 strictNullChecks errors appear in one legacy package and a manager asks to set strict false at the root; how do you preserve a narrow boundary?

  • 63

    You must migrate a 420,000-line JavaScript application with 38 weekly releases to TypeScript over nine months; what do you change first without freezing delivery?

    typescriptjavascript
  • 64

    Six months into a JS-to-TS migration, 11,000 files compile only because checkJs is off and ts-nocheck grew by 35 percent; how do you restart progress?

    migrations
  • 65

    A codemod converted 4,800 files from CommonJS to TypeScript, but 73 modules now execute initialization in a different order; how do you handle the batch?

    typescriptmodulessoft-skills
  • 66

    A critical JavaScript fraud library has no declarations, returns five undocumented result shapes, and must remain in service for the next quarter; how do you integrate it safely?

    javascript
  • 67

    After a recursive Path<T> utility lands, clean type-check time rises from 48 seconds to 6 minutes 20 seconds across 22 packages; how do you diagnose and roll it back safely?

    recursion
  • 68

    VS Code completion jumps from 250 ms to 7 seconds after a permission template-literal type grows to 18,000 members; what do you change first?

    types
  • 69

    A library build grows from 2 minutes to 9 minutes after an exported factory acquires a huge inferred return type; how do you verify declaration emit is the cause?

    advanced-types
  • 70

    tsc starts exceeding an 8 GB CI limit in an 80-package workspace after three packages begin importing each other's source; how do you recover?

    compiler
  • 71

    Warm CI passes, but a clean nightly tsc -b fails in 14 packages after cached .tsbuildinfo was moved between runners; what do you inspect?

    cachingcompiler
  • 72

    Eight developers see no error in VS Code, but CI reports TS2345 on the same file after a shared config release; how do you establish parity today?

    config
  • 73

    A package compiles inside the monorepo but its NodeNext consumer gets TS1479 and a default-import error within minutes of installing it; how do you debug the published artifact?

    monorepoartifacts
  • 74

    Declaration emit fails with TS4023 because an exported function's inferred result names a private type from a dependency; 25 consumers need a patch today. What do you change?

    dependenciesadvanced-types
  • 75

    Turning on skipLibCheck removes 620 CI errors, but one service still crashes because two libraries disagree on Request.user; do you keep the flag?

    conflict
  • 76

    A non-null assertion on session.user! causes about 20 intermittent checkout failures per day after logout was made asynchronous; how do you fix it?

    asyncfundamentalssessions
  • 77

    An out-of-range prices[index] read produced NaN in 600 invoices, and noUncheckedIndexedAccess is currently off in a 300,000-line app; what is your first safe step?

    indexes
  • 78

    TypeScript rejects config.token inside a queue callback even though an if check passed 40 lines earlier; a teammate proposes config.token!. What do you review?

    typescriptcallbacksdata-structures
  • 79

    A custom isAccount predicate checks only id, and 90 malformed records pass it before failing on account.plan; how do you repair trust in the predicate?

  • 80

    A fifteenth payment state was added, but a reducer's default branch silently returned the old state and 4 percent of refunds stalled; how do you prevent recurrence?

  • 81

    A PATCH client sends nickname: undefined, but the serializer drops it while one server interprets presence as deletion; 12,000 profiles are at risk. What do you change?

    serializationfundamentals
  • 82

    FastifyInstance types include auditLog through module augmentation, but one of 12 services forgot to register the plugin and crashes on first request; how do you close the gap?

  • 83

    A patch release reorders two overloads and 35 consumer builds infer string instead of UserId; how do you handle the release?

    soft-skillsadvanced-types
  • 84

    An OpenAPI client still compiles after the provider changes total from number to numeric string, and 8 percent of reports show concatenated totals; where do you intervene?

    openapi
  • 85

    A team maintains a Zod schema and a separate User interface; after three releases the schema accepts null where the interface says string, causing 240 failures. How do you remove the drift?

    typesfundamentalsschema
  • 86

    A public Result library passes runtime tests but TypeScript 5.8 changes narrowing at 18 consumer call sites; support promises cover 5.5 through 5.8. What do you test and release?

    promisestypescripttype-narrowing
  • 87

    In code review, a 20-line generic parse<T> returns JSON.parse(text) as T and is already used in 47 files; what exact change do you request before approval?

    genericscode-review
  • 88

    A pull request adds a 14-branch conditional type for one form and makes errors expand to 120 lines; the author says it removes 30 duplicated lines. How do you review it?

    code-reviewformsadvanced-types
  • 89

    A junior fixes 16 compiler errors in a payment PR with as unknown as and asks for approval before the afternoon deploy; how do you mentor without taking over?

    mentoringdeployment
  • 90

    A teammate disables useUnknownInCatchVariables for a 60-file package because an Axios upgrade creates 85 errors; what feedback and rollout do you give?

    feedback
  • 91

    A review replaces a 12-key configuration type with Record<string, any> to unblock two plugin fields before tomorrow's launch; what do you propose?

    config
  • 92

    A Cloudflare Worker compiles because the shared tsconfig includes DOM, but crashes on document during 15 percent of requests after a new branch runs; how do you fix the configuration?

    configtsconfigdom
  • 93

    An app imports @platform/auth/src/private through a tsconfig path, passes local type-checking, then the production bundle cannot resolve it in 6 of 20 deployments; what do you change?

    deploymenttsconfig
  • 94

    After switching moduleResolution to Bundler in a Node service, type-checking passes but production rejects three extensionless imports; how do you recover?

    bundling
  • 95

    Introducing project references creates a cycle among ui, analytics, and config, and tsc -b now stops 120 packages; a release train leaves today. What do you do?

    configcompilermonorepo
  • 96

    A source-generated file changes on every machine because paths leak into declarations, producing 9,000-line diffs and cache misses; how do you stabilize it?

    caching
  • 97

    Tests mock a new required Invoice.currency field with as Invoice, so production formatting fails although 1,200 tests pass; how do you repair the test boundary?

    mocking
  • 98

    A library inlines a const enum across 40 consumers, then a server deploy changes member values and old clients send the wrong numeric code; how do you contain the skew?

    deploymentenums
  • 99

    Enabling isolatedModules breaks 310 global-script files that use cross-file namespace merging, but the new transpiler rollout is scheduled this week; how do you migrate safely?

    kubernetesmodules
  • 100

    A pull request tightens T extends object to T extends Record<string, unknown> in a widely used package, and a preview build breaks 260 downstream calls; the author labels it a patch. What do you decide?

    code-review