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

type-brandy

v1.0.2

Published

Nominal typing for TypeScript

Downloads

153

Readme

About this package

With type-brandy package You can achieve nominal typing by leveraging a technique that is called "type branding" in TypeScript world. This works by intersecting a base type with a object type with a non-existent property. It's very similar to Flow's opaque type aliases.

Why type branding is important?

Let's say You have two types (they could be numbers, strings or even objects) that are structurally equal but are used for different things in your code. For example - number could be a user id, a phone number or tracking number. Many other things, maybe even used for security. To a structural type system there is no way to say that this function should only work on user ids and not be allowed to received other numbers that could result it getting data for the wrong user. People tend to make mistakes, but we can make our lives a little better. We can make it so a user id is not equal to every other number in our application.

Flavoring vs branding

Both methods are useful. But in practice, flavoring is more often applicable. It is useful in the following cases:

  • You want to allow implicit conversion of composite-structures which are from trusted sources, but want to use semantic subtypes.
  • You wish to trace a category or source of a simple value, but aren’t willing to sign up for the friction of casting or using functions to “bless” values explicitly in all of your unit tests, etc. Good example - units of measure.
  • You want to annotate the type of an argument with semantic information in a way that TypeScript can trace for you and make visible in e.g. editor tooltips while still using simple types at runtime.

Branding also has its uses. We will use the stricter approach when:

  • We want to write code that can safely assume that some data validation has occurred. For example - IsoDate type which must be a valid ISO 8601 date string.
  • A type error admitted by implicit conversion could lead to a dangerous error, such as when using types to access tokens for authorization.

Installation

$ npm install type-brandy --save-dev
$ yarn add type-brandy --dev
$ pnpm add type-brandy --save-dev

Usage examples

Flavoring

import { Flavor } from 'type-brandy';

type UserId = Flavor<number, 'User'>;
type BlogId = Flavor<number, 'Blog'>;

function getUserById(userId: UserId) {
  return User.findOne({ _id: userId });
}

const blogId: BlogId = 1000;      // OK
const userId: UserId = 2000;      // OK

const user = getUserById(blogId); // Compile time error

Branding

import { Brand, make } from 'type-brandy';

type IsoDate = Brand<string, 'IsoDate'>;

const IsoDate = make<IsoDate>((value: string) => {
  if (new Date(value).toJSON() !== value) {
    throw new TypeError('invalid ISO 8601 date string');
  }
});

// Throws compile time error
const date1: IsoDate = '2000-01-01T00:00:00.000Z';
// Throws runtime error
const date3: IsoDate = IsoDate('9999-99-99T99:99:99.999Z');
// Compilation would be successful
const date2: IsoDate = IsoDate('2000-01-01T00:00:00.000Z');