Two posts about a new open-source org called Necoz are sitting in dev.to's top 20 this week — 138 reactions on the intro, 130 on the launch announcement. A cat-themed org with two small projects. I have seen a hundred launches like this, and most of them are a README and a dream. So I did the only check that matters: I installed the thing and used it.
The project is called is-kit. The pitch, from its own README: a lightweight, zero-dependency TypeScript toolkit for composing reusable type guards. Version 1.16.4. Install took one command and 425 milliseconds, and it added exactly one package. I checked — the whole thing is 232KB on disk. Zero dependencies is a claim I have seen broken before, so I verified that one first. It holds.
Here is the test I ran. A JSON payload arrives from an API, supposedly a user. I composed small guards into one user guard and threw good and bad payloads at it:
const { isString, isInteger, isPositive, isBoolean, isArray, and, struct } = require("is-kit");
const isAdultAge = and(isInteger, isPositive);
const isUser = struct({
name: isString,
age: isAdultAge,
admin: isBoolean,
tags: isArray,
});
Verbatim output:
good payload passes struct: true
negative age passes struct: false
numeric name passes struct: false
A negative age fails. A numeric name fails. The good payload passes. That is the whole job of a guard library, and it did it without ceremony. The and(isInteger, isPositive) composition is the part I actually wanted: an age that is an integer but negative is not an adult age, and the composition reads like the sentence it enforces.
The second thing I tested was refineKey, which narrows one property of an object you already checked:
refineKey name on good: true
refineKey name on badName: false
Then I made the mistake the README warns you about by not warning you at all. I called safeParse the way I assumed it worked — safeParse('{"a":1}') — and got this:
TypeError: fn is not a function
safeParse takes the guard first, then the value. The JSON version is a different function, safeJsonParse, which takes the string first. My call was wrong, not the library, but the error message did not say that. fn is not a function is the kind of error that sends you to the source code, which is where I went. Corrected, it behaves properly:
safeParse good: {"valid":true,"value":{"name":"Salam","age":30,"admin":false,"tags":["dev","ai"]}}
safeParse badAge: {"valid":false}
safeJsonParse valid: {"valid":true,"value":{"name":"Salam","age":30,"admin":false,"tags":[]}}
safeJsonParse broken JSON: {"valid":false}
safeJsonParse wrong shape: {"valid":false}
Broken JSON and wrong shapes both come back as {valid: false} instead of throwing. That is the right behaviour for an API boundary — the caller decides what to do with a failure instead of catching an explosion.
What I like about it, honestly: it is small enough to read. The entire API surface is guards you already understand — isString, isInteger, and, or, not, struct, refineKey — composed the way you would compose them by hand, except the TypeScript narrowing stays correct, which is the part that breaks when you hand-roll guards and forget a predicate. The README is upfront about what it is not: if you want rich structured validation errors or a schema-first workflow, use Zod or Valibot. This is for the utils/is.ts file every TypeScript project grows, the one full of hand-written guards that solve the same problems across projects.
What I do not like: the safeParse / safeJsonParse split is a trap for exactly the person the library is for — someone validating unknown input, who will reach for the JSON function first and find the other one. A single function that took the guard and figured out the input would be kinder. And the TypeScript narrowing, the whole reason to use this over plain checks, is something I verified only by reading the types, not by running a type error — the runtime cannot show you narrowing, and the article cannot either without a compile step I did not do. So take the narrowing claim on the strength of the type declarations, which are thorough, not on a run.
The verdict: a real tool, small, honest about its scope, from an org whose whole philosophy is "small, simple, useful." The intro post says a tiny utility can solve a real problem, and a small PR can make a project better. I installed it, ran it, broke it once by my own mistake, and it did the job in 232KB. That is a better hit rate than most launches I have tried. The cat branding is doing a lot of work, but the code holds up without it.
P.S. Everything I write stays free — that part doesn't change. But if this one was worth something to you, you can buy me a coffee ☕ (one-time or monthly, both work). It covers the API credits and hosting behind these experiments — and honestly, it's also the nudge that gets the next one started on the evenings I'd otherwise skip it. — Salam

Top comments (0)