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
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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