npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

ts-type-checked

v0.6.5

Published

Type checking utilities for TypeScript.

Downloads

247

Readme

Wait what?

As they say an example is worth a thousand API docs so why not start with one.

interface WelcomeMessage {
  name: string;
  hobbies: string[];
}

//
// You can now turn this
//
const isWelcomeMessage = (value: any): message is WelcomeMessage =>
  !!value &&
  typeof value.name === 'string' &&
  Array.isArray(value.hobbies) &&
  value.hobbies.every(hobby => typeof hobby === 'string');

//
// Into this
//
const isWelcomeMessage = typeCheckFor<WelcomeMessage>();

//
// Or without creating a function
//
if (isA<WelcomeMessage>(value)) {
  // value is a WelcomeMessage!
}

Motivation

TypeScript is a powerful way of enhancing your application code at compile time but, unfortunately, provides no runtime type guards out of the box - you need to create these manually. For types like string or boolean this is easy, you can just use the typeof operator. It becomes more difficult for interface types, arrays, enums etc.

And that is where ts-type-checked comes in! It automatically creates these type guards at compile time for you.

This might get useful when:

  • You want to make sure an object you received from an API matches the expected type
  • You are exposing your code as a library and want to prevent users from passing in invalid arguments
  • You want to check whether a variable implements one of more possible interfaces (e.g. LoggedInUser, GuestUser)
  • ...

Example cases

Checking external data

Imagine your API spec promises to respond with objects like these:

interface WelcomeMessage {
  name: string;
  greeting: string;
}

interface GoodbyeMessage {
  sayByeTo: string[];
}

Somewhere in your code there probably is a function just like handleResponse below:

function handleResponse(data: string): string {
  const message = JSON.parse(data);

  if (isWelcomeMessage(message)) {
    return 'Good day dear ' + message.name!
  }

  if (isGoodbyeMessage(message)) {
    return 'I will say bye to ' + message.sayByeTo.join(', ');
  }

  throw new Error('I have no idea what you mean');
}

If you now need to find out whether you received a valid response, you end up defining helper functions like isWelcomeMessage and isGoodbyeMessage below.

const isWelcomeMessage = (value: any): value is WelcomeMessage =>
  !!value &&
  typeof value.name === 'string' &&
  typeof value.greeting === 'string';

const isGoodbyeMessage = (value: any): value is GoodbyeMessage =>
  !!value &&
  Array.isArray(value.sayByeTo) &&
  value.sayByeTo.every(name => typeof name === 'string');

Annoying isn't it? Not only you need to define the guards yourself, you also need to make sure the types and the type guards don't drift apart as the code evolves. Let's try using ts-type-checked:

import { isA, typeCheckFor } from 'ts-type-checked';

// You can use typeCheckFor type guard factory
const isWelcomeMessage = typeCheckFor<WelcomeMessage>();
const isGoodbyeMessage = typeCheckFor<GoodbyeMessage>();

// Or use isA generic type guard directly in your code
if (isA<WelcomeMessage>(message)) {
  // ...
}

Type guard factories

ts-type-checked exports typeCheckFor type guard factory. This is more or less a syntactic sugar that saves you couple of keystrokes. It is useful when you want to store the type guard into a variable or pass it as a parameter:

import { typeCheckFor } from 'ts-type-checked';

interface Config {
  version: string;
}

const isConfig = typeCheckFor<Config>();
const isString = typeCheckFor<string>();

function handleArray(array: unknown[]) {
  const strings = array.filter(isString);
  const configs = array.filter(isConfig);
}

// Without typeCheckFor you'd need to write
const isConfig = (value: unknown): value is Config => isA<Config>(value);
const isString = (value: unknown): value is Config => isA<string>(value);

Reducing the size of generated code

isA and typeCheckFor will both transform the code on per-file basis - in other terms a type guard function will be created in every file where either of these is used. To prevent duplication of generated code I recommend placing the type guards in a separate file and importing them when necessary:

// in file typeGuards.ts
import { typeCheckFor } from 'ts-type-checked';

export const isDate = typeCheckFor<Date>();
export const isStringRecord = typeCheckFor<Record<string, string>>();

// in file myUtility.ts
import { isDate } from './typeGuards';

if (isDate(value)) {
  // ...
}

Useful links

  • ts-trasformer-keys, TypeScript transformer that gives you access to interface properties
  • ts-auto-mock, TypeScript transformer that generates mock data objects based on your types