Storing interfaces in object - javascript

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
}
}

Related

how to call a function class javascript

I wanted to create a class with private parameters and functions to access the data I want. You can see this :
export class Product {
private name: string;
private type: string;
private longDetail: string;
private shortDetail: string;
private stock: number;
private price: number;
private linkImage: string;
private id: number;
constructor(
name: string,
type: string,
longDetail: string,
shortDetail: string,
stock: number,
price: number,
linkImage: string,
id: number
) {
this.name = name;
this.type = type;
this.longDetail = longDetail;
this.shortDetail = shortDetail;
this.stock = stock;
this.price = price;
this.linkImage = linkImage;
this.id = id;
}
getName(): string {
return this.name;
}
getType(): string {
return this.type;
}
getLongDetail(): string {
return this.longDetail;
}
getShortDetail(): string {
return this.shortDetail;
}
getStock(): number {
return this.stock;
}
getPrice(): number {
return this.price;
}
getLinkImage(): string {
return this.linkImage;
}
getId(): number {
return this.id;
}
}
And when I want to call a function in a component I am told :
ProductListComponent.html:15 ERROR TypeError: newProduct.getName is not a function
Do you have a solution ? Thank you very much in advance !
EDIT :
This is the code called after the click in front end
addProductBasket(newProduct: Product) {
const newClientBasket = this.createNewClientBasketWithAdd(
this.clientBasket.getValue(),
newProduct
)
this.clientBasket.next(newClientBasket)
console.log(newClientBasket)
}
private createNewClientBasketWithAdd(
oldClientBasket: BasketProduct[],
newProduct: Product
): BasketProduct[] {
const found = oldClientBasket.find((product) => {
if (product.getId() === newProduct.getId()) {
product.addOneProduct()
}
})
if (found === undefined) {
console.log(newProduct.getName())
oldClientBasket.push(
new BasketProduct(
newProduct.getName(),
newProduct.getType(),
newProduct.getLongDetail(),
newProduct.getShortDetail(),
newProduct.getStock(),
newProduct.getPrice(),
newProduct.getLinkImage(),
newProduct.getId()
)
)
}
return oldClientBasket
}
It's my apiservice to get data
export class ApiService {
private dataApi: BehaviorSubject<Product[]> = new BehaviorSubject<Product[]>([]);
constructor(private http: HttpClient) {
this.getDataFromApi();
}
private getDataFromApi(){
this.http
.get<Product[]>("../../assets/data.json")
.toPromise()
.then((data) => this.dataApi.next(data));
}
public getData():Observable<Product[]>{
return this.dataApi.asObservable();
}
}
You should have an instance of Product class before accessing its methods.
var newProduct = new Product();
newProduct.getName();
After David's help in commenting, I understood that I had to instantiate the data I receive in http client.
I then modified the constructor and my client http get
constructor(obj: any) {
Object.assign(this, obj);
}
and
private getDataFromApi(){
this.http
.get<Product[]>("../../assets/data.json").pipe()
.toPromise()
.then((data) => {
const productList = data.map(product => new Product(product));
this.dataApi.next(productList)});
}

Deserialize a JSON object into its original class

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)

Transforme synchronous Map method to async traitment

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();

Map JSON to existing deep object structure

Say I have the following Typescript model:
class Person{
public Address: Address;
public FirstName: string;
public LastName: string;
constructor(){
this.Address = new Address();
}
}
And I get an exact representation of this object from a server via JSON.
How would I go about generically setting the properties of both the Person and the Address but leave the existing objects intact.
So similar to this, but generically:
public SetData(json:any){
this.Address.City = json.Address.City;
this.Address.Province = json.Address.Province;
this.FirstName = json.FirstName;
}
The gotcha being that the original objects must remain and have there setters called as they are Mobx observables. This rules out Object.assign and any 'extend' methods I have found.
Thanks.
In somewhat simplified case you can do it manually without too much effort:
class Address
{
public City: string;
public Province: string;
}
class Person{
public Address: Address;
public FirstName: string;
public LastName: string;
constructor() {
this.Address = new Address();
}
private SetDataInternal(target: any, json: any)
{
if (typeof json === "undefined" || json === null)
{
return;
}
for (let propName of Object.keys(json))
{
const val = target[propName];
if (typeof val === "object")
{
this.SetDataInternal(val, json[propName]);
}
else
{
target[propName] = json[propName];
}
}
}
public SetData(json: any)
{
this.SetDataInternal(this, json);
}
}
const json = {
Address: {
City: "AAA",
Province: "BBB"
},
FirstName: "CCC"
}
const p = new Person();
p.SetData(json);
console.log(p);
It surely miss some checks and corner cases validations, but apart from that it does what you ask for.
My final implementation based of Amids:
import * as _ from "underscore";
export class ObjectMapper
{
public static MapObject(source: any, destination: any) {
_.mapObject(source, (val, key) => {
if(_.isObject(val))
{
this.MapObject(val, destination[key]);
}
else if(_.isArray(val))
{
const array = destination[key];
for(var i in val)
{
const newObject = {};
_.extend(newObject, val[i]);
array.push(newObject);
}
}
else
{
destination[key] = val;
}
});
}
}

Returning a function from Javascript into TypeScript

I have just started out with Typescript and want to convert the following Javascript into Typescript.
The first code block is the actual JS and the second is the TypeScript which I have so far.
I think it's mostly right but the bit I am having trouble working out is the return function at the end of the Javascript. How would I write this bit in my TypeScript code?
var ABC = ABC || {};
ABC.navigationService = function () {
var navigate = {};
function navigateChart(e, query) {
}
function drillChart(direction, type, query) {
}
function canDrillUp(series, query) {
}
function drillUp(series, query) {
}
return {
navigate: navigateChart,
drill: drillChart,
canDrillUp: canDrillUp,
drillUp: drillUp
}
}
angular.module("MyApp").service("navigationService", [ABC.navigationService]);
and with the typescript, this is what I have so far, including only the functions for which I want to return the results as in the Javascript:-
module ABC.Visualisation.Services {
'use strict';
var xPos = 0;
var yPos = 0;
var canDrilldownPoint = false;
var canDrillupPoint = false;
var canDrilldownSeries = false;
var canDrillupSeries = false;
var navigate = {
loading: false,
showGui: false,
canDrilldownPoint: canDrilldownPoint,
canDrillupPoint: canDrillupPoint,
canDrilldownSeries: canDrilldownSeries,
canDrillupSeries: canDrillupSeries,
x: xPos,
y: yPos
};
export class NavigationService implements INavigationService {
public navigateChart(e: any, query: any) {
}
public drillChart(direction: string, type: string, query: any): void {
}
public canDrillUp(series: any, query: any): boolean {
}
public drillUp(series: any, query: any): void {
}
}
}
angular.module("MyApp").service("NavigationService", [ABC.Visualisation.Services.NavigationService]);
1) I believe that
angular.module("MyApp").service("NavigationService", [ABC.Visualisation.Services.NavigationService]);
should be
angular.module("MyApp").service("NavigationService", ABC.Visualisation.Services.NavigationService);
2) This is how you can put variables to a module: Creating a variable with get/set inside a module in Typescript

Categories