TypeScript Senior
How does TypeScript determine type compatibility (assignability) between two types?
Select the correct answer
A source type is assignable only if it shares the exact same declared type name.
A source type is assignable if it has fewer members than the target type needs.
A source type is assignable if it has at least the members the target requires.
A source type is assignable only when both types come from the same module file.
What is global augmentation and what are the risks of using it in a shared codebase?
Select the correct answer
It extends types in the global scope, and the risk is that changes silently affect every file, causing conflicts and hidden coupling.
It extends types only within one module, and the risk is that other modules cannot see them, causing duplicated declarations everywhere.
It generates new declaration files at build time, and the risk is that the compiler regenerates them slowly, causing long incremental builds.
It rewrites the global runtime objects directly, and the risk is that browsers may crash when augmented prototypes are accessed at runtime.
What is the difference between declaration merging and module augmentation?
Select the correct answer
Module augmentation runs at runtime to patch objects, while declaration merging is a purely compile-time alias resolution step.
Module augmentation adds members to an existing module's types, while declaration merging combines same-named declarations into one.
Declaration merging only works on classes, while module augmentation only works on plain interfaces declared inside files.
They are identical features, merely two different names the compiler uses for combining interfaces across separate source files.
What is the purpose of triple-slash directives and are they still relevant in modern TypeScript?
Select the correct answer
They are compiler flags written inline to disable strict mode; still required in every modern TypeScript project entry file.
They are special comments that declare file dependencies; largely superseded by module imports but still used in .d.ts files.
They are decorators that inject metadata for dependency systems; replaced by the standard decorators proposal in recent versions.
They are runtime annotations that load polyfills automatically; modern bundlers handle this, so they are now completely removed.
Explain 'Declaration Merging.' Which construct supports it, and why might it be useful or dangerous when working with third-party libraries?
Select the correct answer
Classes alone support it; it lets you patch library methods but may duplicate the generated JavaScript output during bundling.
Only type aliases support it; it lets you override library types but may break the runtime behavior of imported modules.
Only enums support it; it lets you append library values but can accidentally shadow existing global variables at compile time.
Interfaces and namespaces support it; it lets you extend library types but can silently introduce conflicting members.
What are assertion functions and the 'asserts' keyword? How do they differ from type predicates using 'is'?
Select the correct answer
Assertion functions return a boolean that narrows inside the branch; is predicates throw an error whenever the runtime check happens to fail
Assertion functions and is predicates both throw on failure; the only difference is that asserts cannot be used with class instances
Assertion functions throw if a condition fails and narrow the type afterward; is predicates return a boolean to narrow inside a branch
Both narrow types identically, but asserts works at runtime only while is predicates are erased entirely during compilation
How does TypeScript handle module resolution? Explain the difference between CommonJS and ESNext module targets in the context of a modern frontend or backend application.
Select the correct answer
Both produce identical output, but CommonJS targets browsers directly while ESNext is restricted to server-side Node environments only
CommonJS supports top-level await and dynamic imports natively; ESNext lacks these and must transpile every module down to function wrappers
CommonJS uses import/export with asynchronous loading; ESNext uses require and module.exports resolved at runtime later
CommonJS uses require and module.exports loaded synchronously; ESNext uses import/export static modules that enable tree-shaking
Discuss the performance implications of complex recursive types on the TypeScript compiler.
Select the correct answer
They have no measurable impact because all type information is erased before the compiler begins its type evaluation and checking phase
They speed up compilation by letting the checker cache instantiations, though they noticeably increase the size of the emitted output files
Deeply recursive or conditional types can sharply slow type-checking, raise memory use, and may hit the compiler's recursion depth limit
Recursive types are evaluated lazily at runtime, so they add startup latency to the emitted JavaScript but never affect the compile times
What are conditional types in TypeScript? Explain the syntax T extends U ? X : Y and how it allows for dynamic type branching.
Select the correct answer
If T is assignable to U the type resolves to X, otherwise to Y
If T inherits class U the type resolves to X, otherwise to never
If U is assignable to T the type resolves to X, otherwise to Y
If T equals U exactly the type resolves to X, otherwise it errors
Explain Template Literal Types. How can they be used to enforce string patterns like CSS properties or API endpoints at compile time?
Select the correct answer
They build runtime template strings from variables like `${value}px` for output formatting
They build regex patterns from string types like /\d+px/ for validating input at runtime
They build string literal types from interpolations like `${number}px` for compile-time checks
They build enum members from string unions like red | blue for restricting allowed inputs
What is the purpose of the infer keyword within a conditional type?
Select the correct answer
It defines a default type to fall back on when the conditional cannot be fully resolved
It declares a placeholder within extends to capture and extract a type for the branch
It asserts that an unknown value matches a given type before the conditional evaluates
It forces the compiler to widen a literal type to its base type within the true branch
What is the difference between as const (const assertions) and the satisfies operator?
Select the correct answer
Both narrow values to readonly literals, but satisfies also freezes them at runtime
Both validate a value against a declared type without changing the value's inferred type
satisfies narrows to readonly literals; as const only validates against a given type
as const narrows to readonly literals; satisfies checks a type while keeping inference
What does it mean for a conditional type to be 'distributive', and how does wrapping a type in a tuple prevent distribution?
Select the correct answer
A naked type parameter applies the condition to each union member; a tuple evaluates once
A union is merged into a single type before the condition runs just one time over it all
The condition is true for every union member, but a tuple makes it false as a whole instead
A tuple applies the condition to each union member; a naked parameter evaluates only once
What are decorators in TypeScript? Explain the conceptual difference between the experimental legacy decorators and the new Stage 3 ECMAScript decorators.
Select the correct answer
Legacy decorators are the standardized ECMAScript syntax, while Stage 3 decorators remain a TypeScript-only experimental feature.
Legacy decorators support classes only, while Stage 3 decorators support parameters, accessors, and namespaces too.
Legacy decorators run only at runtime, while Stage 3 decorators are fully erased and run purely at compile time.
Legacy decorators are an experimental TypeScript-specific feature, while Stage 3 decorators follow the standardized ECMAScript proposal.
Explain the concept of branded types (or opaque types) and how you simulate nominal typing.
Select the correct answer
You intersect a primitive with a unique phantom property so structurally identical values are treated as distinct.
You annotate the type with a decorator that the compiler reads to generate separate nominal type identities.
You declare the type with a runtime class wrapper so the JavaScript engine enforces the distinction at runtime.
You mark the type readonly so two structurally identical values can no longer be assigned to one another.
Explain variance in TypeScript: what are covariance and contravariance and how do they affect function compatibility?
Select the correct answer
Function return types are contravariant while parameter types are covariant for sound assignability.
Function return types are covariant while parameter types are contravariant for sound assignability.
Both function return and parameter types are strictly contravariant under all TypeScript compiler settings.
Both function return and parameter types are strictly covariant under all TypeScript compiler settings.