m05-type-driven
Uses type-state patterns and marker traits to replace runtime checks with compile-time validations for valid data states.
Install
mkdir -p .claude/skills/m05-type-driven && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/8301" && unzip -o skill.zip -d .claude/skills/m05-type-driven && rm skill.zipInstalls to .claude/skills/m05-type-driven
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
CRITICAL: Use for type-driven design. Triggers: type state, PhantomData, newtype, marker trait, builder pattern, make invalid states unrepresentable, compile-time validation, sealed trait, ZST, 类型状态, 新类型模式, 类型驱动设计Key capabilities
- →Encode business invariants into types
- →Implement state-machine pattern variations
- →Replace runtime primitives with type-safe wrappers
- →Utilize marker traits for compile-time enforcement
- →Identify opportunities for Zero-Sized Types (ZSTs)
How it works
It applies a checklist of type-driven strategies to convert runtime conditional checks into compile-time valid states, using constructs like PhantomData and newtypes.
Inputs & outputs
When to use m05-type-driven
- →Implementing the type-state pattern
- →Replacing primitive obsession with newtypes
- →Creating marker traits for compile-time safety
- →Designing complex state machines
About this skill
Type-Driven Design
Layer 1: Language Mechanics
Core Question
How can the type system prevent invalid states?
Before reaching for runtime checks:
- Can the compiler catch this error?
- Can invalid states be unrepresentable?
- Can the type encode the invariant?
Error → Design Question
| Pattern | Don't Just Say | Ask Instead |
|---|---|---|
| Primitive obsession | "It's just a string" | What does this value represent? |
| Boolean flags | "Add an is_valid flag" | Can states be types? |
| Optional everywhere | "Check for None" | Is absence really possible? |
| Validation at runtime | "Return Err if invalid" | Can we validate at construction? |
Thinking Prompt
Before adding runtime validation:
-
Can the type encode the constraint?
- Numeric range → bounded types or newtypes
- Valid states → type state pattern
- Semantic meaning → newtype
-
When is validation possible?
- At construction → validated newtype
- At state transition → type state
- Only at runtime → Result with clear error
-
Who needs to know the invariant?
- Compiler → type-level encoding
- API users → clear type signatures
- Runtime only → documentation
Trace Up ↑
When type design is unclear:
"Need to validate email format"
↑ Ask: Is this a domain value object?
↑ Check: m09-domain (Email as Value Object)
↑ Check: domain-* (validation requirements)
| Situation | Trace To | Question |
|---|---|---|
| What types to create | m09-domain | What's the domain model? |
| State machine design | m09-domain | What are valid transitions? |
| Marker trait usage | m04-zero-cost | Static or dynamic dispatch? |
Trace Down ↓
From design to implementation:
"Need type-safe wrapper for primitives"
↓ Newtype: struct UserId(u64);
"Need compile-time state validation"
↓ Type State: Connection<Connected>
"Need to track phantom type parameters"
↓ PhantomData: PhantomData<T>
"Need capability markers"
↓ Marker Trait: trait Validated {}
"Need gradual construction"
↓ Builder: Builder::new().field(x).build()
Quick Reference
| Pattern | Purpose | Example |
|---|---|---|
| Newtype | Type safety | struct UserId(u64); |
| Type State | State machine | Connection<Connected> |
| PhantomData | Variance/lifetime | PhantomData<&'a T> |
| Marker Trait | Capability flag | trait Validated {} |
| Builder | Gradual construction | Builder::new().name("x").build() |
| Sealed Trait | Prevent external impl | mod private { pub trait Sealed {} } |
Pattern Examples
Newtype
struct Email(String); // Not just any string
impl Email {
pub fn new(s: &str) -> Result<Self, ValidationError> {
// Validate once, trust forever
validate_email(s)?;
Ok(Self(s.to_string()))
}
}
Type State
struct Connection<State>(TcpStream, PhantomData<State>);
struct Disconnected;
struct Connected;
struct Authenticated;
impl Connection<Disconnected> {
fn connect(self) -> Connection<Connected> { ... }
}
impl Connection<Connected> {
fn authenticate(self) -> Connection<Authenticated> { ... }
}
Decision Guide
| Need | Pattern |
|---|---|
| Type safety for primitives | Newtype |
| Compile-time state validation | Type State |
| Lifetime/variance markers | PhantomData |
| Capability flags | Marker Trait |
| Gradual construction | Builder |
| Closed set of impls | Sealed Trait |
| Zero-sized type marker | ZST struct |
Anti-Patterns
| Anti-Pattern | Why Bad | Better |
|---|---|---|
| Boolean flags for states | Runtime errors | Type state |
| String for semantic types | No type safety | Newtype |
| Option for uninitialized | Unclear invariant | Builder |
| Public fields with invariants | Invariant violation | Private + validated new() |
Related Skills
| When | See |
|---|---|
| Domain modeling | m09-domain |
| Trait design | m04-zero-cost |
| Error handling in constructors | m06-error-handling |
| Anti-patterns | m15-anti-pattern |
When not to use it
- →Prototyping where rapid iteration outweighs type safety
- →Simple CRUD applications with no complex business rules
Limitations
- →Non-invocable (design guidance only)
- →Can lead to complex type hierarchies if over-engineered
- →Increases initial development time for boilerplate types
How it compares
It forces the logic into the type system phase, preventing the existence of invalid states that generic approaches allow to persist until runtime.
Compared to similar skills
m05-type-driven side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| m05-type-driven (this skill) | 0 | 6mo | No flags | Advanced |
| organize-modules | 1 | 2mo | Review | Intermediate |
| m06-error-handling | 1 | 6mo | Review | Intermediate |
| m04-zero-cost | 0 | 6mo | No flags | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by actionbook
View all by actionbook →You might also like
organize-modules
r3bl-org
Apply private modules with public re-exports pattern for clean API design. Includes conditional visibility for docs and tests. Use when creating modules, organizing mod.rs files, or before creating commits.
m06-error-handling
actionbook
CRITICAL: Use for error handling. Triggers: Result, Option, Error, ?, unwrap, expect, panic, anyhow, thiserror, when to panic vs return Result, custom error, error propagation, 错误处理, Result 用法, 什么时候用 panic
m04-zero-cost
actionbook
CRITICAL: Use for generics, traits, zero-cost abstraction. Triggers: E0277, E0308, E0599, generic, trait, impl, dyn, where, monomorphization, static dispatch, dynamic dispatch, impl Trait, trait bound not satisfied, 泛型, 特征, 零成本抽象, 单态化
minimize-rust-ffi-crate-surface
RediSearch
Remove Rust-defined C symbols that are either unused or only used in C/C++ unit tests.
upgrade-oxc
rolldown
Upgrade oxc, run codegen, and fix any breaking changes.
clean-code
gregoire78
Apply clean code practices in this repository with small, safe refactors, explicit naming, reduced complexity, and behavior-preserving changes. Use for readability improvements, technical debt cleanup, and maintainability reviews in Rust code.