TypeScript Risk Assessment & Management 4 — Questions and Answers
Question 1: A developer writes an async function that can throw, but callers never await it. Which TypeScript configuration helps surface this risk?
- Enabling the ESLint @typescript-eslint/no-floating-promises rule alongside strict mode (Correct answer)
- Setting target to ES2017
- Enabling declaration maps
- Using module: commonjs
Correct answer: Enabling the ESLint @typescript-eslint/no-floating-promises rule alongside strict mode
TypeScript alone does not flag unawaited promises; the no-floating-promises ESLint rule catches cases where a Promise is silently discarded.
Question 2: Which type guard pattern provides the safest runtime risk mitigation when consuming data from an external API?
- A user-defined type guard with runtime validation (e.g., using zod or manual checks) (Correct answer)
- Casting the response directly with 'as ApiResponse'
- Declaring the fetch return type as 'any'
- Using interface merging to extend the Response type
Correct answer: A user-defined type guard with runtime validation (e.g., using zod or manual checks)
Runtime validation libraries like zod verify that external data actually matches the expected shape, catching schema mismatches before they propagate through the app.
Question 3: What is the risk of declaring a variable with 'let' but never reassigning it in a TypeScript project?
- It signals intent incorrectly; using 'const' makes mutability guarantees clear and reduces accidental reassignment bugs (Correct answer)
- It causes a memory leak in all JavaScript engines
- It prevents TypeScript from narrowing the variable's type
- It disables destructuring for that variable
Correct answer: It signals intent incorrectly; using 'const' makes mutability guarantees clear and reduces accidental reassignment bugs
Using const for values that never change communicates immutable intent and allows TypeScript and engines to optimize, while let implies (misleading) mutability.
Question 4: A library exported interface is extended by consumers via module augmentation. What versioning risk does this introduce for the library author?
- Adding a required property to the interface in a minor version is a breaking change for consumers who augmented it (Correct answer)
- Module augmentation prevents the library from being tree-shaken
- Augmented interfaces cannot be used with generics
- It forces consumers to use namespace imports
Correct answer: Adding a required property to the interface in a minor version is a breaking change for consumers who augmented it
If a library adds a required property to an interface that consumers have augmented, those consumers must add the property too, making the change breaking despite a minor version bump.
Question 5: Which approach best mitigates the risk of type drift between a TypeScript frontend and a backend API over time?
- Generating shared TypeScript types from a single source of truth such as an OpenAPI spec or Protobuf schema (Correct answer)
- Manually copying interfaces between projects weekly
- Using 'any' for all API response types
- Disabling strict mode on API boundary files
Correct answer: Generating shared TypeScript types from a single source of truth such as an OpenAPI spec or Protobuf schema
Code generation from a shared contract ensures both sides always agree on types, eliminating drift caused by independent manual updates.
Question 6: When using mapped types to transform an existing type, what risk arises from using '-?' to remove optionality on all properties?
- Consumers of the mapped type may receive compile errors if they cannot provide all formerly-optional values (Correct answer)
- The mapped type silently becomes a union type
- It disables index signatures on the resulting type
- It prevents the type from being used in generics
Correct answer: Consumers of the mapped type may receive compile errors if they cannot provide all formerly-optional values
Removing optionality with -? makes every property required, which can break existing code that previously omitted those properties.
Question 7: Which TypeScript feature can be used to prevent a numeric ID type from being accidentally passed where a string ID type is expected, reducing mis-wiring risk?
- Branded (nominal) types using intersection with a unique tag (Correct answer)
- Using number literals for all IDs
- Enabling strictBindCallApply
- Declaring both types as type aliases of the same base type
Correct answer: Branded (nominal) types using intersection with a unique tag
Branding adds a phantom property that distinguishes structurally identical types, so passing a numeric brand where a string brand is required causes a compile error.
A developer writes an async function that can throw, but callers never await it.
Which TypeScript configuration helps surface this risk?