π Postmortem: TypeScript TS2688 Error Due to Global Type Inference
π Issue Summary
After a routine dependency update in the root of a monorepo, CI builds for several microservices started failing with the following TypeScript error:
error TS2688: Cannot find type definition file for 'hapi__shot'.
The file is in the program because:
Entry point for implicit type library 'hapi__shot' This error appeared despite no direct usage of @types/hapi__shot in the codebase. The issue only manifested in some environments and was reproducible via a clean dependency install.
π Root Cause
The underlying cause was TypeScriptβs default behavior of attempting to globally include all type definitions from the @types namespace, even for packages that already ship with their own types.
Specifically:
@hapi/shot, introduced transitively via OpenTelemetry, includes its own types.- TypeScript incorrectly assumed the need for a separate
@types/hapi__shotpackage, which does not exist and is deprecated. - Without a
"types"field intsconfig.json, TypeScript inferred global types from all dependencies innode_modules, triggering this error.
π‘ Diagnosis & Context
The issue was subtle because it was triggered by implicit global type inclusion, not explicit usage. It only affected environments performing fresh installs, and not all dev environments saw the issue consistently.
This behavior is also brittle: even with strict version pinning of Node and npm, future updates to dependencies could cause similar failures if global type inference remains uncontrolled.
β Solution
To scope TypeScript's type inference and eliminate this class of error, the following fix was applied:
"types": [
"node",
"jest"
]This change was rolled out across all tsconfig.json files in the monorepo. It tells TypeScript to only include type definitions for Node.js and Jest globally, avoiding accidental inclusion of unwanted or non-existent @types/* packages.
π§ Why This Works
- Most types used in the codebase are either explicitly imported or included inline by the libraries themselves.
- Only a small number of global types (like
process,describe, etc.) are used β all of which are covered bynodeandjest. - Restricting the
"types"array improves type hygiene and avoids accidental or breaking inference.
π Next Steps
To prevent similar issues in future projects, the fix was added to all internal generators:
- Microservice and npm module templates: Updated to include
"types": ["node", "jest"]by default. - CLI tool and shared libraries: Reviewed and updated to align with the new tsconfig convention.
π§° Takeaways
- Always explicitly declare global types in
tsconfig.jsonto prevent TypeScript from inferring unnecessary or deprecated type packages. - Dependency changes β even transitive ones β can introduce subtle type inference issues when TypeScript is left unscoped.
- Locking tool versions (Node, npm) may mask the issue but won't address the root cause unless paired with proper configuration of TypeScript's type resolution behavior.