type-driven-development

Encodes domain invariants in static type systems to make illegal states unrepresentable.

Updated Nov 20, 2025
One-click install
npx skills add https://github.com/apuya/react-basics-ui --skill type-driven-development-apuya
Or copy as Structured Prompt for Agent
Please help me install this Agent Skill.
Skill: type-driven-development
Source: https://github.com/apuya/react-basics-ui/tree/main/.claude/skills/type-driven-development
Command: npx skills add https://github.com/apuya/react-basics-ui --skill type-driven-development-apuya

SYSTEM DOCUMENTATION & REQUIREMENTS

What problem does it solve? Codebases often rely on conventions, comments, and runtime checks to enforce rules like "this string must be a valid email" or "a connection must be open before sending". These rules silently break as code evolves. This Skill moves those invariants into the type system so the compiler rejects illegal states on every build, eliminating whole classes of bugs and the tests that compensate for them. ## Core Features & Use Cases - Four type-level moves: Selects between newtypes/branded types (identity), smart constructors (construction), discriminated unions (state machines), and phantom types (operation ordering) based on the invariant being protected. - Code audits for type smells: Detects primitive obsession, boolean blindness, stringly-typed data, partial functions, validation-instead-of-parsing, and missing exhaustiveness checks, with the smallest fix for each. - Types vs tests decisions: Applies a four-criterion test to decide whether a check belongs in the type system or in a test suite. - Use Case: Given a RequestState object with optional data, error, and loading fields, refactor it into a discriminated union with an exhaustiveness-checked switch, then delete the now-redundant tests. ## Quick Start Ask the assistant to audit a module for primitive obsession and refactor its stringly-typed status fields into discriminated unions with exhaustiveness checks.

Frequently Asked Questions about type-driven-development

High-intent search queries and answers about installing and using this skill.

FAQPage Schema
How do I make illegal states unrepresentable in TypeScript?

Replace bags of optional fields with discriminated unions where each state is a tagged variant carrying only its valid data. Add a never-typed default in every switch over the union so the compiler flags unhandled new variants.

What is the difference between a smart constructor and validation?

Validation returns a boolean and leaves the input typed as the unrefined form, losing the proof. A smart constructor parses input and returns a refined type or an error, so every downstream function can trust the invariant without re-checking.

When should a check live in the type system versus a test?

A check belongs in the type system when it is structural, decidable at compile time, must hold on every code path, and is cheaper than testing every call site. Behavioural, IO, time-dependent, and cross-process properties belong in tests.

Does type-driven development work in Python or only TypeScript?

The same moves apply in Python using NewType for branded identities, frozen dataclasses with classmethod parsers for smart constructors, and Literal-tagged unions for state machines. Rust and Haskell support all four moves natively.

Why is a switch over a union without a never default risky?

Without a never-typed default, adding a new union variant compiles silently and the switch falls through. The assertNever default turns the omission into a compile error, forcing every branch to handle the new variant.