Skip to content
matthewcm.dev

    ↑ ↓ move · ↵ open · tab scopeopen graph →

    TypeScript as a Team Contract

    In 2020 I sold TypeScript as documentation you get for free. Since then I've migrated a large codebase to zero type errors. Types are a contract between people as much as between functions, but only if the build enforces them.

    Growingtended planted 2 min read
    On this page

    My 2020 post on TypeScript made three claims: types are documentation that lives with the code, they make onboarding easier because a new collaborator can see what every function expects, and you can adopt them gradually without converting a codebase in one go.

    All three hold up. What I didn’t understand then is the condition that makes them true.

    Types only help if they’re enforced

    A few years later at Sky I worked on a large React codebase that had been “migrated” to TypeScript. The files were .tsx, but the compiler reported a long backlog of errors, so type checking was switched off in the build. Nobody could tell a new error from the old noise, and the types had quietly stopped meaning anything.

    I took it to zero through months of small, non-functional merge requests, then turned type checking on in the build so a type error failed the pipeline (Driving a TypeScript Codebase to Zero Errors). That’s when the 2020 claims came true: the types became trustworthy documentation, reviewers stopped arguing about things the compiler could settle, and new code got real feedback.

    The lesson: gradual adoption is only safe if you ratchet. Every cleaned area should get enforcement, and lint rules should stop new errors creeping back in.

    A contract between people

    The idea I’d add to the 2020 post is that types are a contract between people. A function signature tells the next developer what they may rely on. An API response type tells the front end what the back end has promised. When those types are generated from an API specification, they become a contract the two teams can check automatically (API Modelling).

    Where it gets weak is at the boundaries. A type says what you expect the server to send; it doesn’t check what actually arrives. For data from outside your process, I now validate at runtime (a schema library) and derive the static type from the same schema, so the two can’t drift.

    Habits that keep types honest

    • No blanket any. Use unknown and narrow it. If you must cast, make it searchable and leave a reason.
    • Prefer unions to enums. as const objects and string unions erase cleanly and read well.
    • Type the edges first. API responses and shared helpers are where one fix clears fifty errors.
    • Strict mode for new code, even if old code isn’t there yet.
    • Let types shape the design. If something is hard to type, it’s often hard to understand.

    And with AI in the loop

    Strong types are also one of the best feedback sensors for coding agents. An agent that runs tsc can correct its own mistakes before a human reviews them. The stricter the types, the less steering it needs, which is exactly the feedback-loop thinking in TDD as a Feedback Loop.


    Originally written in 2020 → TypeScript

    • #typescript
    • #type-safety
    • #code-quality
    • #documentation
    • #migration

    ↪ Links to (3)