I am trying to serialize/deserialize an object. I am thinking that the best way would be to save the path to the file that called as part of the json, but I am unsure of how to get said path.
Can getting this path to the file (A.ts/B.ts) be done when called within the parent (Base.ts)?
Is there maybe a better approach to doing this? I am trying to take a class created in the main node process, and and pass it to a worker process, the only why to do this that I can see is to serialize/deserialize the class somehow.
// src/Base.ts
export abstract class Base {
public serialize() {
return JSON.stringify({path: '', obj: this})
}
public static deserialize(json: string) {
let { path, obj } = JSON.parse(json) as { path: string, obj: { [key: string]: any } }
let newable = require(path)
let o = new newable
return Object.assign(o, obj)
}
}
// src/filter/A.ts
export class A extends Base {
public cat: string = 'meow'
public sayHi() { return this.cat }
}
// src/filter/B.ts
export class B extends Base {
public dog: string = 'woof'
public sayHi() { return this.dog }
}
// test.ts
let serializedA = new A().serialize()
let serializedB = new B().serialize()
// Create child...
let worker = cp.fork(path.join(__dirname, './worker'), [], { silent: true })
worker.send({ serializedA, serializedB })
// worker.ts
process.on('message', msg => {
let classA = Base.deserialize(msg.serializedA)
let classB = Base.deserialize(msg.serializedB)
})
The simplest way that comes to mind would be to have a set of class names associated with callbacks that would require the appropriate classes.
// src/JsonIO.ts
export class JsonIO {
private _classes: { name: string, callback: () => { new(): any } }[] = []
public serialize(obj: any): string {
return JSON.stringify({ class: obj.constructor.name, value: obj })
}
public deserialize(json: string) {
const obj = JSON.parse(json) as { class: string, value: any }
const clazz = this._classes.find(c => c.name == obj.class)
if(!clazz) return obj.value
return Object.assign(new (clazz.callback()), obj.value)
}
public registerClass(name: string, callback: () => { new(): any }) {
this._classes.push({ name, callback })
}
}
// src/Base.ts
export abstract class Base { /* ... */ }
// src/filter/A.ts
export class A {
public cat: string = 'meow'
}
// src/filter/B.ts
export class B {
public dog: string = 'woof'
}
// test.ts
const io = new JsonIO()
io.registerClass('A', () => A /* require('filter/A.ts') */)
io.registerClass('B', () => B /* require('filter/B.ts') */)
const serializedA = io.serialize(new A)
const serializedB = io.serialize(new B)
const a = io.deserialize(serializedA)
const b = io.deserialize(serializedB)
Related
I am trying to type de values of an array in a template object.
Currently I have achieved my goal using objects like so :
// defining the model type
interface RouteModel {
route: string
params?: Record<string, string>
}
interface RoutesModel {
[routeName: string]: RouteModel
}
// value constructor
function makeRoutes<T extends RoutesModel>(input: T) {
return input
}
// type safe creation for routes
const routes = makeRoutes({
potato: {
route: '/potato/:potatoId/rate',
params: { potatoId: '' },
},
grapes: {
route: 'grapes',
},
banana: {
route: 'bag/:bagId/:bananaId',
params: { bagId: '', bananaId: '' },
},
})
const useTypedHistory = <T extends RoutesModel>() => {
const navigate = <K extends keyof T>(route: K, params: Record<keyof T[K]['params'], string>) => {
}
return { navigate }
}
const Component = () => {
const { navigate } = useTypedHistory<typeof routes>()
navigate('banana', { bagId: '123', bananaId: '567' })
ʌ --- type safety works here, it requires the right object depending on the first param
return null
}
export default useTypedHistory
My problem is that at the beginning, I declare my params as an object with the correct keys but empty string to make it work.
I would like to an array of strings instead, so it would look like this:
// defining the model type
interface RouteModel {
route: string
params?: string[]
}
interface RoutesModel {
[routeName: string]: RouteModel
}
// value constructor
function makeRoutes<T extends RoutesModel>(input: T) {
return input
}
// type safe creation for routes
const routes = makeRoutes({
potato: {
route: '/potato/:potatoId/rate',
params: ['potatoId'],
},
grapes: {
route: 'grapes',
},
banana: {
route: 'bag/:bagId/:bananaId',
params: ['bagId', 'bananaId'],
},
})
const useTypedHistory = <T extends RoutesModel>() => {
const navigate = <K extends keyof T>(route: K, params: Record<(valueof T[K]['params']), string>) => {
ʌ --- does not work
}
return { navigate }
}
But that doesn't work at all. From what I've seen, valueof would allow me to infer the values, but I can't seem to make it work.
Any help would be very much appreciated
Code Coverage doesn't reach some lines of codes even though I'm testing them. :(
Here is bind.ts decorator and [[NOT COVERED]] code coverage tag I created. Currently the set(value) is not covered by the test even though I'm covering it.
type Descriptor<T> = TypedPropertyDescriptor<T>;
export default function bind<T = Function>(
target: object,
key: string,
descriptor: Descriptor<T>
): Descriptor<T> {
...
set(value: T): void {
[[22-NOT COVERED]] if (process.env.NODE_ENV !== 'test') {
[[23-NOT COVERED]] throw new Error('Unable to set new value to decorated method');
[[24-NOT COVERED]] }
[[25-NOT COVERED]] Object.defineProperty(this, key, { ...descriptor, value });
},
};
}
bind.spec.ts
My strategy is to create new class Component and test its context on call
class MockClass extends React.PureComponent<Props, State> {
#bind
getProp(): string {
const { propName } = this.props;
return propName;
}
#bind
getState(): string {
const { stateName } = this.state;
return stateName;
}
#bind
setProp(value: string): void {
this.state = { stateName: value };
}
}
...
describe('bind', () => {
const mockState = {
stateName: 'stateName',
};
const mockProp = {
propName: 'propName',
};
const mockClass = new MockClass(mockProp, mockState);
...
it('should have called it once', () => {
expect(mockClass.getProp()).toBe(mockProp.propName);
});
it('should have called it in setState', () => {
expect(mockClass.setProp('newState')).toBe(undefined); <<<- This can cover 22-25??
});
The uncovered setter is code that would be exercised if you set a value of the class property. You don't have any test code that does this. You're only getting a property named setProp then calling it. The fact that the property has "set" in its name may be confusing matters.
Your test code would have to do something like this to test the setter of the decorator:
mockClass.props.otherPropName = 'blah';
mockClass.getProp = function() {
const { otherPropName } = this.props;
return otherPropName;
};
expect(mockClass.getProp()).toEqual('blah');
I have a huge amont of data to transform into new format.
Actually I'm using map method but as it's syncronous and it's affecting performances.
dataFormatted = cmtAllRawdataDB[0].rows.map(elm => new Message(elm, configResult));
For information Message class have globally this format:
export class Data {
public value: string;
public date: Date;
constructor(dbData) {
this.value = '123';
}
}
export class Measure {
public name: string;
public unit: string;
public data: Data[];
constructor(config, dbData) {
this.name = config.name;
this.unit = config.value;
...
this.data = [new Data(dbData)];
}
}
export class Sensor {
public id: string;
public label: string;
public measures: Measure[] = [];
constructor(dbData, config) {
this.id = '123';
this.label = 'SensorType';
config.unitConfig.map(elm => this.measures.push(new Measure(elm, dbData)));
}
}
export class Message {
public id: string;
...
public sensors: Sensor[];
constructor(dbData: any, config: any) {
this.id = dbData.value._id;
....
this.sensors = [new Sensor(dbData, config)];
console.log(this.id, this.arrivalTimestamp);
}
}
Is there a way to run asynchronously this code ?
Just put this operation inside function and put it inside settimeout method, for just 10 millisecond
var example = () => {
setTimeout(() => {
return (dataFormatted = cmtAllRawdataDB[0].rows.map(
elm => new Message(elm, configResult)
));
}, 10);
};
Use async and await keywords like this way
async getDataFormatted(){ return(cmtAllRawdataDB[0].rows.map(elm => new Message(elm, configResult)));
}
let dataFormatted= await getDataFormatted();
I have the following javascript function that returns a function containing additional methods given in the object arguments.
I think the code will be more understandable than an explanation:
var scopeFunction = (object) => {
// create main function if it doesn't exist
if (!object.main) object.main = function(){
alert("No main function for this scopeFunction");
};
// add all object keys to the main function that will be return
_.each(object, function(d,i){ object.main[i] = d; });
// return main function
return object.main;
};
I want to define properly this code in typescript, this is what I did, but atom typescript (that's where I've tested it) throws errors when I try to access the keys of my returned function object.
This is how my current code look:
// TYPES
namespace ScopeFunction {
export interface Function<Obj extends MakerObject, Key extends keyof Obj> {
(): Obj["main"];
[key: Key]: Obj[Key];
}
export interface MainFunction {
(...args:any[]) : any;
}
export interface MakerObject {
[key: string]: any;
main?: MainFunction;
}
export type Maker = <Obj extends MakerObject, Key extends keyof Obj>(object:Obj) => ScopeFunction.Function<Obj, Key>;
}
// FUNC
var scopeFunction:ScopeFunction.Maker = (object) => {
// create main function if it doesn't exist
if (!object.main) object.main = function(){
alert("No main function for this scopeFunction");
};
// add all object keys to the main function that will be return
_.each(object, function(d,i){ object.main[i] = d; });
// return main function
return object.main;
};
// TEST
var test = scopeFunction({
a: 1,
b: "3",
main: () => { console.log("testLog"); return 0; }
})
var test1 = test(); // WORKS OK
var test2 = test.main(); // ALERT: Property 'main' doesn't exist on type 'Function<{ a: number; b: string; main: () => number }, "main" | "a" | "b">'
var test3 = test.a; // ALERT: Property 'a' doesn't exist on type 'Function<{ a: number; b: string; main: () => number }, "main" | "a" | "b">'
Any idea where the problem is in my definition?
There are several problems with your code:
The definitions don't compile [key: Key]: Obj[Key] is not valid, and indexer argument must be either number or string (those and only those types are valid). You need to use a mapped type instead.
(): Obj["main"] will not be a call signature of the same type as Obj["main"] is will be a function that returns whatever the property of main is.
The type of the main function is too generic, and it will not preserve any argument types.
A solution that does what you expect might be:
namespace ScopeFunction {
export type Function<Obj extends MakerObject<(...args: any[]) => any>> = Obj['main'] & {
[P in keyof Obj]: Obj[P];
}
export interface MakerObject<TMain extends (...args: any[]) => any> {
main?: TMain;
}
export type Maker = <TObj extends MakerObject<(...args: any[]) => any>>(object: TObj) => ScopeFunction.Function<TObj>;
}
// FUNC
var scopeFunction: ScopeFunction.Maker = (object) => {
// create main function if it doesn't exist
if (!object.main) object.main = function () {
alert("No main function for this scopeFunction");
};
// return main function
return Object.assign(object.main, object);
};
// TEST
var test = scopeFunction({
a: 1,
b: "3",
main: (param: number) => { console.log("testLog"); return param; }
})
var test1 = test(10);
var test2 = test.main(10);
var test3 = test.a;
Imagine I have the following interfaces
interface IMarket {
ID: number,
Name: string,
MarketDescription: string
}
interface IDepartment {
ID: number,
Name: string,
DepartmentDescription: string
}
Is there a way to store the interfaces in an object like this?
var typeMap = { Markets: IMarket, Departments: IDepartment }
I'd like to do something like this. I'd like to dynamically set the generic type for "getQueryResults" based on a string value I pass into the constructor.
export class Service {
protected baseURL = "";
protected typeName = "";
private typeMap = { Markets: IMarket, Departments: IDepartment }
constructor(typeName) {
this.baseURL = 'http://localhost/API/odata/' + typeName;
this.currentType = typeMap[typeName];
}
getQueryResults(): Promise<this.currentType> {
return new Promise<this.currentType>((resolve, reject) => {
$.getJSON(this.baseURL, function (returnValue) {
resolve(returnValue.value);
});
})
}
}
var marketService = new Service("Markets");
var topMarket = marketService.getQueryResults();
//topMarket is an instance(?) of IMarket
var departmentService = new Service("Departments");
var topDepartment = departmentServicegetQueryResults();
//topDepartment is an instance(?) of IDepartment
That can be simply solved using generics, it's exactly what it's for:
export class Service<T> {
protected baseURL = "";
constructor() {
this.baseURL = 'http://localhost/API/odata/' + typeName;
}
getQueryResults(): Promise<T> {
return new Promise<T>((resolve, reject) => {
$.getJSON(this.baseURL, function (returnValue) {
resolve(returnValue.value);
});
})
}
}
var marketService = new Service<IMarket>();
var topMarket: Promise<IMarket> = marketService.getQueryResults();
var departmentService = new Service<IDepartment>();
var topDepartment: Promise<IDepartment> = departmentService.getQueryResults();
Edit
You can use 2 more classes to "get rid" of the need to have Service<TYPE> more than once (per TYPE):
export abstract class Service<T> {
protected baseURL = "";
constructor() {
this.baseURL = 'http://localhost/API/odata/' + this.getTypeName();
}
getQueryResults(): Promise<T> {
return new Promise<T>((resolve, reject) => {
$.getJSON(this.baseURL, function (returnValue) {
resolve(returnValue.value);
});
})
}
protected abstract getTypeName(): string;
}
export class MarketsService extends Service<IMarket> {
protected getTypeName(): string {
return "Markets";
}
}
export class DepartmentsService extends Service<IDepartment> {
protected getTypeName(): string {
return "Departments";
}
}
var marketService = new MarketsService();
var topMarket: Promise<IMarket> = marketService.getQueryResults();
var departmentService = new DepartmentsService();
var topDepartment: Promise<IDepartment> = departmentService.getQueryResults();
But unlike the need to specify the type every time you use Service, these extra classes will be part of the compiled js, so it's a question of what's more important to you.
Taking a note from the TypeScript docs:
http://www.typescriptlang.org/docs/handbook/namespaces.html#namespaced-validators
namespace Validation {
export interface StringValidator {
isAcceptable(s: string): boolean;
}
It appears you would want:
namespace YourNamespace {
export interface IMarket {
ID: number,
Name: string,
MarketDescription: string
}
export interface IDepartment {
ID: number,
Name: string,
DepartmentDescription: string
}
}