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

kiss-and-type

v0.8.0

Published

A lightweight approach to typing in plain JavaScript

Downloads

26

Readme

npm version

What is this?

A lightweight approach to typing in plain JavaScript.

You might enjoy some advantages of more common type systems without the need to change your language and tooling. You might enjoy some unexpected advantages as well.

Features

Let’s look at the practical aspects first. – If you are really interested, more general and slightly deeper thoughts on typing are further down.

Some safety

> fooT = createType([ 'bar' ])
…
> fooT.get.baz
Thrown:
TypeError: Unknown property 'baz'
…

Immutable

By default, all operations are immutable. However, immutability is not enforced in any fashion on the objects.

> fooT = createType(['bar'])
…
> foo1 = fooT.set.bar(0)({})
{ bar: 0 }
> foo2 = fooT.set.bar(1)(foo1)
{ bar: 1 }
> foo1
{ bar: 0 }

Functional programming

Data-last and curried

> fooT = createType([ 'bar' ])
…
> fooT.set.bar(0, {})
{ bar: 0 }
> fooT.set.bar(0)({})
{ bar: 0 }
>

Composition

All functions need no binding and thus lend themselves perfectly for functional composition.

> fooT = createType([ 'bar' ])
> r.pipe(
  () => [ null, {}, { bar: 0 }, fooT.objOf.bar(1) ],
  r.filter(fooT.has.bar),
  fooT.pluck.bar
)()
[ 0, 1 ]

Familiar lingo

objOf, has and pluck in the above example are inspired by Ramda lingo.

So are pick, pickAll, eq, over.

Aliases

In practice I have found it very helpful to define aliases for props.

  • Under some circumstances the cost for a deep rename might be too high.
  • Or you might be unable to change the prop name at all, e.g. when using an external API.
  • Or you might actually want to have both, a simple name and a very precise one. Say you have a prop called f72OriginalClosingDay. This name is very precise in the domain of Schlussnoten and we might want to keep it for reference. At the same time, it is a pain to use. So we might want to alias it to originalClosing.
> fooT = createType([ {someVeryLongAndOrCumbersomeName: { alias: 'bar' }} ])
…
> foo = fooT.set.someVeryLongAndOrCumbersomeName(0)({})
{ someVeryLongAndOrCumbersomeName: 0 }
> fooT.get.bar(foo)
0

Progressive enhancement

You get getters and setters out of the box. If you want, you can enhance them.

> fooT = createType([ {bar: { get: r.defaultTo(0) }} ])
…
> fooT.get.bar({})
0
> fooT = createType([ {bar: { set: Math.floor }} ])
…
> fooT.set.bar(0.5)({})
{ bar: 0 }

Relationships between types

Interfaces, inheritance, mixins are largely unexplored at the moment. The value of this approach to typing seems to lie elsewhere, either way.

API

> fooT = createType([ 'bar' ])
{
  props: { bar: [Function] },
  get: { bar: [Function: f1] },
  pick: { bar: [Function] },
  pluck: { bar: [Function: f1] },
  has: { bar: [Function] },
  eq: { bar: [Function] },
  set: { bar: [Function: f2] },
  objOf: { bar: [Function] },
  over: { bar: [Function: f2] }
}

Please refer to the living doc for details and examples. Should anything be unclear, kindly open an issue, or even a PR.

On types

Types, huh, yeah!
What are they good for?

– Edwin Starr

I just thought of this and it made me laugh. It is not my actual position on types. Nor is it Edwin Starr’s.

So what are types good for? A few things come to mind immediately.

  • Autocompletion (simple tooling)
  • Refactoring (advanced tooling) – Nothing in the JS world (nor even in the TS world at the moment) comes even close to what an IDE can do to Java safely.
  • Documentation
  • Type safety

When we discuss types in the broader JS ecosystem, in my perception, we talk almost exclusively only about these aspects. These are all valuable things to have. They are not free, however. I generally agree with Eric Elliott regarding the Type(Script) Tax. Loosely summarized: 1) JS tooling isn’t too bad1; 2) if you practice TDD and code reviews, type safety does not save you from too many additional bugs; 3) the cost of working with TS types is greater than the incremental advantages of TS over JS.

But wait, there is more. Nevermind structural typing. We need a place where to put the behavior of our things. As our things grow and can do more, we naturally put related behaviors close to each other and unrelated behaviors farther away. As we build cohesive modules, we naturally build types.

This has always been my main motivation for types. More importantly, I naturally gravitated towards building types in this sense in JS, a language that does not exactly force such constructs on you. When building cohesive behavior, of course, you also access the same properties time and time again.

This coincided with the desire for a simple way to encapsulate property access. It was driven by the wish for some safety. This became a major concern for me when a team that I was working with was writing quite a few tests just in order to guard itself against mistyping property names. The team gave Flow a try, but found the type tax too high. – I also wanted to be able to progressively enhance the property access.

Finally, because of an intensive use of Ramda, functional composition became a major concern.

The marriage of these three – behavior honeypot, property access and FP – led me to this type construct.

I use createType to scaffold the property access. I then add interesting behavior as it emerges.

Footnotes

  1. On episode #90 of JS Party, Chris Hiller shared how he used TS tooling with JSDoc in order to type-check plain JS. I have not tried this. But it sounds as if you could get most advantages of TS’s type checking without switching to TS. See also https://www.typescriptlang.org/docs/handbook/type-checking-javascript-files.html.

What’s with the name?

“Keep it simple stupid” came to mind immediately when I started looking for a name. There is a distinct lack of sophistication to this approach. Yet, it is useful. – So, “KISS” and “type” were there. I liked the association of “kiss and tell”. So I kept it.

Added bonus: 😽 and type.

Thanksgiving

Hacking

  • yarn run check lints and runs tests
  • yarn run release checks and publishes

Typical concerns

  • semantics: use enhancer or not?
    • hypothesis: in most cases probably yes