I have a problem with typescript. The problem is that it suddenly stopped compiling class properties to the .js file. It compiles everything, the functions the constructor but the properties aren't there.
Server.ts
export class Server{
id: String;
prefix: String;
constructor(){
}
copy(object){
object = JSON.parse(JSON.stringify(object));
console.log(this);
for (const k in object) {
if(this.hasOwnProperty(k))
this[k] = object[k];
}
console.log(this);
}
}
Server.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Server = void 0;
class Server {
constructor() {
}
copy(object) {
object = JSON.parse(JSON.stringify(object));
console.log(this);
for (const k in object) {
if (this.hasOwnProperty(k))
this[k] = object[k];
}
console.log(this);
}
}
exports.Server = Server;
Scenario.ts
import { percentageChance } from "../utilities/percentageChance";
import { Champion } from "./Champion";
export class Scenario {
name: String;
description: String;
champion: Champion;
choices: string[];
outcome: number[];
final: string[];
constructor(name: String, description: string | String, champion: Champion, choices: string[], outcome: number[], final: string[]) {
this.name = name;
this.description = description;
this.champion = champion;
this.choices = choices;
this.outcome = outcome;
this.final = final;
}
calculateOutcome(choiceIndex : number) {
if(percentageChance(this.choices, this.outcome) == this.choices[choiceIndex]){
return true;
}
return false;
}
}
Scenario.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Scenario = void 0;
const percentageChance_1 = require("../utilities/percentageChance");
class Scenario {
constructor(name, description, champion, choices, outcome, final) {
this.name = name;
this.description = description;
this.champion = champion;
this.choices = choices;
this.outcome = outcome;
this.final = final;
}
calculateOutcome(choiceIndex) {
if (percentageChance_1.percentageChance(this.choices, this.outcome) == this.choices[choiceIndex]) {
return true;
}
return false;
}
}
exports.Scenario = Scenario;
Has anyone had anything like this happened? If yes then how should it fix this?
I think that one is easy, as JS is a dynamically typed language no property that was not assigned a value will be created. So typescript will leave those properties to be created on-demand.
If you put a default value in class properties, it makes them transpile to JS on compile time.
export class Scenario {
name: String = '';
description: String = '';
champion: Champion = new Champion();
choices: string[] = [];
outcome: number[] = [];
final: string[] = [];
//...
}
Had the same problem using a method to copy properties of the same values from another object, since my properties do not exist (because of absent default value) nothing was copied...
Related
I have a simple class which creates classes from JSON objects. Here are the types. When I call map on the destructured SNode, it complains that an array of ANode [] is not assignable to ANode | BNode. My expectation is the map takes each object from the objects in the destructured children ANode array so I did not expect this error. Can someone explain why this is happening? When I hover over the { children } = snode in the IDE, it shows me that children is an array of ANode[], so why is map not passing individual ANodes? My goal is to be able to return a specific type when getClass determines the type of node passed in. As I am new to Typescript, I don't know how to do that to accomplish my goal.
interface ANode {
id: string;
value: string;
}
interface BNode {
id: string;
value: string;
}
interface SNode {
sid: string;
children: Array<ANode>;
}
function isSNode(node: any): node is SNode {
return node && "sid" in node;
}
class SObject {
constructor(public children: Map<string, ANode>) {}
}
type ValidNode = ANode | BNode;
function getClass(node: ValidNode): SObject {
if (isSNode(node)) {
return createSNodeClass(node);
}
}
function createSNodeClass(snode: SNode): SObject {
const { children = [] } = snode;
return new SObject(toMap(children.map(getClass))); //
//Error
//SNode [] is not assignable to getClass of ANode | BNode
}
function toMap(nodes: ANode[]) {
const m = new Map<string, ANode>();
for (const node of nodes) {
m.set(node.id, node);
}
return m;
}
class ANode {
constructor(
public id: string,
public value: string,){}
}
class BNode {
constructor(
public id: string,
public value: string,){}
}
class SNode{
constructor(
public sid: string,
public children: Array<ANode>,){}
}
function isSNode(node:any): node is SNode{
return node && node instanceof SNode;
}
function isANode(node:any): node is ANode{
return node && node instanceof ANode;
}
function isBNode(node:any): node is BNode{
return node && node instanceof BNode;
}
class SObject {
constructor(
public children: ANodeClass[]
){}
}
type ValidNode = ANode | BNode | SNode
type FinalOutput = ANodeClass | BNodeClass | SObject
class ANodeClass {
constructor(
public node: ANode){
this.id = node.id;
this.value = node.value;
}
id: string;
value: string;
}
class BNodeClass {
constructor(
public node: BNode){}
id: string;
value: string;
}
function getClass(node: ValidNode): FinalOutput | undefined{
if(isSNode(node)){
return createSNodeClass(node)
}else if(isANode(node)){
return new ANodeClass(node)
}else if(isBNode(node)){
return new BNodeClass(node)
}else{
throw new Error('Not Allowed type');
}
}
function createSNodeClass(snode:SNode): SObject{
const {children = []} = snode;
return new SObject(children.map(node =>
getClass(node) as ANodeClass
));
}
function toMap(nodes: ANodeClass []){
const m = new Map<string, ANodeClass>();
for(const node of nodes){
m.set(node.id, node);
}
return m;
}
I'm new to Webpack and typescrypt, so it may be a simple error, but I'm not capable to find it.
I import my model from the main.ts file :
import View from "./View/View";
import Controller from "./Controller/Controller";
import Inventaire from "./Modele/Modele";
require("tether");
require("bootstrap");
function main(): void {
let model = new Inventaire();
let controller = new Controller(model);
let view = new View(controller);
}
main();
but when I run it I have an error in the console :
TypeError: Modele_1.default is not a constructor
model.ts :
export default class Inventaire {
private _achats: Achat[];
constructor(achats: Achat[] = [/*default values*/]) {
this._achats = achats;
};
/*...*/
}
class Achat {
Nom: string;
private _Quantite: number;
Prix: number;
Description: string;
Poids: number;
Photo?: string[]; //path of the image
constructor(nom: string, prix: number, description: string, poids: number = 0, photo?: string[]) {
this.Nom = nom;
this.Prix = prix;
this.Description = description;
this.Poids = poids;
if (photo) this.Photo = photo;
}
}
class ModelePanier {
Contenu: Achat[];
Quantite: number = this.Contenu.length;
PrixTotal(): number {
let prix: number = 0;
for (let achat of this.Contenu) {
prix += achat.Prix;
}
return prix;
}
}
I tried to use the import with and without default, but both don't work.
The following will definitely work:
export default class Inventaire {
And assuming the path ./Modele/Modele is correct the following will definitely work:
import Inventaire from "./Modele/Modele";
I'm pretty new with these languages and I'm sure it's not so complicated but could not find how to walk through class instance variables dynamically.
I'm trying to display instance's variables in a table using Angular. I'd like to write a single function that will work with any class.
Let's say I have a workbook class:
export class workbookEntity {
public name: string;
public creationDate: string;
constructor() {
this.name = 'my work book name';
this.creationDate = '2018/01/26';
}
}
And let's say I want to get the names and values of the variables of an instance of this class in another class' function:
export class showClassComponent {
// some code here
whenSubmitedInHtmlForm(className: string): void {
// do something to walk through instance
// with className parameter = 'workbookEntity'
}
// more code there
}
How would you walk through the instance to get each variable's name and value to get something like this?
[
{
name: 'name',
value: 'my work book name'
},
{
name: 'creationDate',
value: '2018/01/26'
}
]
There's no concept of reflection, so much in Typescript, so you can't neccessarily do a type lookup. But you might be able to do something along the lines of...
export class showClassComponent {
var classes = [ { name: 'workBookEntity', value: new WorkBookEntity() }]
whenSubmitedInHtmlForm(className: string): void {
let c = classes.filter(class => class.name == className)[0];
if(c) {
let ret = [];
for (var key in c) {
if (c.hasOwnProperty(key)) {
ret.push({ name: key, value: c[key] });
}
}
return ret;
}
}
// more code there
}
If you want to be able to retain accurate type information, you can create a pick utility function and then just import it into your class module wherever you need to use it.
let workBook = new WorkbookEntity();
export function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
let newObj = {} as Pick<T, K>;
for (const key of keys) {
newObj[key] = obj[key];
}
return newObj;
}
console.log(pick(workBook, ['name', 'creationDate']));
// log: { name: 'my work book name', creationDate: '2018/01/26' }
Then you can import the first function into another utility function if you to be able to handle multiple objects.
export function pickObjects<T extends object, K extends keyof T>
(objects: T[], keys: K[]): Pick<T, K>[] {
return objects.reduce((result, obj) => [...result, pick(obj, keys)], []);
}
let workbooks = pickObjects([workBook, workBook2], ['name']);
workbooks[0].name // this exists
workbooks[0].age // Typescript error.
The editor will show that age doesn't exist since we didn't pick it
Using Angular & TypeScript, we can use generics and all the Compile-goodness to assure some sort of type-safety. But if we are using for example the HTTP-Service, we don't get a specific objec but just parsed JSON. For example, we have some generic methods doing that:
public get<T>(relativeUrl: string): Promise<T> {
const completeUrlPromise = this.createCompleteUrl(relativeUrl);
const requestOptions = this.createRequestOptions(ContentType.ApplicationJson, true);
return completeUrlPromise.then(completeUrl => {
return this.processResponse<T>(this.http.get(completeUrl, requestOptions));
});
}
private processResponse<T>(response: Observable<Response>): Promise<T> {
const mappedResult = response.map(this.extractData);
const result = mappedResult.toPromise();
return result;
}
private extractData(res: Response): any {
let body;
if (!Array.isArray(res)) {
if (res.text()) {
body = res.json();
}
} else {
body = res;
}
if (!JsObjUtilities.isNullOrUndefined(body)) {
return body;
}
return {};
}
Ultimately, the generic type is useless this way, since we just get the JSON. If the generic object has methods or properties not in the JSON, they are lost.
To avoid this, we added the possibility to pass a constructor-function to truly create the object:
private processResponse<T>(response: Observable<Response>, ctor: IParameterlessConstructor<T> | null = null): Promise<T> {
let mappedResult = response.map(this.extractData);
if (ctor) {
mappedResult = mappedResult.map(f => {
const newObj = JsObjFactory.create(f, ctor);
return newObj;
});
}
const result = mappedResult.toPromise();
return result;
}
And the JsObjFactory looking like this:
export class JsObjFactory {
public static create<T>(source: any, ctorFn: IParameterlessConstructor<T>): T {
const result = new ctorFn();
this.mapDefinedProperties(source, result);
return result;
}
private static mapDefinedProperties<T>(source: Object, target: T): void {
const properties = Object.getOwnPropertyNames(target);
properties.forEach(propKey => {
if (source.hasOwnProperty(propKey)) {
target[propKey] = source[propKey];
}
});
}
}
This works well for shallow objects, but doesn't work, if a property is also a complex type with a constructor. As there are no types at runtime, the best bet I have currently is to kindahow parse the properties, check if classes exist and then create them. But this seems to be very error-prone and cumbersome.
Since I'm always certain, I'm not the only person with this issues, are there solutions, or TypeScript/JavaScript features I'm not aware off, which would help here?
I don't personally do it like this, but it may be what you're looking for.
Example:
Customer.ts
export interface ICustomer {
Id: number;
Name: string;
Orders: IOrder[];
...
}
export class Customer implements ICustomer {
public Id: number;
public Name: string;
public Orders: IOrder[];
constructor(customer: Partial<ICustomer>) {
this.Id = customer.Id || 0;
this.Name = customer.Name || '';
this.Orders = [];
customer.Orders.forEach((order: IOrder) => this.Orders.push(new Order(order)));
}
//some functions
}
Order.ts
export interface IOrder {
Id: number;
Weight: number;
Shipmentdate: string;
}
export class Order implements IOrder {
public Id: number;
public Weight: number;
public Shipmentdate: string;
constructor(order: Partial<IOrder>) {
this.Id = order.Id || 0;
this.Weight = order.Weight || 0;
this.Shipmentdate = order.Shipmentdate || '';
}
//some functions
}
This would make the Object (in this case Customer) responsible for instantiating it's known complex types that you pass in. And Order in turn could have its complex types that it instantiates.
How to include system.js to fix the error below? Or is there any other solution?
I downloaded the relay-starter-kit (https://github.com/relayjs/relay-starter-kit), changed the database.js to database.ts with the below content (Snippet 1).
I ran "npm run update-schema" and got the error
System.register([], function (exports_1) {
^
ReferenceError: System is not defined
at Object.<anonymous> (database.js:9:1)
at Module._compile (module.js:410:26)
..
I know it occurs because update-schema uses scripts/updateSchema.js -> data/schema.js -> which imports objects from data/database.js (compiled version of database.ts) has -
System.register([], function(exports_1) {
Snippet 1:
/// <reference path="./interface.d.ts" />
export class User implements IUser{
constructor (public id: String, public name: String){
this.id = id;
this.name = name;
}
}
// Model types
class UserModel extends User implements IUserModel {
constructor(public id: String, public name: String){
super (id,name);
}
getUser ():IUser{
return this;
}
setUser (_User:IUser) : void {
this.id = _User.id;
this.name = _User.name;
}
getUserbyId (_id:String):IUser{
if (_id === this.id){
return this;
} else {
return null;
}
}
}
export class Widget implements IWidget{
constructor (public id: String, public name: String){
this.id = id;
this.name = name;
}
}
// Model types
class WidgetModel extends Widget implements IWidgetModel {
constructor(public id: String, public name: String){
super (id,name);
}
getWidget ():IWidget{
return this;
}
setWidget (_Widget:IWidget) : void {
this.id = _Widget.id;
this.name = _Widget.name;
}
getWidgetbyId (_id:String):IWidget{
if (_id === this.id){
return this;
} else {
return null;
}
}
}
// Mock data
var viewer:IUserModel = new UserModel('1','Anonymous');
var widgets:IWidget[] = ['What\'s-it', 'Who\'s-it', 'How\'s-it'].map((name:String, i:any) => {
let widget:IWidgetModel = new WidgetModel(name,`${i}`);
return widget;
});
export function getUser (_id:String):IUser {
return viewer.getUserbyId(_id);
}
export function getViewer ():IUser {
return viewer.getUser();
}
export function getWidget (_id:String):any {
widgets.forEach(
w => {
if (w.id === _id)
return w;
else
return null;
}
);
}
export function getWidgets (): IWidget[]{
return widgets;
}
tsconfig.json had
"module": "system",
Changed it to
"module": "umd",
and it worked.