TypeScript Expert
Why would we choose TypeScript over JavaScript for a large-scale enterprise application? What specific 'pain points' of JS does it solve?
Select the correct answer
It executes noticeably faster than JavaScript because types are optimized at runtime.
It removes the need for unit tests by fully guaranteeing program correctness for you.
It lets code run without any build step while still adding strong type guarantees.
It catches type errors at compile time and improves tooling and refactoring.
Explain the difference between explicit and implicit type assignment.
Select the correct answer
Explicit checks types at runtime; implicit checks them only when compiling.
Explicit means you annotate the type; implicit means TypeScript infers it.
Explicit means TypeScript infers the type; implicit means that you annotate it.
Explicit applies only to functions; implicit applies only to local variables.
TypeScript uses a structural type system. Can you explain what that means and how it differs from nominal typing found in languages like Java or C#?
Select the correct answer
Compatibility is checked at runtime by comparing object prototype chains.
Compatibility requires both types to explicitly declare the same exact name.
Compatibility is determined by a type's shape, not by its declared name.
Compatibility requires both types to inherit from a shared base class.
What is Type Inference, and what are the trade-offs between letting TypeScript infer a type versus explicitly annotating it?
Select the correct answer
Inference always produces wider types, while explicit annotations are purely cosmetic.
Inference disables strict checks, while explicit types enable full safety in all cases.
Inference cuts verbosity, while explicit types document intent and guard public APIs.
Inference runs at runtime, while explicit types are validated only during execution.
Explain the concept of 'Erasure' in TypeScript. What happens to your types once the code is compiled and running in the browser?
Select the correct answer
Types are compiled into hidden classes that enforce constraints at runtime.
Types are preserved as metadata accessible through reflection while running.
Types are stripped during compilation and have no presence at runtime.
Types are converted into runtime checks that validate values during execution.
What are the trade-offs of using TypeScript, and when would you not use it?
Select the correct answer
It requires a special runtime, so avoid it when targeting standard web browsers.
It slows runtime performance, so avoid it for any compute-heavy production services.
It blocks modern JS features, so avoid it on projects needing the latest syntax.
It adds build and learning overhead, so skip it for tiny scripts or quick prototypes.
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 the difference between an Interface and a Type Alias, and when would you choose one over the other?
Select the correct answer
Interfaces are faster at runtime; type aliases work only with plain object shapes here.
Interfaces support declaration merging; type aliases can express unions and primitives.
Interfaces exist at runtime; type aliases are completely erased during compilation steps.
Interfaces can express unions and tuples; type aliases support declaration merging fully.
What are 'Utility Types' like Partial, Omit, Pick, and Record? Explain how one of them works under the hood.
Select the correct answer
Omit<T, K> uses a mapped type that strips the readonly modifier from the listed keys.
Partial<T> uses a mapped type that adds the optional modifier to every property of T.
Record<K, T> uses an index-signature loop that copies all values from T into K.
Pick<T, K> uses conditional types to filter out properties not assignable to K.
What is excess property checking and why does it only trigger on object literals?
Select the correct answer
It flags extra properties on object literals assigned directly, since a stored variable could legitimately hold a wider type.
It flags extra properties only on class instances because nominal typing requires their shapes to match exactly.
It flags extra properties on every object because structural typing strictly forbids any unexpected members anywhere.
It flags extra properties on function return values because inference is unable to widen the resulting object type.
What is the difference between the readonly modifier in TypeScript and the const keyword in JavaScript? How does ReadonlyArray differ from a standard array?
Select the correct answer
const deeply freezes objects, while readonly is shallow and ReadonlyArray returns copies of elements.
const works at runtime only, while readonly is identical and ReadonlyArray adds runtime immutability checks.
const blocks rebinding a variable, while readonly blocks property writes and ReadonlyArray hides mutating methods.
const blocks property writes, while readonly blocks rebinding and ReadonlyArray disables index access.
What is an index signature, and what are its limitations compared to a Record type?
Select the correct answer
An index signature types arbitrary keys of one value type, but cannot restrict keys to a finite known set.
An index signature types only a fixed set of keys, but cannot allow values that belong to a union type.
An index signature accepts only numeric keys, but Record additionally permits both string and symbol keys.
An index signature is verified at runtime, but Record performs only compile-time validation of its keys.
What is the difference between union types and intersection types? Can you give an example of when you'd use each?
Select the correct answer
A union means a value is one of several types; an intersection means a value has all properties of every type combined.
A union restricts values to the shared members of the types; an intersection produces a type holding either listed type.
A union means a value has all properties of every type at once; an intersection means a value is just one of several types.
A union merges object types into a single combined shape; an intersection picks the common subset of properties they share.
What are tuple types in TypeScript, and how do they differ from regular arrays?
Select the correct answer
Tuples are objects with numeric keys and named fields, while regular arrays only support sequential numeric index access.
Tuples have a fixed length where each position holds a specific known type, unlike arrays of arbitrary length and one type.
Tuples are immutable arrays whose values cannot change after creation, while regular arrays allow elements added or removed.
Tuples have an arbitrary length where every position holds one shared type, unlike arrays which fix the type at each index.
Explain the difference between the any and unknown types. Why is unknown considered safer, and how do you eventually use a value typed as unknown?
Select the correct answer
unknown and any are identical at compile time, but unknown is removed from the emitted output.
unknown accepts any value but forbids operations until you narrow it, whereas any disables all checks.
unknown accepts only primitives while any accepts objects, so you must always cast objects before use.
unknown disables checks like any, but additionally emits a runtime warning whenever its value is read.
Explain the never type. When would a function return never, and how is it useful for exhaustive type checking?
Select the correct answer
never represents an empty object; functions returning nothing use it, enabling optional property checks at compile time.
never represents nullable values; functions that might fail return it, enabling stricter non-null assertions in callers.
never represents values that never occur; functions that always throw or loop return it, enabling exhaustiveness.
never represents the void result; functions with no return use it, enabling control-flow narrowing inside branches.
Explain the difference between void and undefined in the context of function return types.
Select the correct answer
void means the return value is ignored, allowing functions that return any value to be assignable there.
void means the function returns undefined exactly, so returning other values raises a type error.
void and undefined are interchangeable, except void cannot be used as a variable type annotation.
undefined means any value is ignored, while void requires an explicit return statement always.
Explain the difference between null and undefined in the context of strictNullChecks.
Select the correct answer
With strictNullChecks, undefined is allowed everywhere while null is banned entirely and must be replaced with optional property values.
With strictNullChecks, both null and undefined stay assignable to any type but the compiler warns whenever they are actually used.
With strictNullChecks, null and undefined merge into one type that is freely assignable to every other type in the program.
With strictNullChecks, null and undefined are separate types not assignable to other types, so each must be handled explicitly.
What are literal types and how do they differ from primitive types?
Select the correct answer
A literal type is any value written directly in source, which the compiler always widens to its primitive type kind.
A literal type is one specific exact value, narrower than the primitive type that includes all values of that kind.
A literal type is a runtime construct holding constants, while primitive types are purely compile-time annotation kinds.
A literal type represents the whole set of strings or numbers, while primitive types restrict variables to one fixed value.
What are .d.ts files? Explain the concept of ambient declarations and when you would need to write a custom declaration file for a third-party library.
Select the correct answer
They hold type declarations without implementation, describing existing code; you write one when a JS library ships no type definitions.
They hold compiled JavaScript output with inline types, describing runtime behavior; you write one to bundle types into a library yourself.
They hold both declarations and implementations for modules, describing logic; you write one to override a library's existing runtime code.
They hold configuration for the compiler's strict options, describing builds; you write one to enable type checking on any untyped imports.
Explain the difference between type-only imports and regular imports. Why is this distinction important for build tools like isolatedModules?
Select the correct answer
import type brings in only runtime values and stays in output, so isolatedModules can strip unused types from every file when bundling.
import type brings in only types and is fully erased in output, so isolatedModules can transpile each file alone without type knowledge.
import type brings in only default exports and is erased later, so isolatedModules can merge declaration files into a single output module.
import type brings in both types and values but defers loading, so isolatedModules can resolve circular dependencies across separate files.
What is the difference between a 'Namespace' and a 'Module' in modern TypeScript, and which one should you use today?
Select the correct answer
Namespaces and modules are identical aliases for the same feature; either one may be used today since the compiler treats both alike.
Namespaces are file-based ES units with import syntax; modules are the older global grouping construct, and namespaces are preferred today.
Namespaces are TypeScript's older way to group code in one global scope; modules are file-based ES modules, which you should use today.
Namespaces compile to separate output files per scope; modules merge everything into one global object, so modules are discouraged today.
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 is the 'Non-null Assertion Operator' (!) and why is it generally discouraged in production code?
Select the correct answer
It asserts a value is not null or undefined, skipping checks; risky because it can mask genuine runtime null errors.
It forces a value to be null or undefined at runtime, adding checks; risky because it removes useful compile-time inference data.
It declares a value as optional in a function signature, adding checks; risky because it can break strict null checking entirely.
It converts a nullable value into a default fallback value, skipping checks; risky because it can silently swallow thrown exceptions.
What is the difference between optional chaining (?.) and the nullish coalescing operator (??), and when would you use each?
Select the correct answer
?. stops and yields undefined when accessing a property of a nullish value; ?? supplies a fallback only when the left value is nullish
?. throws an error when the operand is nullish at runtime; ?? silently swallows that error and substitutes the right-hand operand instead
Both return a default value, but ?. treats empty strings and zero as missing while ?? only checks against the undefined value alone
?. supplies a fallback value when the operand is nullish; ?? stops and yields undefined when a property access happens to fail
What is type narrowing? Explain different ways to achieve it, such as using typeof, instanceof, or custom type predicates with the is keyword.
Select the correct answer
It refines a broad type to a more specific one in a scope via checks such as typeof, instanceof, or is predicates.
It removes all type information during compilation to produce plain JavaScript using guards like instanceof and type predicates.
It widens a specific type to a broader union at runtime via casts such as as, satisfies, or non-null assertions on values.
It converts a value from one type to another permanently using helpers like typeof, keyof, and mapped type transforms.
What is the difference between a type assertion and type casting?
Select the correct answer
A type assertion validates the value at runtime and throws on mismatch, while casting silently ignores any type error at compile time.
A type assertion works only on primitives like numbers and strings, while casting works exclusively on classes and interface instances.
A type assertion only changes the compiler's view with no runtime effect, while casting actually converts the value at runtime.
A type assertion creates a new copy of the object with a new type, while casting mutates the original object's prototype at runtime.
What is the difference between the non-null assertion operator and optional chaining? What are the trade-offs of using the assertion operator?
Select the correct answer
Optional chaining converts nullish values into empty objects at runtime, while the assertion guarantees the property exists at runtime.
Optional chaining works only inside async functions for promises, while the assertion works only on synchronous property access.
Optional chaining throws an error when a value is nullish at runtime, while the assertion provides a default fallback value instead.
Optional chaining returns undefined safely when a value is nullish, while the assertion only silences the compiler's null check.
How do you perform 'Exhaustiveness Checking' in TypeScript to ensure all cases of a union are handled?
Select the correct answer
Use the satisfies operator on the switch statement itself, so the compiler checks that every union member has been listed.
Assign the union value to a never-typed variable in the default case, so any unhandled member triggers a compile error.
Add a runtime assertion in the default case that throws an error, so any unhandled member triggers a thrown exception only.
Enable the strict flag in tsconfig, so the compiler automatically reports any switch statement missing a union member case.
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
What does the strict flag in tsconfig.json actually do? Name at least three specific checks it enables such as strictNullChecks or noImplicitAny.
Select the correct answer
It forces every variable to carry an explicit type annotation and disallows the any type from appearing anywhere in the entire codebase
It only enables strictNullChecks and must be combined manually with noImplicitAny and strictFunctionTypes for coverage
It turns on a group of strict checks at once, including strictNullChecks, noImplicitAny, and strictFunctionTypes among others
It enables runtime validation of types and inserts checks into emitted JavaScript so null and undefined errors are caught while executing
What is the difference between target and lib in your TypeScript configuration?
Select the correct answer
Both control the output version, but target applies to application code and lib applies only to the bundled third-party dependencies
target sets the JavaScript language version of the emitted output; lib chooses which built-in API type declarations are available
target controls the module system of the output while lib determines the directory where compiled declaration files are written out
target chooses which built-in API declarations are available; lib sets the JavaScript language version used for the emitted output files
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 Generics, and how do they improve code reusability without sacrificing type safety?
Select the correct answer
They convert types into runtime values that can be inspected, letting code branch on the actual type passed in during normal execution
They let a function accept any type at runtime by erasing all checks, so callers gain flexibility at the cost of full type safety
They duplicate a function for each concrete type at compile time, so reuse comes from generated overloads rather than a single definition
They let a function or type work over many types while preserving relationships between inputs and outputs, avoiding the loose any type
What are generics in TypeScript? Explain the concept of constraints in generics using the extends keyword and why they are useful for creating reusable components.
Select the correct answer
extends forces a type parameter to inherit from a base class, so only subclasses of that class may be passed to the component
extends merges two type parameters into one intersection, so the component must receive values satisfying both shapes at the same time
extends widens a type parameter to accept any type at all, which removes constraints and lets the component handle every possible input
extends constrains a type parameter to types assignable to a given shape, so the body can safely access the guaranteed properties
What does the keyof operator do, and how is it typically used in conjunction with generics?
Select the correct answer
Produces a union of a type's property values, used as V extends valueof T in generics
Creates a new type by mapping each property of an existing type to an optional one
Produces a union of a type's property names, used as K extends keyof T in generics
Extracts the runtime keys of an object during execution and stores them as an array
How do default type parameters work in generics, and when are they useful?
Select the correct answer
A generic can alias another type like <T as string>, used to rename the parameter
A generic can infer a type from runtime values, used when no value is passed in
A generic can specify a fallback like <T = string>, used when the argument is omitted
A generic can constrain a type like <T extends string>, used to limit the argument
What are Mapped Types, and how do they allow you to create new types based on the properties of an existing type?
Select the correct answer
They merge two existing types using & to build a new combined intersection type
They convert a runtime object using typeof to build a precise literal type from it
They filter a type's keys using extends clauses to build a narrowed subset type
They iterate over a type's keys using [K in keyof T] to build a new transformed type
Explain the keyof operator and the typeof operator. How do they work together to create types based on existing object structures?
Select the correct answer
typeof captures a value's type and keyof its keys, e.g. keyof typeof obj
typeof captures a type's keys and keyof its values, e.g. typeof keyof obj
keyof captures a value's type and typeof its keys, e.g. typeof keyof obj
typeof returns a runtime string and keyof its length, e.g. keyof typeof obj
Explain the satisfies operator introduced in TS 4.9. How does it differ from a standard type assertion or type annotation?
Select the correct answer
It forces a value into a type bypassing checks while keeping its broad declared type
It checks a value conforms to a type while keeping its narrow inferred type
It annotates a value with a type and widens it to match the declared type exactly
It converts a value to a type at runtime while preserving its original literal type
What does the as const assertion do to an object or array? How does it affect type widening?
Select the correct answer
Makes members readonly and narrows literals to exact values, preventing widening
Marks all members optional and infers each as a union with undefined
Freezes the object at runtime so its property values cannot be reassigned later
Widens literal types to their general primitives such as string and number
Explain the difference between the typeof operator in JavaScript versus its use in a TypeScript type context.
Select the correct answer
In JS it returns a runtime string; in a type context it extracts a value's static type
In both contexts it produces a static type used only at compile time for type checks
In both contexts it returns a string describing the runtime category of a value given
In JS it extracts a value's static type; in a type context it returns a runtime string
What are indexed access types (lookup types), like T[K], and how are they used?
Select the correct answer
They constrain K to be a valid key but always return the full original type T
They create a runtime object indexed by K to retrieve property values dynamically
They look up the type of a property by its key, yielding the type stored at that key
They iterate over each key of T to build a mapped type from its property values
Explain the utility types ReturnType, Parameters, and Awaited. How does each extract type information?
Select the correct answer
All three operate on promises, extracting the resolved, rejected, and pending value types
ReturnType gives the return type, Parameters each name, Awaited the async function
ReturnType gives the return type, Parameters the argument tuple, Awaited the resolved value
ReturnType gives the argument tuple, Parameters the return type, Awaited the promise
What is type widening in TypeScript, and how can you prevent it?
Select the correct answer
Narrowing a type inside a type guard; prevent it by using explicit type assertions
Expanding generics to unknown; prevent it by passing explicit type parameters
Inferring a broader type from a literal; prevent with const, as const, or annotations
Expanding union types to include undefined; prevent it with strict null checks
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
How does TypeScript help in handling null and undefined? Explain the Strict Null Checks feature and how it changes the way you write code.
Select the correct answer
null and undefined become assignable to any type, letting the compiler infer safe defaults automatically.
null and undefined are automatically narrowed away everywhere, so explicit checks become entirely unnecessary.
null and undefined throw compile errors on declaration, so variables must always be initialized immediately.
null and undefined are excluded from every type unless explicitly added, forcing you to handle them.
What is a 'Discriminated Union,' and why is it considered a best practice for handling complex state or API responses?
Select the correct answer
A union of types that share all of their fields, distinguished only by their allowed value ranges
A union combined with intersection types in order to merge all of its variants into one shape
A union where each type has fully unique property names so overlap is impossible at runtime
A union of types sharing a common literal tag field that lets TS narrow to the exact variant
What is the difference between a standard enum and a const enum? How does the choice between them affect the compiled JavaScript output?
Select the correct answer
A standard enum emits a runtime object, whereas a const enum is inlined at call sites and emits no object.
Both emit runtime objects, but a const enum additionally freezes its members to prevent later reassignment.
Neither emits runtime code, but a standard enum allows reverse mapping while a const enum forbids it.
A standard enum is inlined at usage sites, whereas a const enum emits a lookup object preserved at runtime.
Explain the difference between private, protected, and the native JavaScript #private fields.
Select the correct answer
All three are enforced at runtime, differing only in whether the member name is mangled in the output.
private and protected are erased compile-time checks, while #private fields are truly private at runtime.
All three are erased at compile time, differing only in whether subclasses may access the member directly.
private and protected are enforced at runtime, while #private fields exist only as compile-time annotations.
What is an abstract class and how does it differ from an interface?
Select the correct answer
Both exist at runtime, but an abstract class cannot be instantiated whereas an interface freely can be.
An abstract class can hold implementation and exists at runtime, while an interface is erased and type-only.
Both are erased at compile time, but an abstract class may declare constructors while an interface may not.
An interface can hold implementation and exists at runtime, while an abstract class is erased and type-only.
How does function overloading work in TypeScript compared to languages like Java or C#?
Select the correct answer
You declare each signature in a separate interface, and the compiler merges them into methods like C#.
You declare multiple signatures over one implementation; there are no separate bodies, unlike Java or C#.
You annotate one signature with overload decorators, and the runtime selects the body as Java or C# does.
You write multiple implementation bodies, and the compiler emits runtime dispatch much like Java or C#.
What are the trade-offs of using enum versus a const object or a union of string literals? Why do some teams ban the use of Enums entirely?
Select the correct answer
Enums lack exhaustiveness checks that unions provide, so banning them removes a redundant safety guarantee.
Enums are fully type-checked but string unions always need extra explicit runtime validation to work safely.
Enums emit extra runtime JavaScript, while string unions and const objects are erased, so teams avoid them.
Enums cannot hold string values at all, which forces teams to migrate toward const objects instead.
When typing React components, what is the difference between React.ReactNode and React.ReactElement?
Select the correct answer
ReactNode is anything renderable like strings, numbers, or null; ReactElement is a single element object.
ReactNode and ReactElement are identical aliases whose difference is purely stylistic in modern typings.
ReactNode represents browser DOM nodes, while ReactElement represents virtual component instances at runtime.
ReactElement accepts strings, numbers, and arrays, while ReactNode only accepts a single JSX element object.
Why is it generally discouraged to use React.FC (FunctionComponent) in modern React-TypeScript projects?
Select the correct answer
It prevents components from using hooks, forcing them back into class components for any stateful logic.
It breaks server-side rendering by attaching runtime metadata that cannot be serialized into plain HTML.
It implicitly added a children prop and complicates generic components, offering little real benefit.
It disables prop type inference entirely, requiring every prop to be cast manually at each call site.
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.