The idea
Build small guards.
Compose them.
Reusable runtime checks, trustworthy types, and ordinary TypeScript control flow.
Design principles and non-goals
A practical reference for deciding what belongs in is-kit, and why.
The principles at a glance
01 / Make guards trustworthy
A guard connects a runtime check to TypeScript narrowing. When it returns true, the narrowed type must accurately describe the value.
The guard contract
At runtime
guard(value) === true
Then you can trust
The value fits its narrowed type.
That promise must hold at edge cases as well as on ordinary inputs. Prefer soundness over surprising or unsafe acceptance, and test both runtime and type behavior.
02 / Prefer small, composable guards
Small predicates can be understood, tested, reused, and combined independently. Each piece below has one job: check a string, allow null, or check every array element.
import { arrayOf, isString, nullable } from 'is-kit';const isNullableString = nullable(isString);const isList = arrayOf(isNullableString);isList(['hello', null]); // trueisList(['hello', 42]); // false
Prefer existing primitives when composition expresses the use case clearly. A larger abstraction should solve a real problem that composition alone does not address well.
03 / Preserve natural TypeScript narrowing
Guards should work with ordinary control flow and APIs such as filter and find. Preserve useful information from existing types: refineKey narrows a property while retaining its parent type. See the property refinement guide for the details.
04 / Keep the core small and dependency-free
Every public API adds concepts to learn and a contract to maintain. New surface area should earn its place. Better documentation, composition patterns, tests, performance, or edge-case handling may solve a problem without another abstraction.
05 / Keep runtime behavior explicit
A guard should be understandable as a predicate on the value it receives. Avoid hidden coercion or transformation. When a helper performs a separate operation, such as decoding JSON before checking it, make that behavior explicit in its contract.
Where is-kit draws the line
What we focus on
- Reusable type guards
- Composition of small checks
- Natural TypeScript narrowing
- Predictable runtime predicates
Non-goals
- Rich structured validation error trees
- Transformation or coercion pipelines
- Schema generation
- Becoming a full schema-validation framework
Zod, Valibot, and ArkType address broader schema-oriented needs. They can coexist with is-kit where those needs arise.
Evaluating a change
Use this path for features, fixes, and public API proposals. The questions guide a discussion; they are not an automatic approval.
| Step | Question |
|---|---|
| 1. Start with the use case | Does it make reusable guards easier to build, compose, or use? |
| 2. Try composition | Can existing APIs already express the idea clearly? |
| 3. Check the contract | Do runtime behavior and narrowing agree, including at edge cases? |
| 4. Consider the fit | Does it work naturally with TypeScript, and justify its public surface? |
Consider the boundary
Does the idea fit the core, belong in an optional layer, or move the project toward a different category of library? Rich errors, transformations, and schema generation are generally outside the core.
Put the principles to work
Build small guards. Compose them. Then use them where they help.