Model data so illegal states will not compile, then turn the checker to strict and let it prove correctness for free. Use when designing types, modeling state machines, or hardening a loosely typed module.
Scanned 9/5/2026
Install to Claude Code
npx -y skills add Amey-Thakur/AI-SKILLS --skill type-safety --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Type Safety?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/amey-thakur-type-safety)More formats (shields.io, HTML) on the badges page.
---
name: type-safety
description: Model data so illegal states will not compile, then turn the checker to strict and let it prove correctness for free. Use when designing types, modeling state machines, or hardening a loosely typed module.
---
# Type safety
A type is a proof the compiler rechecks for free on every build, but only
about the things you let it see. Model with loose types (everything a
string, every field optional, states tracked by scattered flags) and the
compiler cannot help; model tightly and whole categories of bug stop
compiling.
## Method
1. **Make illegal states unrepresentable.** Replace a `status: string` plus
a nullable `error` with a tagged union: `{ kind: 'loading' } | { kind:
'ok', data: T } | { kind: 'error', message: string }`. Now "ok with an
error message" cannot be constructed, so no code has to guard against
it.
2. **Parse, do not validate, at the edge.** Turn input into a precise type
once (`Email`, `UserId`, `NonEmptyList`) with zod, io-ts, or pydantic,
then pass that type inward. A function taking `Email` never re-checks the
`@`, because the type already carries the proof.
3. **Wrap primitives that mean different things.** A `UserId` and an
`OrderId` that are both bare `string` will get swapped eventually.
Branded types, newtypes, or `NewType` in Python make the swap a compile
error at no runtime cost.
4. **Keep required fields required.** Make a field optional only when
absence is a real, handled case. Every `?` you add is a branch every
caller inherits, so keep the mandatory fields mandatory and let the type
document what must exist.
5. **Turn the checker to strict.** `strict: true` in tsconfig, mypy
`--strict`, Kotlin explicit-API mode, the relevant `-Werror` analyses.
Half-on type checking passes files a full pass would reject, which is
worse than knowing they are unsound.
6. **Make exhaustiveness a compile error.** Switch on a union with a
`never` default (TypeScript) or a sealed `when` (Kotlin) so adding a
variant fails the build at every site that does not yet handle it. The
compiler becomes your checklist for the change.
## Checks
- Can you construct a value that is in two contradictory states at once? If
so, tighten the type.
- Does adding a new enum case break the build at every site that must
change?
- Are `UserId` and `OrderId` distinguishable to the compiler, or both just
`string`?
## Boundaries
Dynamic languages without gradual typing, and seams with untyped systems
(raw JSON, FFI, reflection), cap how much the compiler can prove: validate
at those seams instead. Type modeling has a cost, and past a point the
ceremony outweighs the bugs prevented, so stop tightening when the next
constraint costs more than the mistake it would block.

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!