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-looks-like

v4.0.0

Published

Helper function to generate TypeScript type guards from an example instance

Downloads

71

Readme

ts-looks-like

Helper function to generate TypeScript type guards from an example instance.

This module exports a function named looksLike that can be used to generate TypeScript type guard functions based on an example instance.

For instance, suppose that you have a model object like this:

export interface INode {
    label: string;
    color: string;
    visible: boolean;
}

Creating a type guard manually is tedious:

export function isNode(obj: any): obj is INode {
    return (
        typeof obj === "object" &&
        obj.hasOwnProperty("label") &&
        typeof obj.label === "string" &&
        obj.hasOwnProperty("color") &&
        typeof obj.color === "string" &&
        obj.hasOwnProperty("visible") &&
        typeof obj.label === "boolean"
    );
}

However, you can now use looksLike to generate one from an example object:

const nodeExample: INode = {
    label: "Some label",
    color: "red",
    visible: false,
};

export const isNode = looksLike(nodeExample);

You can also inline the example object if you give a type hint to looksLike:

export const isNode = looksLike<INode>({
    label: "Some label",
    color: "red",
    visible: false,
});

In general, looksLike accepts the following objects as inputs:

  • undefined will return a type guard that accepts undefined only.

  • null will return a type guard that accepts null only.

  • Passing true or false will return a type guard that accepts a boolean.

  • Passing any number will return a type guard that accepts a number.

  • Passing any string will return a type guard that accepts a string.

  • Passing any symbol will return a type guard that accepts any symbol (not only the given one).

  • Passing any array will return a type guard that accepts arbitrary arrays (with arbitrary item types). See the arrayOf() modifier below for typed arrays.

  • Passing any unary function is assumed to be a type guard on its own so the function itself will be returned.

  • Passing any other function to looksLike is not allowed.

  • Passing a plain object without a prototype will accept any object whose "shape" matches the example object (comparison is done key-wise for all keys in the example object, values are also treated as examples recursively using looksLike). Objects can also be nested in each other, i.e. you can put another object as a value for a key in a plain object. You can also pass additional type guards (even ones generated with looksLike) as values.

Modifiers

ts-looks-like provides several helper functions that allow you to specify more advanced behaviour with looksLike. These helper functions are called modifiers; typically, they must be called with one or more arguments and will return a type guard function that you can then pass to looksLike as a value for a key in the example object. Typically, when using a modifier, you need a type hint for looksLike to help TypeScript infer the proper type; for example:

export const isNode = looksLike<INode>({
    label: "Some label",
    color: optional("red"),
    visible: false,
});

Supported modifiers are:

  • arrayOf(x) will make the type guard accept arbitrary arrays of objects that are of the type inferred from x as an example object.

  • exactly(x) will make the type guard accept x and only exactly x.

  • instanceOf(x) needs a class and will return a type guard that accepts objects that are instances of the given class. This can be used to work around the restriction that passing an object with a constructor to looksLike throws an Error:

  • maybeNull(x) will make the type guard accept null as well as the type inferred from x as an example object.

  • maybeNil will make the type guard accept null or undefined as well as the type inferred from x as an example object.

  • oneOf(x) will make the type guard accept objects that are an exact match for one of items in the x array.

  • optional(x) will make the type guard accept undefined as well as the type inferred from x as an example object. For instance, optional(42) will return a type guard that accepts numbers and undefined.

  • record(key, x) will make the type guard accept Records whose keys match the key example value or type guard and whose values satisfy the x example object or type guard.

export const isBlogPost = looksLike<IBlogPost>({
    body: "Lorem ipsum dolor sit amet...",
    createdAt: instanceOf(Date),
    title: optional("Lorem ipsum"),
});

Combiners

Multiple type guards may be combined with the allOf() and anyOf() functions. allOf(...) will return a type guard that takes an object and returns true if the object passes all the type guards passed to the original allOf() invocation. Similarly, anyOf(...) will return a type guard that returns true if the object passes at least one of the type guards passed to the original anyOf() invocation.

These functions accept any object that is accepted by looksLike() itself, so you can do something like this:

export const isNumberOrString = anyOf(123, "Lorem ipsum");

You can also use allOf() to handle interface inheritance:

export interface INode {
    label: string;
    visible: boolean;
}

export const isNode = looksLike<INode>({
    label: "Some label",
    visible: false,
});

export interface IColoredNode extends INode {
    color: string;
}

export const isColoredNode = allOf(isNode, {
    color: "red",
});

Caveats

looksLike() is not optimized for performance and it is not expected to cover all the possible use-cases; for instance, disjunctions of types are not covered and there is no plan to do so. If you need something more complicated, you will need to implement your own type guard, but you can then use this type guard just like any other modifier described above.