TypeRegistry
Creating type registry
import { TypeRegistries } from '@temabit/proto';
abstract class ABase {
abstract todo(): void;
}
const registry = TypeRegistries.create(ABase, '@@type');
Persisting type registry
import { TypeRegistries } from '@temabit/proto';
abstract class ABase {
abstract todo(): void;
}
const registry = TypeRegistries.for(ABase, '@@type');
Adding implementations to registry
import { TypeRegistries } from '.';
abstract class ABase {
abstract todo(): void;
}
const Implementations = TypeRegistries.for(ABase, '@@type');
@Implementations.Define() // defines implementation with `
class ImplementationA extends ABase {
todo() {
console.log(this.constructor.name);
}
}
@Implementations.Define({
name: 'B' // defines class with specific name
})
class ImplementationB extends ABase {
todo() {
console.log(this.constructor.name);
}
}
Adding serializable properties to classes
import { TypeRegistries } from '.';
const Objects = TypeRegistries.for(Object, '@@type');
abstract class AConnection {
@Expose()
@Objects.Integer()
public timeout!: number; // Common property
public abstract connect(): Promise<any>;
}
const Connections = TypeRegistries.for(AConnection, '@@type');
@Connections.Define({ name: 'postgres' })
class PostgresConnection extends AConnection {
@Expose()
@Connections.String()
public host!: string;
@Expose()
@Connections.String()
public user!: string;
@Expose()
@Connections.String()
public database!: string;
@Expose()
@Connections.Integer()
public port!: number;
public connect() {
return Promise.resolve(this);
}
}
@Connections.Define({ name: 'sqlite' })
class SQLiteConnection extends AConnection {
@Expose()
@Connections.String()
public filename!: string;
public connect() {
return Promise.resolve(this);
}
}
Adding property validators
import { TypeRegistries, Validate } from '.';
const Objects = TypeRegistries.for(Object, '@@type');
abstract class AConnection {
@Expose()
@Objects.Integer([
Validate.Number.Positive(),
Validate.Number.Max(Math.pow(2, 31) - 1)
])
public timeout!: number; // Common property
public abstract connect(): Promise<any>;
}
const Connections = TypeRegistries.for(AConnection, '@@type');
@Connections.Define({ name: 'postgres' })
class PostgresConnection extends AConnection {
@Expose()
@Connections.String([
Validate.String.IsNotEmpty()
])
public host!: string;
@Expose()
@Connections.String([
Validate.String.IsNotEmpty()
])
public user!: string;
@Expose()
@Connections.String([
Validate.String.IsNotEmpty()
])
public database!: string;
@Expose()
@Connections.Integer([
Validate.Number.Min(1),
Validate.Number.Max(65536),
])
public port!: number;
public connect() {
return Promise.resolve(this);
}
}
@Connections.Define({ name: 'sqlite' })
class SQLiteConnection extends AConnection {
@Connections.String([
Validate.String.IsNotEmpty()
])
public filename!: string;
public connect() {
return Promise.resolve(this);
}
}
Parsing plain instance
import { TypeRegistries, Validate } from '.';
const Objects = TypeRegistries.for(Object, '@@type');
abstract class AConnection {
@Objects.Integer([
Validate.Number.Positive(),
Validate.Number.Max(Math.pow(2, 31) - 1)
])
public timeout!: number; // Common property
public abstract connect(): Promise<any>;
}
const Connections = TypeRegistries.for(AConnection, '@@type');
@Connections.Define({ name: 'postgres' })
class PostgresConnection extends AConnection {
@Connections.String([
Validate.String.IsNotEmpty()
])
public host!: string;
@Connections.String([
Validate.String.IsNotEmpty()
])
public user!: string;
@Connections.String([
Validate.String.IsNotEmpty()
])
public database!: string;
@Connections.Integer([
Validate.Number.Min(1),
Validate.Number.Max(65536),
])
public port!: number;
public connect() {
return Promise.resolve(this);
}
}
@Connections.Define({ name: 'sqlite' })
class SQLiteConnection extends AConnection {
@Connections.String([
Validate.String.IsNotEmpty()
])
public filename!: string;
public connect() {
return Promise.resolve(this);
}
}
try {
console.log(Connections.parseInstance({
'@@type': "postgres",
timeout: 1000,
host: 'localhost',
port: 5432,
user: 'postgres',
database: 'postgres',
}));
/*
PostgresConnection {
timeout: 1000,
host: 'localhost',
user: 'postgres',
database: 'postgres',
port: 5432
}
*/
} catch (e) {
console.error(String(e));
}
try {
console.log(Connections.parseInstance({
'@@type': "sqlite",
timeout: 1000,
filename: 'test.sqlite3'
}));
/*
SQLiteConnection { timeout: 1000, filename: 'test.sqlite3' }
*/
} catch (e) {
console.error(String(e));
}
try {
console.log(Connections.parseInstance({
'@@type': "unknown",
timeout: 1000,
filename: 'test.sqlite3'
}));
} catch (e) {
console.error(String(e));
/*
ValidationFailure: : data must be an instance of AConnection
*/
}
Parsing plain array
import { TypeRegistries, Validate } from '.';
const Objects = TypeRegistries.for(Object, '@@type');
abstract class AConnection {
@Objects.Integer([
Validate.Number.Positive(),
Validate.Number.Max(Math.pow(2, 31) - 1)
])
public timeout!: number; // Common property
public abstract connect(): Promise<any>;
}
const Connections = TypeRegistries.for(AConnection, '@@type');
@Connections.Define({ name: 'postgres' })
class PostgresConnection extends AConnection {
@Connections.String([
Validate.String.IsNotEmpty()
])
public host!: string;
@Connections.String([
Validate.String.IsNotEmpty()
])
public user!: string;
@Connections.String([
Validate.String.IsNotEmpty()
])
public database!: string;
@Connections.Integer([
Validate.Number.Min(1),
Validate.Number.Max(65536),
])
public port!: number;
public connect() {
return Promise.resolve(this);
}
}
@Connections.Define({ name: 'sqlite' })
class SQLiteConnection extends AConnection {
@Connections.String([
Validate.String.IsNotEmpty()
])
public filename!: string;
public connect() {
return Promise.resolve(this);
}
}
try {
console.log(Connections.parseArray([
{
'@@type': "postgres",
timeout: 1000,
host: 'localhost',
port: 5432,
user: 'postgres',
database: 'postgres',
},
{
'@@type': "sqlite",
timeout: 1000,
filename: 'test.sqlite3'
}
]));
/*
[
PostgresConnection {
timeout: 1000,
host: 'localhost',
user: 'postgres',
database: 'postgres',
port: 5432
},
SQLiteConnection { timeout: 1000, filename: 'test.sqlite3' }
]
*/
} catch (e) {
console.error(String(e));
}
Validating parsed instances or arrays
import { TypeRegistries, Validate } from '.';
const Objects = TypeRegistries.for(Object, '@@type');
abstract class AConnection {
@Objects.Integer([
Validate.Number.Positive(),
Validate.Number.Max(Math.pow(2, 31) - 1),
])
public timeout!: number; // Common property
public abstract connect(): Promise<any>;
}
const Connections = TypeRegistries.for(AConnection, '@@type');
@Connections.Define({name: 'sqlite'})
class SQLiteConnection extends AConnection {
@Connections.String([
Validate.String.IsNotEmpty(),
])
public filename!: string;
public connect() {
return Promise.resolve(this);
}
}
try {
const instance = Connections.parseInstance({
'@@type': 'sqlite',
timeout: 1000,
filename: '',
});
Connections.assert(Connections.validate(instance));
} catch (e) {
console.error(String(e));
/*
ValidationFailure: .filename: filename should not be empty
*/
}
Serialization
import { TypeRegistries, Validate } from '.';
const Objects = TypeRegistries.for(Object, '@@type');
abstract class AConnection {
@Objects.Integer([
Validate.Number.Positive(),
Validate.Number.Max(Math.pow(2, 31) - 1),
])
public timeout!: number; // Common property
public abstract connect(): Promise<any>;
}
const Connections = TypeRegistries.for(AConnection, '@@type');
@Connections.Define({name: 'sqlite'})
class SQLiteConnection extends AConnection {
@Connections.String([
Validate.String.IsNotEmpty(),
])
public filename!: string;
public connect() {
return Promise.resolve(this);
}
}
try {
const instance = new SQLiteConnection();
instance.filename = '123';
instance.timeout = 1000;
console.log(Connections.serialize(instance));
/*
{ filename: '123', timeout: 1000, '@@type': 'sqlite' }
*/
} catch (e) {
console.error(String(e));
}
Cloning
import { TypeRegistries, Validate } from '.';
const Objects = TypeRegistries.for(Object, '@@type');
abstract class AConnection {
@Objects.Integer([
Validate.Number.Positive(),
Validate.Number.Max(Math.pow(2, 31) - 1),
])
public timeout!: number; // Common property
public abstract connect(): Promise<any>;
}
const Connections = TypeRegistries.for(AConnection, '@@type');
@Connections.Define({name: 'sqlite'})
class SQLiteConnection extends AConnection {
@Connections.String([
Validate.String.IsNotEmpty(),
])
public filename!: string;
public connect() {
return Promise.resolve(this);
}
}
try {
const instance = new SQLiteConnection();
instance.filename = '123';
instance.timeout = 1000;
console.log(Connections.clone(instance));
/*
SQLiteConnection { filename: '123', timeout: 1000 }
*/
} catch (e) {
console.error(String(e));
}
Complex sample
import { TypeRegistries, Validate } from '.';
const Objects = TypeRegistries.for(Object, 'type');
class Point {
@Objects.Number()
public x!: number;
@Objects.Number()
public y!: number;
}
abstract class ASceneObject {
public abstract draw(context: CanvasRenderingContext2D): void;
}
const SceneObjects = TypeRegistries.for(ASceneObject, '@@type');
@SceneObjects.Define()
class Scene extends ASceneObject {
@SceneObjects.Array(
SceneObjects.AbstractInstance()
)
public children!: ASceneObject[];
public draw(context: CanvasRenderingContext2D): void {
for (const child of this.children) {
try {
context.save();
child.draw(context);
} finally {
context.restore();
}
}
}
}
@SceneObjects.Define()
class Circle extends ASceneObject {
@SceneObjects.Instance({
type: Point
})
public point!: Point;
@SceneObjects.Number([
Validate.Number.Positive()
])
public radius!: number;
public draw(context: CanvasRenderingContext2D): void {
context.arc(this.point.x, this.point.y, this.radius, 0, 2*Math.PI);
}
}
@SceneObjects.Define()
class Line extends ASceneObject {
@SceneObjects.Array(
SceneObjects.Instance({
type: Point
})
)
public points!: Point[];
public draw(context: CanvasRenderingContext2D): void {
if (this.points.length > 1) {
const [start, ...points] = this.points;
context.moveTo(start.x, start.y);
for (const p of points) {
context.lineTo(p.x, p.y);
}
}
}
}
console.dir(SceneObjects.parseInstance(
{
'@@type': Scene.name,
children: [
{
'@@type': Line.name,
points: [
{
x: 0, y: 0,
},
{
x: 0, y: 1,
},
{
x: 1, y: 1,
},
{
x: 1, y: 0,
},
]
},
{
'@@type': Circle.name,
point: {
x: 5,
y: 5,
},
radius: 3
}
]
}
), { depth: null });
/*
Scene {
children: [
Line {
points: [
Point { x: 0, y: 0 },
Point { x: 0, y: 1 },
Point { x: 1, y: 1 },
Point { x: 1, y: 0 }
]
},
Circle { point: Point { x: 5, y: 5 }, radius: 3 }
]
}
*/