Skip to content

hqtsm/class

Repository files navigation

HQTSM: Class

Class utilities for strongly typed classes

class constant types readonly

JSR npm CI

Features

  • Pure TypeScript, run anywhere
  • Utility types and functions for classes
  • Strong static type checking
  • Tree shaking friendly design

Usage

Abstract

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);

Concrete

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);

Class

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);

ReadonlyKeys

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'

constant

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);

toStringTag

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]');

About

Class utilities for strongly typed classes

Resources

License

Stars

Watchers

Forks

Packages

No packages published