ts-roids
Version:
Bullet-proof TS even more
165 lines (159 loc) • 4.54 kB
JavaScript
/**
* @hidden
*/
class FinalTypeError extends TypeError {
}
/**
* Marks a class as final, preventing inheritance from this class.
* When applied, any attempt to extend this class will result in a TypeError at runtime.
* @remarks
* This decorator does not prevent instantiation of the final class itself.
* @example
* ```ts
* @Final
* class Foo<T> {
* foo: T;
* bar: string;
*
* constructor(foo: T) {
* this.foo = foo;
* this.bar = 'bar';
* }
*
* someFoo(): T {
* return this.foo;
* }
* }
*
* // No problem with instantiation
* const foo = new Foo<string>('foo');
* // The line below will cause a TypeError: Cannot inherit from the final class Foo
* const sub = new SubFoo('subFoo');
* ```
* @see {@link https://github.com/microsoft/TypeScript/issues/1534| Issue #1}
* @see {@link https://github.com/microsoft/TypeScript/issues/8306| Issue #2}
* @see {@link https://github.com/microsoft/TypeScript/issues/50532| Issue #3}
*/
const Final = (cst) => {
class F extends cst {
constructor(...args) {
super(...args);
const newTarget = new.target;
if (newTarget !== F) {
throw new FinalTypeError(`Cannot inherit from the final class: `);
}
}
}
Reflect.defineProperty(F, 'name', {
// eslint-disable-next-line
value: cst.name || 'Final',
});
return F;
};
const _freeze = (obj) => {
Object.freeze(obj);
};
/**
* When applied to a class it creates a [frozen](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) instance of it,
* thus preventing modifications to instance properties after instantiation.
*
* @example
* ```ts
@Frozen
class Foo<T> {
foo: T;
bar?: MaybeUndefined<string>;
constructor(foo: T) {
this.foo = foo;
this.bar = 'bar';
}
someFoo(): T {
return this.foo;
}
}
const foo = new Foo('foo');
// The line below will cause a TypeError: Cannot assign to read only property 'bar'
foo.bar = 'altered bar';
// The line below will cause a TypeError: Cannot delete property 'bar'
delete foo.bar;
* ```
*/
function Frozen(cst) {
return class Locked extends cst {
constructor(...args) {
super(...args);
_freeze(this);
}
};
}
const _seal = (obj) => {
Object.seal(obj);
};
/**
* When applied to a class, it creates a [sealed](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal) instance of it,
* preventing extensions and making existing properties non-configurable.
*
* @example
* ```ts
* @Sealed
* class Person {
* constructor(public name: string, public age?: number) {}
* }
*
* const john = new Person('John', 30);
* // Trying to add a new property will throw an error
* (john as any).email = 'john@example.com'; // TypeError: Cannot add property email, object is not extensible
*
* // Existing properties can still be modified
* john.age = 31; // Allowed
*
* // Existing properties cannot be re-configured or deleted
* delete john.age; // TypeError: Cannot delete property 'age'
* }
* ```
* */
function Sealed(cst) {
return class Locked extends cst {
constructor(...args) {
super(...args);
_seal(this);
}
};
}
/**
* When applied to a class, it ensures that only a single instance of the class can be created.
* If an attempt is made to create another instance, the existing instance will be returned.
*
* @remarks
* The `Singleton` pattern is often used for cases where a global state or resource needs to be shared across the entire application.
* It is common in cases like configuration settings or managing connections to a shared resource (e.g., a database or API).
*
* @example
* ```ts
* @Singleton
* class Database {
* constructor(public host: string, public port: number) {}
* }
*
* const db1 = new Database('localhost', 5432);
* const db2 = new Database('localhost', 5432);
*
* console.log(db1 === db2); // true, both db1 and db2 refer to the same instance
* ```
* @see
* https://en.wikipedia.org/wiki/Singleton_pattern
*/
function Singleton(cst) {
let instance;
return class Singleton extends cst {
constructor(...args) {
if (instance) {
return instance;
}
super(...args);
instance = new cst(...args);
return instance;
}
};
}
export { Final, FinalTypeError, Frozen, Sealed, Singleton };