JSOG for TypeScript
jsog-typescript is a TypeScript module that implements the JavaScript Object Graph (JSOG) format, enabling the serialization and deserialization of JavaScript objects while preserving object references to prevent cycles and duplicates. Its key differentiator is the ability to instantiate actual TypeScript class instances during deserialization, leveraging decorators for type mapping. This allows developers to work with rich object models that retain their methods and `typeof` information after being processed. The package is currently at version 1.0.0-1 and appears to be actively maintained, offering specific integration patterns for frameworks like Angular 4 and AngularJS. It builds upon the core JSOG specification and incorporates ideas from `json-typescript-mapper` to provide this type-aware deserialization.
Common errors
-
ReferenceError: Reflect is not defined
cause The `reflect-metadata` polyfill is missing or not imported at the application's entry point, which is crucial for TypeScript decorators to emit and retrieve type metadata at runtime.fixRun `npm install reflect-metadata` and add `import 'reflect-metadata';` as the very first line in your main application entry file (e.g., `main.ts`). -
Error: Cannot convert object with JSOG ID to class. [class name] expected.
cause During `deserializeObject` or `deserializeArray`, `jsog-typescript` failed to instantiate the target TypeScript class, often because of missing decorators or incorrect `tsconfig.json` settings.fixVerify that `experimentalDecorators` and `emitDecoratorMetadata` are enabled in `tsconfig.json` and that all properties intended for deserialization into custom types have the `@JsonProperty()` decorator, with type arguments where necessary (e.g., `@JsonProperty(MyClass)`). -
Property 'someMethod' does not exist on type 'MyClass' after deserialization.
cause While the object might look correct, it was likely deserialized as a plain JavaScript object or an incorrect type, meaning methods defined on your TypeScript class are not available.fixEnsure you are using `jsog.deserializeObject(data, MyClass)` or `jsog.deserializeArray(data, MyClass)` to explicitly tell `jsog-typescript` to instantiate objects as `MyClass` instances, and that `reflect-metadata` is properly imported.
Warnings
- breaking Enabling TypeScript's `experimentalDecorators` and `emitDecoratorMetadata` compiler options is mandatory for this library to function correctly at compile time.
- breaking The `reflect-metadata` package is a required runtime dependency for `emitDecoratorMetadata` to provide type information at runtime. Without it, deserialization into specific TypeScript classes will fail silently or with runtime errors.
- gotcha For complex nested objects or arrays of custom types, the `@JsonProperty` decorator requires a type argument to correctly instantiate objects during deserialization.
- gotcha When deserializing to a specific TypeScript class, ensure that the class has a default (no-argument) constructor, or that the library can infer how to instantiate it. Missing default constructors can lead to errors.
Install
-
npm install jsog-typescript -
yarn add jsog-typescript -
pnpm add jsog-typescript
Imports
- jsogService
const jsogService = require('jsog-typescript').jsogService;import { jsogService } from 'jsog-typescript' - JsogService
import JsogService from 'jsog-typescript';
import { JsogService } from 'jsog-typescript' - JsonProperty
import { JsonProperty } from 'jsog-typescript/decorators';import { JsonProperty } from 'jsog-typescript'
Quickstart
import { jsogService, JsonProperty, JsogService } from 'jsog-typescript';
import 'reflect-metadata'; // Essential for decorators to work at runtime
// 1. Define a TypeScript class with decorators
class Address {
@JsonProperty() street: string = '';
@JsonProperty() city: string = '';
getFullAddress(): string {
return `${this.street}, ${this.city}`;
}
}
class Person {
@JsonProperty() id: number = 0;
@JsonProperty() name: string = '';
@JsonProperty(Address) address: Address | undefined;
@JsonProperty(Person) friends: Person[] = []; // Array of nested custom objects
greet(): string {
return `Hello, my name is ${this.name}!`;
}
}
// 2. Instantiate and serialize an object graph
const person1 = new Person();
person1.id = 1;
person1.name = 'Alice';
person1.address = new Address();
person1.address.street = '123 Main St';
person1.address.city = 'Anytown';
const person2 = new Person();
person2.id = 2;
person2.name = 'Bob';
person2.friends.push(person1); // Create a circular reference
person1.friends.push(person2);
const serializedGraph = jsogService.serialize(person1);
console.log('Serialized Graph:', JSON.stringify(serializedGraph, null, 2));
// 3. Deserialize back into TypeScript objects
const deserializedPerson = jsogService.deserializeObject(serializedGraph, Person);
console.log('\nDeserialized Person:', deserializedPerson);
console.log('Is instance of Person:', deserializedPerson instanceof Person); // true
console.log('Is instance of Address:', deserializedPerson.address instanceof Address); // true
console.log('Person greeting:', deserializedPerson.greet());
console.log('Person address:', deserializedPerson.address?.getFullAddress());
console.log('Friend greeting:', deserializedPerson.friends[0]?.greet());