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

@gabrielurbina/type-guard

v0.2.5

Published

Type safety for your IO

Downloads

903

Readme

Type Guard GitHub license npm version

Bring Typescript types to your run time

This self contained library will provide you with the tools to move the type validation to the edges of your application, to your IO ring, where you can validate the data coming from outside and trust elsewhere in your application.

When to trust your data

A rather common anti-pattern in JavaScript is to check everywhere in your codebase for a value's type, we'll call this Defensive Programing. It is quite likely that you have come across some code like this:

const someFunction = (value) => {
	if (value !== undefined) throw Error("value must be defined");
	return anotherFunction(value, "I already checked value")
};
const anotherFunction = (value, message) => {
	if (value !== undefined) throw Error("value must be defined");
	return value + message;
};

Where a value is checked for its type multiple times across your application, this means that your code doesn't trust its data.

Although Paranoia Oriented Programming might sound cool, it isn't, it is far from productive or even safe.

The application code should be concerned with it's domain, function, modules or classes should not checking if the type of its parameters are valid.

However not all data is safe our code bases should trust internal data, but Must check external data for it might be either malicious, corrupted or just has the wrong format.

This validation should take place in the outermost interfaces of our application what we'll call the IO Layer.

The IO layer exist in different places depending on the application:

  • In libraries or frameworks: the public methods and functions
  • In a server application: HTTP and database requests
  • In a web application: HTTP responses, local-storage and the user's input

Wherever your application meets external data

Performing all the data validation on this layer will allow allow the core application to trust the data is using, allowing you to remove redundant type-checks and unclutter your code.

Type guards

A type guard is a function that checks a value and returns true if the value is of a given type:

type Guard<T extends V, V = unknown> = (v: V) => v is T;

For example this fuction checks that the unkown value is one valid Grade

type Grade = "A" | "B" | "C" | "D" | "F"; 
const isGrade = (value: unknown): value is Grades =>  ["A","B","C","D","F"].includes(value)

Naming: Type guards start with is and then the type they check

This library provides these Type-Guards

Guard factories

Guard factories are functions that return a type guard

This library provides these Guard Factories

High order guards

High order guards are functions that receive type-guards and return another type-guard of a composite type:

const ArrayOf= <T>(type: Guard<T>) => {
	return (arr: unknown): arr is Array<T> => Array.isArray(arr) && arr.every((v) => type(v));
};

Naming: High order guards start with the base type end with Of

This library provides these High order guards

Examples

HTTP responses

When getting data from your students API type-check it before letting pass into your application:

import { ArrayOf, ObjectOf, isString, isNumber, isBoolean } from "@gabrielurbina/type-guard";

type Student = { firstName: string; lastName: string; age: number; active: boolean };

const isStudent = ObjectOf({
   firstName: isString,
   lastName: isString,
   age: isNumber,
   active: isBoolean
});

const isStudentList = ArrayOf([isStudent]);

export const fetchStudents = async (id: string): Promise<Student[]> => {
   // Remember to treat external data as unknown
   const response: unknown = await(await fetch(`api/students?class=${id}`)).json();
   if(!isStudentList(response)) throw Error("Invalid API response");
   return response
}

Local storage

When getting your user's stored preferences from localStorage type-check it before using it or use some default preferences if the type is invalid or there are no stored preferences:

import { ObjectOf, ValueOf, isNumber, isBoolean } from "@gabrielurbina/type-guard";

type Theme = "dark" | "light";
type Currency = "USD" | "EUR" | "GBP";

interface UserPreferences {
   theme: Theme;
   currency: Currency;
   timeZoneOffset: number;
   finishedWalkthrough: boolean;
};

const isUserPreferences = ObjectOf({
   theme: ValueOf<Theme>(["dark" , "light"]),
   currency: ValueOf<Currency>(["USD" , "EUR" , "GBP"]),
   timeZoneOffset: isNumber,
   finishedWalkthrough: isBoolean,
});

const defaultUserPreferences: UserPreferences = {
   theme: "light",
   currency: "USD",
   timeZoneOffset: 0,
   finishedWalkthrough: false,
}

export const retrieveUserPreferences = (): UserPreferences => {
   const storedPreferences = localStorage.getItem("user-preferences");
   if (storedPreferences === null) return defaultUserPreferences;
   // Treat external data as unknown
   const preferences: unknown = JSON.parse(storedPreferences);
   if(!isUserPreferences(preferences)) return defaultUserPreferences;
   return preferences;
}