跪拜 Guibai
← All articles
Frontend · TypeScript

is-kit Passes 100 Stars With New APIs, Stricter Guards, and a Merged Effect Fix

By nyaomaru ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

The NoInfer pattern matters beyond this library: any generic API that takes a predicate should stop the predicate's input type from redefining an already-inferred type parameter. And is-kit's milestone is a concrete example that small, composable guards can gain traction without growing into a schema framework.

Summary

is-kit, a small TypeScript library for composable runtime type guards, passed 100 GitHub stars. The 50-star milestone was built around production use in an app serving more than 100,000 users. Between 50 and 100, the library added refineKey, refineDefinedKey, and refineIndex, which apply an already-trusted guard to an object property or array element. v1.15.0 also introduced an exhaustive discriminatedUnion builder that checks at compile time for missing branches, extra branches, incompatible guards, and incomplete discriminant tuples, so a guard cannot silently lag behind a changing union type.

Stricter behavior came from fixes to recordOf literal-key narrowing, native Error brand checks, arrays with overridden iterators, exact struct validation, symbol-keyed struct schemas, and cross-realm struct schemas. Package tests now run against the published package from ESM and CommonJS TypeScript consumers, with Deno/JSR smoke tests as well.

A user report produced the most interesting result: combining is-kit's unknown-based refinement with Effect.catchIf could widen the error channel to unknown. The report led to a merged Effect PR that changed the refinement input to Predicate.Refinement<NoInfer<E>, EB>, with the same fix applied to Channel and Stream. is-kit also began showing up in other OSS projects, including changelog-bot, where define and oneOfValues keep small predicates composable.

Takeaways
— is-kit reached 100 GitHub stars; at 50 stars it was already used in production TypeScript apps serving more than 100,000 users.
— Three property-refinement helpers arrived: refineKey, refineDefinedKey, and refineIndex, for applying an existing guard to a property or array element.
— v1.15.0 added an exhaustive discriminatedUnion builder; at compile time it rejects missing or extra branches, guards incompatible with a branch, and incomplete discriminant tuples.
— Fixes since 50 stars include recordOf literal-key narrowing, native Error brand checks, array guards with overridden iterators, exact struct validation, symbol-keyed struct schemas, and cross-realm struct schemas.
— Package-level tests now cover published output from both ESM and CommonJS TypeScript consumers, plus Deno/JSR smoke tests.
— changelog-bot uses is-kit's define and oneOfValues to keep changelog section predicates reusable.
— A user's Effect.catchIf report produced merged PR Effect-TS/effect#9252; the refinement input changed to Predicate.Refinement<NoInfer<E>, EB>, with Channel and Stream fixed the same way.
— The library's stated core question is whether an unknown value can safely enter a given branch, not whether schema libraries are obsolete.
Conclusions

A refinement written over unknown can become an inference hazard inside generic APIs; the merged Effect fix shows that a predicate's input type should not participate in inferring a type parameter the API already constrains. That is a general lesson for TypeScript library design, not a one-off patch.

The exhaustive discriminatedUnion attacks a real failure mode: TypeScript types evolve while hand-written guards silently lag. Moving coverage checks into the builder is worth more than the runtime validation alone.

External use carries the most useful evidence in this milestone: users filed issues, compared validators, and triggered a cross-library bug report. That kind of use is exactly what internal testing cannot produce.

Concepts & terms
NoInfer
A TypeScript utility type that tells the compiler not to use a value's type as a candidate for inferring a generic parameter. In the Effect fix, it stops a catchIf refinement's input from widening the error channel.
Discriminated union
A union of object types distinguished by a literal property, such as { ok: true; value: string } | { ok: false; error: string }. is-kit's discriminatedUnion validates both the runtime value and, at compile time, whether the validator covers the whole union.
Cross-realm objects
Objects created in a different JavaScript realm, such as an iframe, worker, or vm context. They can fail normal instanceof or brand checks because their built-in prototypes differ, which is why struct schemas need explicit cross-realm handling.
From the discussion
Featured comments
小编

Congratulations! The type-narrowing part was explained quite clearly.

See top comments, translated →
Source: juejin.cn ↗ Google Translate ↗ Backup ↗