Handling `unknown` in TypeScript… isn't it painful?

Handling `unknown` in TypeScript… isn't it painful?

1 2 18
calendar_todayschedule1 min read
— Originally published at dev.to

Hi there 👋

I'm a frontend engineer based in the Netherlands, currently suffering from hay fever 😿

API responses, form inputs, external data…

In TypeScript, we often end up dealing with unknown,
and handling it properly can become a real pain in day-to-day work.

Image description

Yes, unknown has a kind of gravitational pull like the universe.

But still…
we do want to handle types safely, right?

That’s where is-kit comes in a library for composing type guards.

Image description

https://github.com/nyaomaru/is-kit

Libraries like Zod take a schema-first approach.

In contrast, is-kit focuses on narrowing types within your existing code.

Instead of “writing validation”, you can think of it as extending your everyday if statements with type safety.

If Zod is for boundaries (API / input)
is-kit is for inside your application logic


A simple example

Imagine you need to check “strings with length ≤ 3” multiple times.

With is-kit, you can define it once and reuse it:

import { define, isString } from "is-kit";

const isShortString = define<string>(
  (value) => isString(value) && value.length <= 3,
);

Then use it like this:

import { isShortString } from "~/utils/is";

declare const input: unknown;

// before → repeating conditions every time
if (typeof input === "string" && input.length <= 3) {
  input.toUpperCase();
}

// after → reusable guard
if (isShortString(input)) {
  input.toUpperCase();
}

Image description

This style works seamlessly with
if, filter, map, and other existing logic.


🐾 How is-kit has evolved

It’s been about 6 months since the v1.0 release.

Back then, is-kit started as a lightweight type guard library
with primitives like:

  • define
  • and
  • or
  • struct
  • arrayOf

Since then, up to v1.6, it has gradually evolved into something more practical:

👉 a toolkit for handling unknown values in real-world applications

Let’s look at 5 major improvements.


🪄 1. Distinguishing “missing” vs “undefined” in struct

A common situation in API responses:

  • A key is missing entirely
  • A key exists but its value is undefined

These are not the same.

With v1.5.0, optionalKey(...) was introduced:

import { isString, optional, optionalKey, struct } from "is-kit";

const isUser = struct({
  id: isString,
  nickname: optionalKey(isString),
  displayName: optionalKey(optional(isString)),
});

This allows you to express both cases explicitly.


🔑 2. Key-based narrowing (hasKey, hasKeys, narrowKeyTo)

From v1.1.13 and v1.4.0, key-based utilities were added:

import {
  hasKeys,
  narrowKeyTo,
  oneOfValues,
  struct,
  isString,
  isNumber,
} from "is-kit";

const isUser = struct({
  id: isString,
  age: isNumber,
  role: oneOfValues("admin", "guest", "trial"),
});

const hasRoleAndId = hasKeys("role", "id");
const byRole = narrowKeyTo(isUser, "role");
const isGuest = byRole("guest");

This lets you build on top of existing guards instead of redefining everything.


🧪 3. assert for fail-fast validation

Added in v1.2.0:

import { assert, isString } from "is-kit";

declare const input: unknown;

assert(isString, input, "input must be a string");
input.toUpperCase();

You can now use guards in a fail-fast style when needed.


✨ 4. Support for Set and Map

With v1.6.0:

import { mapOf, setOf, isString, isNumber } from "is-kit";

const isTags = setOf(isString);
const isScores = mapOf(isString, isNumber);

Not everything is an array in real-world apps —
this expands coverage to common JS data structures.


🥏 5. Handling numeric edge cases

From v1.1.x:

  • isInteger
  • isSafeInteger
  • isPositive
  • isNegative
  • isNaN
  • isInfiniteNumber
  • isZero

These help you express “valid numbers” more precisely.


🌟 More features

There are more utilities available —
feel free to explore the docs:

{% embed https://is-kit-docs.vercel.app/en %}


🎯 Summary

is-kit started as a small, composable type guard library.

Over time, it has evolved into:

👉 a practical toolkit for handling unknown in application code

Key improvements:

  • More expressive object schemas (optionalKey)
  • Better key-based narrowing
  • Fail-fast assertions
  • Collection support (Set, Map)
  • Rich numeric guards

The goal of is-kit is simple:

👉 Make type-safe code feel natural to write

If you try it and have ideas or feedback, feel free to share!

See you in the next post 👋

https://github.com/nyaomaru/is-kit

Part 2 of 2 in is-kit
🔥 Join developers growing publicly
Share your knowledge, build in public, and grow your developer presence with a global community.

More Posts

TypeScript Complexity Has Finally Reached the Point of Total Absurdity

Karol Modelski - Apr 23

The Sovereign Vault — A Comprehensive Guide to Protocol-Driven AI

Ken W. Algerverified - Jun 4

Your Tech Stack Isn’t Your Ceiling. Your Story Is

Karol Modelski - Apr 9

Merancang Backend Bisnis ISP: API Pelanggan, Paket Internet, Invoice, dan Tiket Support

Masbadar - Mar 13

I Wrote a Script to Fix Audible's Unreadable PDF Filenames

snapsynapseverified - Apr 20
chevron_left
350 Points21 Badges
Heerlen, the Netherlandsnyaomaru-portfolio.vercel.app
9Posts
4Comments
5Connections
Funny Frontend Engineer! ? Living in the Netherlands.

Commenters (This Week)

1 comment
1 comment
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!