Last active
August 29, 2015 14:20
-
-
Save deanlandolt/ce62bb9795525ebe90b1 to your computer and use it in GitHub Desktop.
Typescript interfaces and classes as DAL model specifications
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Typescript interfaces and classes as DAL model specifications | |
/** | |
* @entity | |
*/ | |
class IndexedEntity { | |
/** | |
* The @id annotation defines primary key, implies @unique, @index | |
* | |
* @id | |
*/ | |
id : string; | |
/** | |
* The @unique annotation an also be used to look up identity, implies @index | |
* | |
* @unique | |
*/ | |
altId : number; | |
/** | |
* The @index field defines an attribute index, no additioanl constraints | |
* | |
* @index | |
*/ | |
indexed : string; | |
/** | |
* Plain, unindexed field | |
*/ | |
unindexed : string; | |
/** | |
* Accessor fields can be defined, not reified in generated entity | |
*/ | |
get computed() : [ string, number ] { | |
return [ this.unindexed, this.altId ]; | |
} | |
/** | |
* Accessors used to create compound fields, may also be @index or @unique | |
* | |
* @index | |
*/ | |
get compoundIndex() : [ string, number ] { | |
return [ this.foo, this.bar ] | |
} | |
} | |
/** | |
* @entity | |
*/ | |
class CompoundKeyed { | |
foo : string; | |
bar : number; | |
/** | |
* Accessor can also be used to define a compound primary key | |
* | |
* @id | |
*/ | |
get foobar() : [ string, number ] { | |
return this.foo, this.bar | |
} | |
} | |
/** | |
* Interfaces declared as entities can be define indexed attributes | |
* | |
* @entity | |
*/ | |
interface IndexedThing { | |
foo? : string; | |
/** | |
* Indexes defined on an interface allow querying across entity classes | |
* | |
* @index | |
*/ | |
bar : number; | |
} | |
/** | |
* Class entities implementing indexed interfacs inherit their indexes | |
* | |
* @entity | |
*/ | |
class SomeThing implements IndexedThing { | |
/** | |
* Optional attribute can be indexed at entity level | |
*/ | |
foo : string; | |
/** | |
* Already indexed at interface level, but can be indexed by entity if need be | |
* | |
* @index | |
*/ | |
bar : number; | |
} | |
/** | |
* @entity | |
*/ | |
class OtherThing implements IndexedThing { | |
bar : number; | |
} | |
/** | |
* Trait interfaces expose attributes symbols as static symbols on trait | |
* | |
* @entity | |
*/ | |
trait interface SomeAttributes { | |
foo : number; | |
bar : boolean; | |
/** | |
* Traits declared as entities allow attribute index declarations too | |
* | |
* @index | |
*/ | |
baz : string | number; | |
quux? : Array<boolean | number>; | |
} | |
/** | |
* @entity | |
*/ | |
class AnotherThing implements IndexedThing, SomeAttributes { | |
// from IndexedThing interface | |
foo : string; | |
bar : number; | |
// from SomeAttributes interface trait | |
// `as` keyword makes trait symbol attribute available through `foo_` accessor | |
[SomeAttributes.foo] as foo_ : number; | |
// no alias for `bar` but you can still ref it by symbol to set a defualt value | |
[SomeAttributes.bar] : boolean = true; | |
// no alias for `baz` either, but it's still implicitly defined as a symbol | |
// unlike interfaces, trait attribute defs can be elided as they won't conflict | |
// even though `quux` is optional it still gets a symbol defined for type checking | |
} | |
// trait interfacees can be used to create instances directly | |
var attrs = SomeAttributes({ foo: 123, bar: true, baz: '', quux: [1, true, 3] }); | |
// attribute values can be referenced through their symbol or string name | |
attrs.foo === attrs[SomeAttributes.foo] === 123; | |
// this can be helpful when initializing entities with required trait attrs | |
var thing = new AnotherThing(Object.assign({ foo: '', bar: 0 }, attrs)); | |
// traits and interfaces declared as entities can be queried directly | |
db.query`${IndexedThing.bar} < 1000`; | |
db.query`${SomeAttributes.baz} >= 'A' && ${SomeAttributes.baz} < 'D'`; | |
// Implementation notes: | |
// trait interface could be implemented as a strait-forward rewriting pass | |
// split trait interface into interface and class implementations | |
// class exposes all properties as static symbols | |
// interface defined using symbols for all implementing class properties | |
// add any `as` aliases as accessors into symbol values | |
// add type declarations for any elided trait attribute symbols | |
// but typescript has no support for symbols | |
// in the meantime, we could rewrite as symbol-like properties for type checking | |
// we could transform symbol-likes to real symbol instances in a post-compile pass | |
// if we're rewriting anyway, we may as well add syntax support for annotations too | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment