Code Quality Tools Deep Dive · 4 of 6

Type Checking — The Refactor Insurance Layer

Type checkers verify that the values flowing through your program match the types you declared, before the program runs. In dynamic languages, type-checking has been the biggest productivity shift of the last decade — TypeScript reshaped JavaScript, mypy and Sorbet did the same for Python and Ruby. The benefit isn't catching weird bugs; it's making large refactors safe and IDE intelligence usable.

TypeScriptmypySorbetPyrightStatic Types
← Back to Testing
Quick Facts

What Type Checking Does

Basic Concepts

  • Statically-typed languages (Java, C#, Go, Rust, Kotlin, Swift, F#) have type checking baked into the compiler — you can't ship code that doesn't type-check.
  • Dynamically-typed languages (JavaScript, Python, Ruby, PHP) optionally bolt on a type checker as a separate tool. The type info is annotations the runtime ignores.
  • Gradual typing: add types to part of a codebase; untyped code keeps working, but the typed parts are checked. The path most teams take to migrate.
  • Soundness vs pragmatism. Some checkers (Rust, OCaml, Haskell) are sound — if it type-checks, certain classes of bugs cannot happen. Most gradual checkers (TypeScript, mypy) trade soundness for productivity; they catch most bugs without rejecting valid programs.
Why It Wins

What Type Checking Buys You

Refactoring at Scale

Rename a property used in 1,000 places. Change a function signature. Move a class. The type checker tells you every call site that no longer compiles. Without it, you're greping for usages and praying. This alone justifies the migration cost in mid-to-large codebases.

IDE Intelligence That Actually Works

Autocomplete, jump-to-definition, find-all-references, inline docs, parameter hints — all driven by types. In a fully-typed JS codebase, the IDE behaves like a Java IDE in 2010. The productivity uplift is enormous.

Catches a Class of Bugs Cheaply

Null/undefined handling, mismatched arguments, missing fields, typos in property names, accidental implicit coercions. The kind of bug that, in the dynamically-typed world, only surfaces at runtime — usually in the customer's session, not yours.

Self-Documenting

Type signatures describe what a function takes and returns without needing a comment. New contributors can read the API of a module by reading the types. Comments lie; types compile.

Tools

The Common Type Checkers

TypeScript (JS/TS)

Microsoft's open-source superset of JavaScript with structural typing and gradual adoption. Effectively the default for new JS projects in 2026 — even teams who write .js usually use TypeScript via JSDoc annotations to get IDE support. Strict mode (strict: true) catches most of what's worth catching.

Worth knowing: generics, mapped types, conditional types, discriminated unions, satisfies. The type system is expressive enough to encode real domain constraints.

mypy & Pyright (Python)

Python type hints went stable in 3.5 (2015) and have grown rapidly. mypy is the original checker; Pyright (Microsoft, behind Pylance in VSCode) is faster and stricter. Most large Python codebases use Pyright in IDEs and mypy in CI, or just Pyright everywhere now.

PEP 484, 526, 612, 695 added the modern syntax. from __future__ import annotations helps with forward references. Tools like pydantic use the type hints at runtime for validation.

Sorbet & RBS (Ruby)

Sorbet from Stripe — gradual typing with the strongest large-codebase track record in Ruby. RBS is the official Ruby community type system, with Steep as the type checker. Less universally adopted than TypeScript or mypy, but powerful when used.

PHPStan & Psalm (PHP)

Type-checkers that work on PHP's type hints plus inferred types. PHPStan and Psalm both ladder up through "levels" — start at level 1, increase strictness as you fix issues. Standard equipment in serious PHP shops.

Flow (JS) — On the Wane

Facebook's earlier alternative to TypeScript. Mostly displaced; a small number of legacy projects still use it.

Already-Static Languages

Java, C#, Go, Rust, Kotlin, Swift — type checking is the compiler. Strictness varies (Rust and Kotlin have nullability in the type system; Java has it via annotations and tools like NullAway, ErrorProne). The disciplines are the same — watch nullability, leverage generics, don't reach for Object / any / interface{} when a real type fits.

Adopting It

Migrating an Untyped Codebase

Strict Mode From the Start

For greenfield projects, turn on strictness from day one. strict: true in TypeScript, --strict in mypy, Pyright's strict mode. Adding strictness later is much harder than living with it from the start.

Gradual Adoption for Legacy

Type the new code. Type the modules under active change. Don't migrate a 100k-line codebase to types in one PR — the diff is unreviewable.

Tools: TypeScript's allowJs + checkJs + JSDoc, mypy's per-module strictness, PHPStan's levels. They all let typed and untyped coexist.

Avoid Escape Hatches

any in TypeScript, Any in Python, T.untyped in Sorbet. Useful in tight spots; corrosive when widespread. The first any you write is fine; the hundredth becomes a tradition. Lint rules to flag explicit-any on new code save the codebase.

Typed Boundaries to Untyped Code

When typed code calls untyped libraries, write a thin typed wrapper. The boundary becomes the place to validate; the rest of the codebase trusts types. Validation libraries: Zod (TS), pydantic (Python), dry-validation (Ruby).

Limitations

What Type Checking Doesn't Catch

  • Logic bugs. A correctly-typed function can still compute the wrong answer.
  • Boundary violations. Untyped network responses, untyped user input — types don't stop bad data from entering. Validate at the edge.
  • Side-effect ordering. Async timing, race conditions, ordering of effects — type systems rarely capture these (Rust's borrow checker is the famous exception).
  • Performance. Type-correct code can still be slow.

Types are a layer of cheap defense. They don't replace tests, code review, or judgment. They make the rest of those activities much more productive.

Continue

Other Code Quality Tools