TypeScript Strict Mode Configuration: Why Blossend Enforces Every Flag
How Blossend configures TypeScript strict mode across six production platforms. Compiler flags, project references, path aliases, and why strict typing catches healthcare bugs before production.
See this stack in production. bootstrapped revenue.
Free forever. Upgrade only when you're ready.
Ex-Amazon Engineer · Healthcare Innovation
No card charged today · Independent engineering · $0 to start
TypeScript strict mode is the cheapest quality assurance investment in software engineering. It costs zero runtime performance, catches entire categories of bugs at compile time, and makes refactoring safe at any scale. Here is how we configure TypeScript across Blossend's six-platform ecosystem and why every flag matters.
The tsconfig.json Philosophy
Our base tsconfig.json enables every strict flag. The "strict" compiler option is a shorthand that enables strictNullChecks, strictFunctionTypes, strictBindCallApply, strictPropertyInitialization, noImplicitAny, noImplicitThis, and alwaysStrict. But we go further. We also enable noUnusedLocals, noUnusedParameters, noFallthroughCasesInSwitch, and exactOptionalPropertyTypes.
Each flag serves a specific purpose. strictNullChecks alone prevents the most common class of JavaScript runtime errors — accessing a property on a value that is null or undefined. In a healthcare application where a null patient ID could expose someone else's medical records, this is not a convenience — it is a safety requirement.
Path Aliases
We configure the @/* path alias to map to ./src/*. This eliminates the fragile relative import chains that plague large codebases. Instead of importing from "../../../../lib/supabase-server," we write "@/lib/supabase-server". The path is absolute, readable, and does not break when files move.
Next.js 16 supports path aliases natively through tsconfig.json without additional bundler configuration. The @/* convention is so standard across the React ecosystem that every developer joining the project understands it immediately.
Project References for Monorepo
Blossend runs six applications in a monorepo structure. TypeScript project references let each application have its own tsconfig.json that extends the base configuration while adding app-specific settings. The base config enforces strict mode and common compiler options. Each app config adds its own include paths and framework-specific settings.
Project references also enable incremental compilation. When I change a file in the shared library, TypeScript only recompiles the library and the applications that import from it — not the entire monorepo. This cuts our type-checking time from 45 seconds to under 10 seconds for typical changes.
Strict Null Checks in Healthcare
The most impactful flag for healthcare applications is strictNullChecks. Consider a function that retrieves a patient record by ID. Without strict null checks, the return type might be Patient, and the caller assumes it always exists. With strict null checks, the return type is Patient | null, forcing the caller to handle the case where the patient does not exist.
In OpenMyPro, this caught a critical bug during development. A booking confirmation page was accessing patient.name without checking if the patient record existed. Without strict null checks, this would have been a runtime crash for any booking with a deleted or deactivated patient. With strict null checks, TypeScript flagged it at compile time and we added proper null handling.
noUnusedLocals and noUnusedParameters
These flags prevent dead code from accumulating. Every unused variable or parameter is a compile error. This might seem aggressive, but in practice it forces cleaner code. If a function parameter is unused, it should not be in the signature. If a variable is declared but never read, it is either a bug or unnecessary code.
We make one exception: underscore-prefixed parameters (_event, _context) are allowed to be unused. This convention signals intentional omission, such as in callback functions where the framework requires a parameter that we do not need.
Integration with ESLint
TypeScript and ESLint work together in our setup. TypeScript handles type correctness while ESLint handles code style and best practices. We use @typescript-eslint/eslint-plugin with type-aware rules that leverage TypeScript's type information for deeper analysis. Rules like no-floating-promises catch unhandled async operations that TypeScript alone would miss.
Our ESLint configuration is strict but pragmatic. We disable rules that conflict with our Tailwind CSS patterns and enable rules that catch real bugs in healthcare code. The CI pipeline runs both tsc --noEmit and eslint --max-warnings 0, meaning zero type errors and zero lint warnings are allowed in production builds.
The Cost of Not Using Strict Mode
Every week I see startups launch with "strict": false in their TypeScript config, planning to enable it later. They never do. The accumulated type debt becomes so large that enabling strict mode would require rewriting hundreds of files. We started with strict mode on day one and it has saved us countless hours of debugging runtime type errors that simply cannot exist in our codebase.