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

typeskin

v0.1.1

Published

Drop-in static type system on top of Vanilla JS

Downloads

7

Readme

TypeSkin

A lightweight, drop-in layer on top of JavaScript which gives it an expressive, static type system. Differently from TypeScript, it:

  1. Doesn't require another language, thus, can be dropped-in to type a critical function or two;

  2. Has zero runtime cost (if you don't enable runtime checks, obviously);

  3. Allows you to express very precise types such as "the type of hex-encoded strings with an even number of characters", and can statically check all of them;

  4. Performs automatic invariant-based tests, similarly to Haskell's QuickCheck, giving you a very high level of safety with minimal dev effort.

Since checking is based on random sampling, it is not a real type-system, in the sense it can't give you a guarantee of correctness, like TypeScript would. But that's not its job: its job is to spot, at compile time, a very large class of common programmings mistake. For that purpose, it actually performs very well, catching a larger class of bugs than TypeScript ever could.

Static type checks

The basic idea is that, when declaring a JavaScript value or function, instead of doing it as usual:

// Receives a Buffer()-compatible byte-string, returns its byte count
const countBytes = function(a) {
  return bytes.length / 3;
}

You can, optionally, wrap it with a type annotation:

// A function from numbers to numbers
const countBytes = T(T.Fn(T.Bytes, T.Uint32),
  function(bytes) {
    return bytes.length / 3;
  });

And then run (i.e., "compile") your module with node myLib.js. TypeSkin will statically check your declarations:

type mismatch

That error is shown because "1" + x is a String, not a Number, as annotated. Once everything checks, your program behaves exactly the same as if TypeSkin wasn't there at all. And that's it!

Rich type language

TypeSkin allows you to define rich types, using an expressive dialect. Here is, for example, a type for Pokémon:

const T = require("TypeSkin");

// A Pokémon Type
const Type = T.Enum(
  "normal" , "fight"   , "flying" , "poison"   ,
  "ground" , "rock"    , "bug"    , "ghost"    ,
  "steel"  , "fire"    , "water"  , "grass"    ,
  "dragon" , "psychic" , "ice"    , "electric" ,
  "dark"   , "fairy"
).__name("Type");

// A Pokémon Stat
const Stat = T.Enum(
  "hp"  , "atk" , "def",
  "spe" , "spa" , "spd"
).__name("Stat");

// A Pokémon
const Pokemon = T.Struct({
  name: T.String,
  number: T.Uint16,
  types: T.Pair(Type, T.Maybe(Type)),
  attacks: T.Vector(4, T.String),
  stats: T.Struct({
    hp: T.Uint8,
    atk: T.Uint8,
    def: T.Uint8,
    spe: T.Uint8,
    spa: T.Uint8,
    spd: T.Uint8
  })
}).__name("Pokemon");

// A function that receives a Pokémon and returns an Uint8
const highestStat = T(
  T.Fn(Pokemon, T.Uint8),
  function(poke) { 
    return Math.max(
      poke.stats.hp,
      poke.stats.atk,
      poke.stats.spe,
      poke.stats.spa,
      poke.stats.spd); 
  })

Notice the presence of enums, several integer types, pairs, structs, a fixed-size vector and even Haskell's Maybe. Types are plentiful and everything is checked statically. Try "compiling" the file above and changing some bits!

Invariants

The code above, while well-typed, is wrong. Let's add another line to it:

T.forall([Pokemon, Stat], (poke, stat) => highestStat(poke) >= poke.stats[stat]);

This declaration says that, for every Pokémon, we expect that its highestStat is at least as large as any of its stats - which is obvious, isn't it? Let's "compile" that file again:

invariant violation

Woops! It is telling us that our invariant doesn't hold for the def stat of ketecape, a Pokémon randomly generated from its type, which happens to have its def as the highest stat. Seems like we forgot to add poke.stats.def to our highestStat function! Invariants are very similar to tests, as they spot mistakes even on well-typed code. They're easy to write and can be very specific, providing a powerful sense of safety.

Learning

The best way to learn TypeSkin is by following the TypeSkin game.