Data object constructor not called when parsing JSON - javascript

I'm getting some data from a server and then parsing it into TypeScript classes. I'm trying use some inheritance - every class needs to be able to report its type.
Here's how that works:
This is the base class
import { PageElementType } from './page-element-type'
export class PageElement {
pageElementType: PageElementType;
constructor(aPageElementType: PageElementType) {
this.pageElementType = aPageElementType;
}
}
This is a derived class
import { PageElement } from './page-element.model'
import { PageElementType } from './page-element-type'
export class Metadata extends PageElement {
id: number;
body: string;
constructor(){
super(PageElementType.metadata);
}
}
Here's the service function I call to parse the data
getExam(){
let exam = this.http.get('http://orangeberry.hopto.org/api/Exam/1')
.map((response:Response) => <Exam>response.json())
.do(data => console.log(data));
return exam;
}
Seems like I'm getting some sort of plain objects. I want meaningful, functional objects that actually follow the class definition. What's the simplest and most straight-forward way of achieving this for my case?

A cast is only a hint for static code analysis but doesn't have any effect at runtime.
If you want the value to become an instance of a class, you need to create such an instance yourself:
.map((response:Response) => new Exam(response.json()))
where the constructor of the Exam class decomposes the JSON to its properties.

Related

Method substitution between objects

In nodejs, typescript, I want to substitute a method of an object with a method of another object; I have written the following very simple example to better understand my problem (the real situation is, more or less, the same):
export default class A_01 {
constructor(private variableA1: string) {}
public writeSomething() {
console.log(`${this.variableA1} from class A`);
}
}
import A_01 from "./oop_class_A";
export default class B_01 extends A_01 {
constructor(private variableB1: string) {
super(variableB1);
}
public writeSomething() {
console.log(`${this.variableB1} from class B`);
}
}
import A_01 from "./oop_class_A";
class C_01 {
constructor() {}
run() {
return new A_01("Object A_01 from class C_01"); // cannot modify this object creation!!!
}
}
import A_01 from "./oop_class_A";
import B_01 from "./oop_class_B";
const D_01 = new A_01("from_class_D_01");
D_01.writeSomething();
So, how to print from_class_D_01 from class B (and NOT from class A) ?
I have tried casting
const D_01 = new A_01("from_class_D_01") as B_01
but it's only a type and I lose it at runtime.
Not sure if this is what you need, this is a very hacky way to overwrite the writeSomething method after an A_01 instance has been created.
const D_01 = new A_01("from_class_D_01")
D_01.writeSomething = B_01.prototype.writeSomething
D_01.writeSomething()
Now it will write "from class B" even though it's an instance of A_01

Convert the Normal class to generic in typescript

I have a class to get JSON objects and convert them to the class that I want. the code is below.
import {plainToClass} from "class-transformer";
import UserDto from "../../auth/dto/user.dto";
class ConvertJson {
userData(data) {
return plainToClass(UserDto, data);
}
}
when I want to convert the class to generaic class
import {plainToClass} from "class-transformer";
import UserDto from "../../auth/dto/user.dto";
class ConvertJson<T> {
userData(data) {
return plainToClass(T, data);
}
}
I get this error
T only refers to a type, but is being used as a value here
After tweaking the code I found out that typescript generic is very different from C# or other languages so I convert the code to this so it has a generic vibe but not syntax.
import {plainToClass} from "class-transformer";
interface IClass {
new (...args : any[])
}
class ConvertJson {
constructor(private dto : IClass) {
}
userData(data) {
return plainToClass(this.dto, data);
}
}
the reason I use IClass instead of any is that I want to make sure
other teammates use class, not other types.
Enhance from Milad's comment👇
class-transformer provide some generic class to do the same as he introduced.
import { plainToClass, ClassConstructor } from "class-transformer";
class ConvertJson {
userData<TargetClass>(data: unknown, dtoClass: ClassConstructor<TargetClass>) {
return plainToClass(dtoClass, data);
}
}
My class-transformer version is v0.5.1
✍Note:
plainToClasss is deprecated❌
Use plainToInstance instead (it's same)âś…
I guess want you want to do is cast the output according to the input type
class ConvertJson<T> {
userData(data) {
return <T>plainToClass(data);
}
}

class Thing<T> extends T

Regarding the title, is something equivalent to class Thing<T> extends T possible in typescript? Preferably in a way that's readable. Currently trying this line of code results in the error Cannot find name 'T' in VSCode. Tried finding an answer on here and google, but found nothing similar.
You can't do it directly, you can crete an intermediary class and then use a type assertion to get it to fit with a custom constructor signature that returns T and the class:
class _Thing { }
const Thing: {
new <T>(): _Thing & T
} = _Thing as any;
new Thing<{ prop: string }>().prop
T is a type, only values (classes) can be extended:
function <T extends new(...arg:any[]) => any> Mixin(Parent: T) {
return class Mixin extends Parent {
// ...
}
}
const Sub = Mixin(class Parent {
works() { /*..*/ }
//...
});
(new Sub).works();

Typescript type definition: TS2604: JSX element type 'something' does not have any construct or call signatures

I have an abstract class in "File1":
// File1.tsx
import * as React from 'react';
export interface IProps {
prop1: string;
prop2: number;
}
export abstract class Something extends React.Component<IProps> {
public typeName: string;
}
then, in other file (File2) i define infinite classes extending from abstract class Something:
// File2.tsx
import { Something } from './File1';
export class Something1 extends Something {
public typeName: string = 'Type1';
public render() {
return <div>Something 1</div>
}
}
export class Something2 extends Something {
public typeName: string = 'Type2';
public render() {
return <div>Something 2</div>
}
}
Now, here is my problem:
In a 3rd File, i import the classes defined before (Something1 or Something2) and then i passing this class to 2 different components: ReadProperty and RenderComponent. In the first component i need accessing to the property typeName of the class and do some stuff and in the second file i need render that class:
// File3.tsx
import { Something } from './File1';
import { Something1, Something2 } from './File2';
interface IReadProperty {
something: Something;
};
const ReadProperty: React.SFC<IReadProperty> = ({ something }) => {
// here i can access to property typeName. No problem here.
something.typeName // Type1 | Type2 | ... Infinite
...do some stuff
}
interface IRenderComponent {
something: Something;
}
const RenderComponent: React.SFC<IRenderComponent> = ({ something }) => {
// i need do some stuff here before render "something" component
// here i get an error when i try render the component
return <something />;
}
const Main: React.SFC = () => {
return (
<div>
<ReadProperty something={Something1} />
<RenderComponent something={Something1} />
</div>
);
}
but when i try to render the component in RenderComponent, i get the follow error: TS2604: JSX element type 'something' does not have any construct or call signatures.
What is wrong here? i defined type 'something' as abstract Class Something because i can define infinite Classes that extend from Something, so i can't define using: something: Something1 | Something2 | Something50 ...;
here is an example that i trying to do: https://codesandbox.io/s/18yzvkx8jj
The issue is that in your definition of IRenderComponent, you're saying that something is an instance of Something, whereas what you want is a constructor type. One other thing is that React generally complains when you try to instantiate components with lowercase names. Try this:
interface IRenderComponent {
Something: new (props: IProps) => Something1;
};
const RenderComponent: React.SFC<IRenderComponent> = ({ Something }) => {
return <Something prop1="foo" prop2={3} />;
}
For IReadProperty, it looks like you do want an instance of Something (since you want to access typeName, which is an instance property). However, you can't actually pass an instantiated component:
<ReadProperty something={<Something1 prop1="" prop2={3} />;} /> //Error
This is because <Something1 ... /> isn't actually an instance of Something1 - it's an instance of JSX.Element. You could pass an instance of Something1, like this:
<ReadProperty something={new Something1({prop1: "", prop2: 3})} />
But I imagine that's not what you want, since you can't actually use the resulting instance in rendering.
The best way to address the IReadProperty issue depends on what you're actually trying to accomplish. In general, React favors composition over inheritance and reflection, so it might be easier to consider how to achieve your goal by starting with base components and composing them, or using higher-order components.
Make sure that you have jsx:react in tsconfig.json
Make sure that you have consistent version of react react-dom #types/react and #types/react-dom package versions.

Typescript: How to extend two classes?

I want to save my time and reuse common code across classes that extend PIXI classes (a 2d webGl renderer library).
Object Interfaces:
module Game.Core {
export interface IObject {}
export interface IManagedObject extends IObject{
getKeyInManager(key: string): string;
setKeyInManager(key: string): IObject;
}
}
My issue is that the code inside getKeyInManager and setKeyInManager will not change and I want to reuse it, not to duplicate it, here is the implementation:
export class ObjectThatShouldAlsoBeExtended{
private _keyInManager: string;
public getKeyInManager(key: string): string{
return this._keyInManager;
}
public setKeyInManager(key: string): DisplayObject{
this._keyInManager = key;
return this;
}
}
What I want to do is to automatically add, through a Manager.add(), the key used in the manager to reference the object inside the object itself in its property _keyInManager.
So, let's take an example with a Texture. Here goes the TextureManager
module Game.Managers {
export class TextureManager extends Game.Managers.Manager {
public createFromLocalImage(name: string, relativePath: string): Game.Core.Texture{
return this.add(name, Game.Core.Texture.fromImage("/" + relativePath)).get(name);
}
}
}
When I do this.add(), I want the Game.Managers.Manager add() method to call a method which would exist on the object returned by Game.Core.Texture.fromImage("/" + relativePath). This object, in this case would be a Texture:
module Game.Core {
// I must extend PIXI.Texture, but I need to inject the methods in IManagedObject.
export class Texture extends PIXI.Texture {
}
}
I know that IManagedObject is an interface and cannot contain implementation, but I don't know what to write to inject the class ObjectThatShouldAlsoBeExtended inside my Texture class. Knowing that the same process would be required for Sprite, TilingSprite, Layer and more.
I need experienced TypeScript feedback/advice here, it must be possible to do it, but not by multiple extends since only one is possible at the time, I didn't find any other solution.
There is a little known feature in TypeScript that allows you to use Mixins to create re-usable small objects. You can compose these into larger objects using multiple inheritance (multiple inheritance is not allowed for classes, but it is allowed for mixins - which are like interfaces with an associated implenentation).
More information on TypeScript Mixins
I think you could use this technique to share common components between many classes in your game and to re-use many of these components from a single class in your game:
Here is a quick Mixins demo... first, the flavours that you want to mix:
class CanEat {
public eat() {
alert('Munch Munch.');
}
}
class CanSleep {
sleep() {
alert('Zzzzzzz.');
}
}
Then the magic method for Mixin creation (you only need this once somewhere in your program...)
function applyMixins(derivedCtor: any, baseCtors: any[]) {
baseCtors.forEach(baseCtor => {
Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
if (name !== 'constructor') {
derivedCtor.prototype[name] = baseCtor.prototype[name];
}
});
});
}
And then you can create classes with multiple inheritance from mixin flavours:
class Being implements CanEat, CanSleep {
eat: () => void;
sleep: () => void;
}
applyMixins (Being, [CanEat, CanSleep]);
Note that there is no actual implementation in this class - just enough to make it pass the requirements of the "interfaces". But when we use this class - it all works.
var being = new Being();
// Zzzzzzz...
being.sleep();
I would suggest using the new mixins approach described there: https://blogs.msdn.microsoft.com/typescript/2017/02/22/announcing-typescript-2-2/
This approach is better, than the "applyMixins" approach described by Fenton, because the autocompiler would help you and show all the methods / properties from the both base and 2nd inheritance classes.
This approach might be checked on the TS Playground site.
Here is the implementation:
class MainClass {
testMainClass() {
alert("testMainClass");
}
}
const addSecondInheritance = (BaseClass: { new(...args) }) => {
return class extends BaseClass {
testSecondInheritance() {
alert("testSecondInheritance");
}
}
}
// Prepare the new class, which "inherits" 2 classes (MainClass and the cass declared in the addSecondInheritance method)
const SecondInheritanceClass = addSecondInheritance(MainClass);
// Create object from the new prepared class
const secondInheritanceObj = new SecondInheritanceClass();
secondInheritanceObj.testMainClass();
secondInheritanceObj.testSecondInheritance();
I found an up-to-date & unparalleled solution: https://www.npmjs.com/package/ts-mixer
You are welcome :)
TypeScript supports decorators, and using that feature plus a little library called typescript-mix you can use mixins to have multiple inheritance with just a couple of lines
// The following line is only for intellisense to work
interface Shopperholic extends Buyer, Transportable {}
class Shopperholic {
// The following line is where we "extend" from other 2 classes
#use( Buyer, Transportable ) this
price = 2000;
}
I think there is a much better approach, that allows for solid type-safety and scalability.
First declare interfaces that you want to implement on your target class:
interface IBar {
doBarThings(): void;
}
interface IBazz {
doBazzThings(): void;
}
class Foo implements IBar, IBazz {}
Now we have to add the implementation to the Foo class. We can use class mixins that also implements these interfaces:
class Base {}
type Constructor<I = Base> = new (...args: any[]) => I;
function Bar<T extends Constructor>(constructor: T = Base as any) {
return class extends constructor implements IBar {
public doBarThings() {
console.log("Do bar!");
}
};
}
function Bazz<T extends Constructor>(constructor: T = Base as any) {
return class extends constructor implements IBazz {
public doBazzThings() {
console.log("Do bazz!");
}
};
}
Extend the Foo class with the class mixins:
class Foo extends Bar(Bazz()) implements IBar, IBazz {
public doBarThings() {
super.doBarThings();
console.log("Override mixin");
}
}
const foo = new Foo();
foo.doBazzThings(); // Do bazz!
foo.doBarThings(); // Do bar! // Override mixin
Unfortunately typescript does not support multiple inheritance. Therefore there is no completely trivial answer, you will probably have to restructure your program
Here are a few suggestions:
If this additional class contains behaviour that many of your subclasses share, it makes sense to insert it into the class hierarchy, somewhere at the top. Maybe you could derive the common superclass of Sprite, Texture, Layer, ... from this class ? This would be a good choice, if you can find a good spot in the type hirarchy. But I would not recommend to just insert this class at a random point. Inheritance expresses an "Is a - relationship" e.g. a dog is an animal, a texture is an instance of this class. You would have to ask yourself, if this really models the relationship between the objects in your code. A logical inheritance tree is very valuable
If the additional class does not fit logically into the type hierarchy, you could use aggregation. That means that you add an instance variable of the type of this class to a common superclass of Sprite, Texture, Layer, ... Then you can access the variable with its getter/setter in all subclasses. This models a "Has a - relationship".
You could also convert your class into an interface. Then you could extend the interface with all your classes but would have to implement the methods correctly in each class. This means some code redundancy but in this case not much.
You have to decide for yourself which approach you like best. Personally I would recommend to convert the class to an interface.
One tip: Typescript offers properties, which are syntactic sugar for getters and setters. You might want to take a look at this: http://blogs.microsoft.co.il/gilf/2013/01/22/creating-properties-in-typescript/
A very hacky solution would be to loop through the class you want to inherit from adding the functions one by one to the new parent class
class ChildA {
public static x = 5
}
class ChildB {
public static y = 6
}
class Parent {}
for (const property in ChildA) {
Parent[property] = ChildA[property]
}
for (const property in ChildB) {
Parent[property] = ChildB[property]
}
Parent.x
// 5
Parent.y
// 6
All properties of ChildA and ChildB can now be accessed from the Parent class, however they will not be recognised meaning that you will receive warnings such as Property 'x' does not exist on 'typeof Parent'
In design patterns there is a principle called "favouring composition over inheritance". It says instead of inheriting Class B from Class A ,put an instance of class A inside class B as a property and then you can use functionalities of class A inside class B.
You can see some examples of that here and here.
In my case I used concatenative inheritance.
Maybe for someone this way will be helpful:
class Sprite {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}
class Plane extends Sprite {
fly(): string {
return 'I can Fly!'
}
}
class Enemy {
isEnemy = true;
}
class Player {
isPlayer = true;
}
// You can create factory functions to create new instances
const enemyPlane = Object.assign(new Plane(1, 1), new Enemy());
const playerPlane = Object.assign(new Plane(2, 2), new Player());
Also I recommend reading Eric Elliott's articles about js inheritance:
The Heart & Soul of Prototypal OO: Concatenative Inheritance
3 Different Kinds of Prototypal Inheritance
There are so many good answers here already, but i just want to show with an example that you can add additional functionality to the class being extended;
function applyMixins(derivedCtor: any, baseCtors: any[]) {
baseCtors.forEach(baseCtor => {
Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
if (name !== 'constructor') {
derivedCtor.prototype[name] = baseCtor.prototype[name];
}
});
});
}
class Class1 {
doWork() {
console.log('Working');
}
}
class Class2 {
sleep() {
console.log('Sleeping');
}
}
class FatClass implements Class1, Class2 {
doWork: () => void = () => { };
sleep: () => void = () => { };
x: number = 23;
private _z: number = 80;
get z(): number {
return this._z;
}
set z(newZ) {
this._z = newZ;
}
saySomething(y: string) {
console.log(`Just saying ${y}...`);
}
}
applyMixins(FatClass, [Class1, Class2]);
let fatClass = new FatClass();
fatClass.doWork();
fatClass.saySomething("nothing");
console.log(fatClass.x);
You can call Dynamic Inheritance or Class Factory.
type ClassConstructor<T> = {
new (...args: any[]): T;
};
interface IA {
a: string;
}
interface IB {
b: string;
}
interface IAB extends IA, IB {}
class EmptyClass {}
function GetA<T>(t: ClassConstructor<T> = EmptyClass as any) {
class A extends (t as any) implements IA {
a = 'Default value a';
}
return A as unknown as ClassConstructor<IA & T>;
}
function GetB<T>(t: ClassConstructor<T> = EmptyClass as any) {
class B extends (t as any) implements IB {
b = 'Default value b';
}
return B as unknown as ClassConstructor<IB & T>;
}
class C extends GetA<IB>(GetB()) implements IAB {}
Found a way:
export interface IsA {
aWork(): void
}
export interface IsB {
}
export class A implements IsA {
aWork() { }
}
export class B implements IsB {
bWork() { }
}
export interface IsAB extends A, B {}
export class IsAB {}
Then you can
export class C extends IsAB {}
or even
const c = new IsAB {}

Categories

Resources