DEV Community

Cover image for TypeScript =/= SOLID
Horace Nelson
Horace Nelson

Posted on

3 1 1

TypeScript =/= SOLID

“Design is about managing change. The cost of change increases with the number of reasons code has to change.” — Sandi Metz, Practical Object-Oriented Design

The Single Responsibility Principle (SRP, or the “S” in “SOLID”) is often cited but poorly understood. It doesn’t say “do one thing.” It says: a module should have one reason to change. That phrase is precise. It reframes SRP not as a matter of functional scope, but of change triggers.

This applies not just to runtime behavior, but to source code. SRP is about scope of responsibility. A change is only appropriate if it falls within what the module is meant to handle. When a module must change to satisfy both domain logic and tooling rules, that’s a sign those concerns are improperly coupled. That’s a violation.

“When you write a software module, you want to make sure that when changes are requested, those changes can only originate from one person, or rather, one tightly-coupled group of stakeholders.” — Robert C. Martin, Clean Architecture

TypeScript introduces a second reason to change

Two people debating SRP vs TypeScript

TypeScript is a static type system. It doesn’t add behavior. It doesn’t affect runtime. Its value is entirely in compile-time enforcement. And yet, satisfying the TypeScript compiler requires changes to application code. Inline annotations, type declarations, structural rewrites, complex generic definitions, and rigid configuration constraints — all of this modifies source files that would otherwise work perfectly in JavaScript. This is application code authored for the type system, not for the application’s purpose.

This introduces a second reason to change the module: compliance with a compile-time validator. That validator doesn’t exist in production. It doesn’t add safety at runtime. It isn’t required for the application to function. So why are we modifying the application logic to satisfy it?

Some will argue that TypeScript isn’t adding external syntax at all, that it’s the base language, and JavaScript is just a subset of it. But this reframing is a gaslight. Developers use TypeScript to build JavaScript applications. The runtime target is JavaScript. The logic is JavaScript. The type system is the addition. Anders Hejlsberg, the lead architect of TypeScript, has said himself that it’s “a tool to make JavaScript development safer.” That makes it extrinsic by design. So whether or not TypeScript is technically a superset, the reality is that developers are forced to modify their application syntax and logic to satisfy a tool that exists purely outside the runtime.

The cost in real code

A person throwing bags of money into the gears of a giant machine

This isn’t an abstract nitpick. In a large codebase, the amount of code written purely to appease the type system is staggering. This added weight bears real costs in both maintenance time spent and levels of effort, and those translate to real $$$. Entire hierarchies of types, overloads, inferred utilities, and workaround constructs exist solely to satisfy the compiler. It’s not auxiliary code. It’s embedded in the modules themselves.

That’s the tell. As touched on earlier:

When business logic must be rewritten, rearranged, or wrapped only to make the tooling happy, the application code now serves two masters. One of them is the domain. The other is the infrastructure.

Intrusion vs Observation

To be clear, this isn’t an argument against type safety. It’s a critique of how TypeScript intrudes on domain logic. They offer feedback or enforce constraints, but they don’t require you to embed their rules into your logic. TypeScript does. They surround the logic. TypeScript infiltrates it.

How is this different from Test-Driven Development?

Some will claim this is no different than test-driven development. In both cases, you write code to pass a test. But that comparison falls apart quickly:

  1. Tests live outside the module. Tests don’t alter your application logic just to pass. They test it from the outside.
  2. Tests prove what your code does. TypeScript only guesses. Tests run your code and confirm its behavior. TypeScript checks types. That’s prediction, not proof.
  3. Tests can be deleted. The app still runs. If you remove TypeScript, your code will likely no longer build.
  4. Tests help you explore. TypeScript forces conformance. TDD helps evolve design through feedback. TypeScript enforces constraints by static rules.

Test-driven development guides design through optional, external tests. TypeScript demands conformance inside your application logic. They are not the same.

But while TypeScript isn't TDD, it is testing...

Isn’t syntax checking the same thing?

Some will compare this to syntax validation. That’s a dodge at best. Syntax rules are intrinsic to the language. They validate that code can execute. They care nothing about safety, intent, or correctness. TypeScript, by contrast, is not validation. It is static testing, an assertion framework for types. It forces you to write extra application code not to make the application work, but to make the type system agree that it will.

I already know what some of you are thinking: something like “TypeScript isn’t testing!” or even “type-checking isn’t testing!” That point is pedantic when, functionally, TypeScript enforces a static test suite via annotations injected directly into source code. As Anders Hejlsberg, himself, said: “The [TypeScript] compiler is just another test.” That’s unassailable testimony against any claim that TypeScript isn’t a testing framework. I rest my case.

Bringing it back to SRP

If a module must be rewritten for both behavioral requirements and tooling compliance, it has two reasons to change. It no longer has a single, clean responsibility. The logic is entangled with infrastructure.

Any tool that forces change in core application code without adding runtime capability breaks that principle. It’s not a stylistic debate. It’s an architectural cost, and a high one.

And SRP has something to say about that.

Warp.dev image

The best coding agent. Backed by benchmarks.

Warp outperforms every other coding agent on the market, and gives you full control over which model you use. Get started now for free, or upgrade and unlock 2.5x AI credits on Warp's paid plans.

Download Warp

Top comments (0)

👋 Kindness is contagious

Explore this insightful write-up embraced by the inclusive DEV Community. Tech enthusiasts of all skill levels can contribute insights and expand our shared knowledge.

Spreading a simple "thank you" uplifts creators—let them know your thoughts in the discussion below!

At DEV, collaborative learning fuels growth and forges stronger connections. If this piece resonated with you, a brief note of thanks goes a long way.

Okay