Skip to content
/ ts-roids Public

Types and decorators you won't need unless you're building complex packages

License

Notifications You must be signed in to change notification settings

ashgw/ts-roids

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ts-roids

100+ types and decorators to bullet proof TypeScript even more.

CI @latest npm downloads Socket Badge


Installation

npm

npm i ts-roids

pnpm

pnpm i ts-roids

If you're only using types, you can install it as a devDependency. And if you're using decorators, set this.

{
  "compilerOptions": {
    // ...
    "experimentalDecorators": true
  }
}

Requires TypesScript v5.0+

Documentation

Checkout the full API reference for all usage examples with details.

Types

Decorators

Basic Usage

Finalize and freeze objects

import type { Optional, NewType, MaybeUndefined } from 'ts-roids';
import { Final, Frozen, Singleton } from 'ts-roids';

type Bar = NewType<'Bar', string>;
type Baz = NewType<'Baz', string>;
type Secret = NewType<'Secret', string>;

abstract class BaseFoo<T> {
  public abstract requestFoo(secret: Secret, baz: Baz): Promise<Optional<T>>;
}

@Final
@Frozen
@Singleton
class Foo<T> extends BaseFoo<T> {
  private static readonly rnd = Math.random();
  private readonly foo: T;
  public bar: Optional<Bar>; // `Bar` then becomes readonly with the decorator

  public constructor(foo: T, bar?: MaybeUndefined<Bar>) {
    super();
    this.foo = foo;
    this.bar = bar ?? null;
  }

  public override async requestFoo(
    secret: Secret,
    baz: Baz
  ): Promise<Optional<T>> {
    if (
      Foo.rnd > 0.5 &&
      secret.concat().toLowerCase() === '123' &&
      baz.concat().toLowerCase() === 'baz' &&
      this.bar !== null
    ) {
      return await Promise.resolve(this.foo);
    }

    return null;
  }
}

class SubFoo extends Foo<string> {
  constructor(foo: string) {
    super(foo);
  }
}

// No problem with instantiation
const foo = new Foo('foo');

// The Singleton ensures the same instance is returned
const foo2 = new Foo('bar');
console.log(foo2 === foo); // True

// Since the object is final:
// The line below will cause a TypeError: Cannot inherit from the final class Foo
new SubFoo('subFoo');

// Since the object is frozen:
// The line below will cause a TypeError: Cannot add property 'requestFoo', object is not extensible
foo.requestFoo = async () => {
  return await Promise.resolve('not foo');
};

// The line below will cause a TypeError: Cannot assign to read only property 'bar'
foo.bar = 'not bar' as Bar;

The TypeScript team has not yet introduced a built-in final modifier yet, check this, this and many other requests. Although they introduced override in v4.3 .

Decorators like @Final provide a limited way to emulate final behavior, these are merely band-aids for now, until TS officially supports a true final modifier.

You can also seal an object btw.

@Sealed
class Person {
  constructor(name: string, age?: number) {}
}

const john = new Person('John', 30);

// Existing properties can still be modified
john.age = 31; // No Errors

// Existing properties cannot be re-configured nor deleted

(john as any).email = 'john@doe.com'; // TypeError: Cannot add property email,
// object is not extensible

delete john.age; // TypeError: Cannot delete property 'age'

Changelog

See releases.

License

GPL-3

About

Types and decorators you won't need unless you're building complex packages

Resources

License

Security policy

Stars

Watchers

Forks

Packages

No packages published