Parse JSON as typed instances.
npm install ts-parse
-
Enable
emitDecoratorMetadata
andexperimentalDecorators
intsconfig.json
:{ "compilerOptions": { "emitDecoratorMetadata": true, "experimentalDecorators": true } }
Typed parsing utilizes constructor-based injection to create actual class instances from JSON or plain JavaScript objects. The Reflect Metadata shim, which is currently a ECMAScript proposal, exposes the type information of a constructor's parameters making it possible to instantiate objects from their real constructors.
For example:
@Serializable()
class Address {
constructor(private city: string, private state: string, private zip: string) {
}
getCityStateZip(): string {
return `${this.city}, ${this.state} ${this.zip}`;
}
}
let address = TSON.parse('{"city":"Richmond","state":"VA","zip":"23230"}', Address);
address instanceof Address; // true
address.getCityStateZip(); // "Richmond, VA 23230"
- Serialize JSON or POJOs to actual class instances
- Instantiation through construction injection
- Fully supports inheritance through super constructors
- Missing arguments in the constructor will be passed as NULL
- Extra JSON properties are set to the class instance via Object.assign
Array support is built-in, but, it requires a cast for compile-time type support. This is necessary to support a simplified API and it eliminates the needs for an extra parsing method for Nth-degree Arrays (e.g. parseArray, parseArrayOfArrays, etc.).
@Serializable()
class Foo { }
const json = "[{},{}]";
// extra cast is required and can't be inferred
let foos = <Foo[]> TSON.parse(json, Foo);
foos[0] instanceof Foo; // true
@Serializable()
class Bar { }
const json = "[[{},{}]]";
// there's no limit to the amount of arrays that can be nested
let bars = <Bar[][]> TSON.parse(json, Bar);
bars[0][0] instanceof Bar; // true
// -------------------------------------------------------------------------------------------------
// TODO make easy getter/setter injection.
// for example:
//
// class A {
//
// constructor(private _foo: string} {
//
// }
//
// get foo() { return this._foo; };
//
// }
//
// then TSON.parse('{"foo":"something"}'
// will serialize into either "_foo" param or "foo" param. if both are detected then ignore "_foo"?
// this should be an optional config item probably
// -------------------------------------------------------------------------------------------------
- TODO Generic serialization when type information does not exist (defaults to true)
- TODO Serialize Private Arguments via prefix (defaults to "_")
- TODO Serialize extra JSON properties to class instance (defaults to true)
Another approach is to creating instances would be member injection which this project does not support.
Tradeoffs and difficulties to field assignment:
- Annotate every member (the "code bloat" is a net zero tradeoff to the constructor "bloat")
- No support for inheritance - why? because we can't clearly define what properties to exclude getOwnProperties
- Assign to -> fields and setters
- How to handle -> readonly, private, protected, const
- TODO Angular4 Serialization Plugin
- HttpClient.get(...);
- The problem is the generic type is erased. We'd have to pass the type - integrating into the existing API would be difficult or impossible. Unless the HttpInterceptor could ... do something cool.
- Fork and/or clone repo.
- Then
npm install
. - Run a quick test
npm run test
. - Make changes and run linter
npm run lint
. - Submit a Pull Request!
You may need to install globals:
npm install mocha chai ts-node typings -g
typings install dt~mocha --global --save
typings install npm~chai --save
Ordering - Dependencies must come before use or they'll be undefined.
In webpack, this might not be avoidable.
What's the workaround?
See: microsoft/TypeScript#4114
One workaround would be to use getters for ElementTypes - that's a last resort. Could probably use a UnionType