Class utilities for strongly typed classes
- Pure TypeScript, run anywhere
- Utility types and functions for classes
- Strong static type checking
- Tree shaking friendly design
Create an abstract class type from concrete base.
import type { Abstract } from '@hqtsm/class';
class Foo {
public add = 0;
public num: number;
constructor(num: number) {
this.num = num;
}
public total(): number {
return this.num + this.add;
}
}
abstract class Bar extends Foo {
public override add = 1;
}
abstract class Baz extends Foo {
public override add = 2;
}
function total(C: Abstract<typeof Foo>, value: number) {
return new (class extends C {})(value).total();
}
console.assert(total(Foo, 40) === 40);
console.assert(total(Bar, 40) === 41);
console.assert(total(Baz, 40) === 42);Create a concrete class from an abstract base.
import type { Concrete } from '@hqtsm/class';
abstract class Foo {
public abstract add: number;
public num: number;
constructor(num: number) {
this.num = num;
}
public total(): number {
return this.num + this.add;
}
}
class Bar extends Foo {
public override add = 1;
}
class Baz extends Foo {
public override add = 2;
}
function total(C: Concrete<typeof Foo>, value: number) {
return new C(value).total();
}
console.assert(total(Bar, 40) === 41);
console.assert(total(Baz, 40) === 42);Create a generic class type (excludes functions) with optional static members.
import type { Class } from '@hqtsm/class';
class Foo {
public static readonly MAGIC = 1;
}
class Bar {
public static readonly MAGIC = 2;
}
function magic(C: Class<{ readonly MAGIC: number }>) {
return C.MAGIC;
}
console.assert(magic(Foo) === 1);
console.assert(magic(Bar) === 2);Declaring constructor type for type-safe child statics (late static binding).
import type { Class } from '@hqtsm/class';
abstract class Foo {
declare public readonly ['constructor']: Class<typeof Foo>;
public num: number;
constructor(num: number) {
this.num = num;
}
public total(): number {
return this.num + this.constructor.ADD;
}
public static readonly ADD: number = 0;
}
class Bar extends Foo {
public static override readonly ADD = 1;
}
class Baz extends Foo {
public static override readonly ADD = 2;
}
console.assert(new Bar(40).total() === 41);
console.assert(new Baz(40).total() === 42);Get readonly keys from a type.
import type { ReadonlyKeys } from '@hqtsm/class';
type Mixed = {
a: number;
readonly b: number;
readonly c?: number;
};
type MixedKeysRO = ReadonlyKeys<Mixed>; // 'b' | 'c'Define constant properties like native constants.
import { constant } from '@hqtsm/class';
class Constants {
public static readonly FOO = 1;
protected static readonly BAR = 2;
private static readonly BAZ = 3;
static {
constant(this, 'FOO');
// TypeScript limitation...
constant(this, 'BAR' as never);
constant(this, 'BAZ' as never);
}
}
const desc = Object.getOwnPropertyDescriptor(Constants, 'FOO')!;
console.assert(desc.value === 1);
console.assert(desc.writable === false);
console.assert(desc.enumerable === false);
console.assert(desc.configurable === false);Define Symbol.toStringTag on a class like native types.
import { toStringTag } from '@hqtsm/class';
class Foo {
static {
toStringTag(this, 'Foo');
}
}
class Bar {
static {
toStringTag(this, 'Bar');
}
}
console.assert(String(new Foo()) === '[object Foo]');
console.assert(String(new Bar()) === '[object Bar]');