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

copyable

v1.0.0

Published

Immutable data objects for TypeScript (and JavaScript)

Downloads

3

Readme

Immutable data objects with type safe .copy(), .map() and .equals() inspired by case class in Scala and data class in Kotlin

Installation

yarn add copyable

TypeScript >= 2.8 required.

Usage

import { Copyable } from 'copyable';

class Person extends Copyable {
  constructor(readonly name: string, readonly age: number) {
    super();
  }
}

const person1 = new Person('John', 25);

const person2 = person1.copy({ name: 'James', age: 20 });
// -> Person(name="James", age=20)
person1.copy('age', 30);
// -> Person(name="John", age=30)

person1.map({
  name: name => 'Older ' + name,
  age: age => age + 20
});
// -> Person(name="Older John", age=45)
person1.map('age', age => age + 20);
// -> Person(name="John", age=45)

person1.equals(person2); // false
person1.equals(new Persion('John', 25)); // true

person1.toString();
/* -> 
  Person(
    name="John",
    age=25
  )
*/

Type safety

person1.copy({ age: '20' }); // comple error: Types of property 'age' are incompatible. Type 'string' is not assignable to type 'number'.

person1.copy({ notAPropertyOfPerson: 20 }); // comple error: Object literal may only specify known properties

person1.copy({ equals: () => false }); // Nope. Can't replace methods.

API Reference

copy

  • copy(patch?: Patch<this>): this

    Returns a copy of the object.

    Accepts an object that contains new values for specified properties

    import { Copyable } from 'copyable';
    
    class Person extends Copyable {
      constructor(readonly name: string, readonly age: number) {
        super();
      }
    }
    
    const person1 = new Person('John', 25);
    person1.copy({ name: 'James', age: 20 });
    // -> Person(name="James", age=20)

    Parameters

    • (Optional) patch: Patch<this>

    Returns

    typeof this

  • copy<K extends PatchKey<this>>(key: K, val: this[K]): this

    Returns a copy of the object.

    Accepts a key and a new value for it

    import { Copyable } from 'copyable';
    
    class Book extends Copyable {
      constructor(readonly title: string) {
        super();
      }
    }
    
    const book1 = new Book('TypeScript Deep Dive');
    book1.copy('title', 'TypeScript handbook');
    // -> Book(title="TypeScript handbook")

    Parameters

    • key: K extends PatchKey<this>
    • value: this[key]

    Returns

    typeof this

map

  • map(patchMap: MapPatch<this>): this

    Maps the object. Allows to copy the object based on its current values.

    Accept an object that contains mapper for specified properties.

    const person1 = new Person('John', 25);
    // John got older
    person1.map({
      name: name => 'Older ' + name,
      age: age => age + 20
    });
    // -> Person(name="Older John", age=45)

    Parameters

    • (Optional) patchMap: MapPatch<this>

    Returns

    typeof this

  • map<K extends PatchKey<this>>(key: K, mapper: Mapper<this[K]>): this

    Maps the object. Allows to copy the object based on its current values.

    Accept a key and a mapper for it.

    import { Copyable } from 'copyable';
    
    class Book extends Copyable {
      constructor(readonly title: string) {
        super();
      }
    }
    
    const book1 = new Book('TypeScript Deep Dive');
    book1.map('title', title => title + ' Part 2');
    // -> Book(title="TypeScript Deep Dive Part 2")

Parameters

  • key: K extends PatchKey<this>
  • mapper: Mapper<this[K]>

Returns

typeof this

equals

equals(other: this | null | undefined): boolean

Checks for structural equality

Note that the comparision is shallow!

const book1 = new Book('TypeScript Deep Dive');
const book2 = new Book('TypeScript handbook');

book1.equals(book2); // false
book1.equals(new Book('TypeScript Deep Dive')); // true

Returns

boolean

toString

toString(): string

Returns a string representation of the object that is a bit nicer than the default one ('[object Object]')

const person = new Person('John', 25);
person.toString();
// Person(
//   name="John",
//   age=25
// )

Returns

string

Can I use it with JavaScript?

Yes! You won't have any type checking, of course, so you risk shooting yourself in a foot.

Prior art

Inspired by Case Classes in Scala and Data Classes in Kotlin.

Implementation influenced by ts-copyable and dataclass.