Using Typescript object spread operator with this keyword - typescript2.0

In my code I often have to copy data from json to instantiate class in constructor.
function append(dst, src) {
for (let key in src) {
if (src.hasOwnProperty(key) {
dst[key] = src[key];
}
}
};
export class DataClass {
id: number;
title: string;
content: string;
img: null | string;
author: string;
// no methods, just raw data from API
}
export class AdoptedClass1 extends DataClass {
// has same fields as DataClass
showcase: string;
constructor (data: DataClass) {
append(data, this);
// do some stuff
}
}
// similar code for AdoptedClass2
I'm wondering if I can replace append function call in constructor with object spread operator

For your need I'll prefer to use Object.assign(this, data) over your custom made append function. Nevertheless have a look at the documentation to understand the limitation of it.
Back to your main question: it is not possible to use the spread operator to do what you want. Many people are interested in that feature but it has been put on hold as you can see here.
To get closer of what you ask we can refactor your code a little:
export class DataClass {
id: number
title: string
content: string
img: null | string
author: string
constructor(data: DataClass) {
Object.assign(this, data)
}
}
export class AdoptedClass1 extends DataClass {
showcase: string
constructor (data: DataClass) {
super(data)
// do some stuff
}
}
By simply adding the constructor to the data class you will be allowed to use super(data) in children and IMHO the code will be a lot cleaner.

You can use object spread operator by replacing this line:
append(data,this)
with this line
data = {...data, ...this};

Related

How do I create an instance of a Class passed as parameter to a function?

I'm building a library in Kotlin and here's my usecase
I have a base class
abstract class Component(...) {
// ... class body
}
I want the users of my library to define their own sub-classes like say:
class MyComponent() : Component() {
// .. class body
}
How can I write a helper function that takes in this derived class as a param and create an instance out of it. Something like:
fun helper(component: Class, props: HashMap<String, String>) : Component {
// somehow create a new instance of Class and return it?
}
Thanks!
You can have users pass a constructor reference:
fun helper(componentConstructor: ()->Component, props: Map<String, String>) : Component {
val component = componentConstructor()
// set it up and return it.
}
// usage:
val component = helper(::MyComponent, emptyMap())
Better for props not to require a specific type of map since it doesn’t matter here. Needless burden for users of your library.
abstract class Component(val prop1: String, val prop2: String) {
// ... class body
}
class MyComponent(prop1: String, prop2: String) : Component (prop1, prop2) {
// ... class body
}
fun helper(component: Class<MyComponent>, props: Map<String, String>): Component {
val constructor = component.constructors.first { it.parameterCount == props.size }
val arguments = props.values.toTypedArray()
return constructor.newInstance(*arguments) as Component
}
val instance = helper(MyComponent::class.java, mapOf("prop1" to "value1", "prop2" to "value2"))
println(instance.prop1 + ", " + instance.prop2) // Prints: value1, value2

Typescript - Why should I rewrite all members to implement an interface?

I have an interface with some optional variables like:
interface A {
id: string;
name?: string;
email?: string;
...
}
What I want to do is that
class B implements A {
constructor(x: string, y: string, ...) {
this.id = x;
this.name = y;
...
}
getName(): string {
return this.name;
}
}
I don't want to rewrite all members that I will use and I need some members to stay optional. Each interface will be implemented with only one class, so if I rewrite all the members in class B than the interface A becomes useless.
You may ask "Why you need interface A anyway?". I need it because I am using it from some other project, and I have to extend and implement it with some functions.
Any solution or different idea about that implementation?
One option is to use Object.assign like so:
interface A {
id: string;
name?: string;
email?: string;
}
class B implements A {
id: string;
name: string;
email: string;
constructor(data: A) {
Object.assign(this, data);
}
getName(): string {
return this.name;
}
}
(code in playground)

How to take a subset of an object using an interface?

Suppose I have this class and interface
class User {
name: string;
age: number;
isAdmin: boolean;
}
interface IUser {
name: string;
age: number;
}
And then I get this json object from somewhere
const data = {
name: "John",
age: 25,
isAdmin: true
}
I want to subset data using IUser and remove the isAdmin property like this
let user = subset<IUser>(data);
// user is now { name: "John", age: 25 }
// can safely insert user in the db
My question is how do I implement that function in TypeScript?
function subset<T>(obj: object) {
// keep all properties of obj that are in T
// keep, all optional properties in T
// remove any properties out of T
}
There's no way to do that which is better than:
function subset(obj: IUser) {
return {
name: obj.name,
age: obj.age
}
}
The typescript interfaces don't exist at runtime (which is when subset is invoked) so you cannot use the IUser interface to know which properties are needed and which aren't.
You can use a class which does "survive" the compilation process but:
class IUser {
name: string;
age: number;
}
Compiles to:
var IUser = (function () {
function IUser() {
}
return IUser;
}());
As you can see, the properties aren't part of the compiled output, as the class members are only added to the instance and not to the class, so even a class won't help you.
You can use decorator and metadata (more on that here) but that sounds like an overkill for your scenario.
Another option for a more generic subset function is:
function subset<T>(obj: T, ...keys: (keyof T)[]) {
const result = {} as T;
keys.forEach(key => result[key] = obj[key]);
return result;
}
let user1 = subset(data, "name", "age");
let user2 = subset(data, "name", "ag"); // error: Argument of type '"ag"' is not assignable to parameter of type '"name" | "age" | "isAdmin"'

Specialized Singleton implementation

I am looking for specialized singleton implementation, probably I might be using wrong terminology and hence looking for expert suggestion. Here is my scenario:
There is common code which can be called by ComponentA or ComponentB. I need to push telemetry data from the common code. Telemetry needs to have information that whether this common code get called by ComponentA or ComponentB.
So common code will have just this line of code:
telemetry.pushData(this._area, data);
where this._area tells the telemetry data is getting pushed for which component
I need to push telemetry data from multiple places so it would be good if object got created once and used through out the code lifetime
One option I can think of passing component context to the common code which in mind doesn't look right, hence looking for suggestion what kind of pattern one should use in this case?
This is what I am thinking
// Telemetry.ts file present in shared code
export class Telemetry extends Singleton {
public constructor() {
super();
}
public static instance(): Telemetry {
return super.instance<Telemetry>(Telemetry);
}
public publishEvent(data): void {
if (!this.area) {
throw new Error("Error: Initialize telemetry class with right area");
}
pushtelemetryData(this.area, data);
}
public area: string;
}
// Create Telemetry object from component A
Telemetry.instance().area = "ComponentA";
// Shared code will call telemetry publishEvent
Telemetry.instance().publishEvent(data);
Thanks
It's not a good pattern to use in TypeScript where you would generally inject dependencies.
If you must absolutely do it then you can do it by faking it somewhat:
namespace Telemetry {
var instance : SingletonSomething;
export function push(data: Any) : void {
if (instance == null) {
instance = new SingletonSomething();
}
instance.push(data);
}
class SingletonSomething() { ... }
}
and then you could call
Telemetry.push(data);
You can imitate the singleton pattern in typescript easily:
class Telemetry {
private static instance: Telemetry;
public static getInstance(): Telemetry {
if (Telemetry.instance == null) {
Telemetry.instance = new Telemetry();
}
return Telemetry.instance;
}
...
}
If you have your code in some sort of closure (module, namespace, etc) then you can replace the static member with:
let telemetryInstance: Telemetry;
export class Telemetry {
public static getInstance(): Telemetry {
if (telemetryInstance == null) {
telemetryInstance = new Telemetry();
}
return telemetryInstance;
}
...
}
But then you can also replace the static method with:
let telemetryInstance: Telemetry;
export function getTelemetryInstance(): Telemetry {
if (telemetryInstance == null) {
telemetryInstance = new Telemetry();
}
return telemetryInstance;
}
export class Telemetry {
...
}
At this point, in case you are using some sort of closure, you might ask yourself if you really need the class at all?
If you use this as a module:
// telemetry.ts
export interface TelemetryData {
...
}
export function pushData(data: TelemetryData): void {
...
}
Then you get exactly what you're looking for, and this is more of the "javascript way" of doing it.
Edit
In the telemetry module there's no need to know the users of it.
If the Telemetry.pushData function needs to have information about the object that called it then define an interface for it:
// telemetry.ts
export interface TelemetryData {
...
}
export interface TelemetryComponent {
name: string;
...
}
export function pushData(data: TelemetryData, component: TelemetryComponent): void {
...
}
Then in the other modules, where you use it:
// someModule.ts
import * as Telemetry from "./telemetry";
class MyComponent implement Telemetry.TelemetryComponent {
// can also be a simple string property
public get name() {
return "MyComponent";
}
fn() {
...
Telemetry.pushData({ ... }, this);
}
}
2nd Edit
Because you are using a module system, your module files are enough to make singletons, there's no need for a class to achieve that.
You can do this:
// telemetry.ts
let area: string;
export interface TelemetryData {
...
}
export function setArea(usedArea: string) {
area = usedArea;
}
export function pushData(data: TelemetryData): void {
...
}
Then:
Telemetry.setArea("ComponentA");
...
Telemetry.publishEvent(data);
The telemetry module will be created only once per page, so you can treat the entire module as a singleton.
Export only the functions that are needed.

TypeScript + Class + createjs.EventDispatcher

I'm trying to use the createjs EventDispatcher as a way to dispatchEvents from a class. I'm extending my class using createjs.EventDispatcher and using the dispatchEvent to trigger the event.
I get the following error when this line isthis.dispatchEvent(createJSEvent); executed:
Uncaught InvalidStateError: Failed to execute 'dispatchEvent' on 'EventTarget': The event provided is null.
Simplified TypeScript code to demonstrate what I'd like to do:
export class deviceOrientation extends createjs.EventDispatcher {
constructor() {
super();
// wait 2 seconds and then fire testDispatch
setTimeout(this.testDispatch(), 2000);
}
testDispatch():void {
var createJSEvent:createjs.Event = new createjs.Event("change", true, true);
this.dispatchEvent(createJSEvent);
}
}
// This is the starting function
export function appExternalModuleTest(): void {
let _deviceOrientation: deviceOrientation;
_deviceOrientation = new deviceOrientation();
_deviceOrientation.addEventListener("change", () => this.changeOrientation());
//_deviceOrientation.on("progress", () => this.changeOrientation());
}
export function changeOrientationi(event: Event): void {
console.log('orienationHasChanged ');
}
I'm using easeljs-0.8.1.min.js
I'm not sure if this is possible with CreateJS. Is there a better approach?
Any help is greatly appreciated.
The problem looks strange, because I do almost the same in my project and don't have any problems.
In a nutshell, I have a d.ts file for createjs classes declaration and I use these declarations in my "normal" typescript classes.
For example:
d.ts:
declare module createjs
{
export class EventDispatcher
{
addEventListener(type: string, listener: any, useCapture?: boolean): void;
removeEventListener(type: string, listener: any, useCapture?: boolean): void;
removeAllEventListener(type?: string): void;
dispatchEvent(event: Event): boolean;
}
export class Event
{
public type: string;
public target: any;
public currentTarget: any;
constructor(type: string, bubbling?: boolean, cancelable?: boolean);
clone(): Event;
}
}
Normal class:
module flashist
{
export class TestEventDispatcher extends createjs.EventDispatcher
{
public constructor()
{
super();
}
public testDispatch(): void
{
var tempEvent: createjs.Event = new createjs.Event("test");
this.dispatchEvent(tempEvent);
}
}
}
And somewhere else in the code you should create an instance of the TestEventDispatcher class. Something like:
this.testDispatcher = new TestEventDispatcher();
this.testDispatcher.addEventListener("test", (event: createjs.Event) => alert("Test Event Listener"));
this.testDispatcher.testDispatch();
I've just tested the code and it works for me.
The only idea I have is to make sure that the easel.js file is loaded before your main app files.