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;
}
Related
I'm creating mini orm in my pet-project. Trying to do models usable in IDE and compiler, but stuck with typing problems.
I have base model class:
import { db } from '../db/db';
export interface Criteria<T> {
field: Extract<keyof T, string>;
value: T[Extract<keyof T, string>];
comparison?: string;
}
export class BaseModel {
static _pk = 'id';
static _table: string = null;
constructor(params: object) {
for (const key in params) {
if (Object.prototype.hasOwnProperty.call(params, key)) {
const fn = `${key}Process`;
if (typeof this[fn] === 'function') {
this[fn](params[key]);
} else {
this[key] = params[key];
}
}
}
}
create<T extends typeof BaseModel>(this: T): Promise<InstanceType<T>> {
const params = this._getParams(); //TS2339: Property '_getParams' does not exist on type 'T'.
const thisTyped = this.constructor as T;
return db
.insertFromModel(thisTyped._table, params)
.then((id) => thisTyped.findOne([{ field: thisTyped._pk, value: id }])); //TS2322: Type 'string' is not assignable to type 'Extract<keyof InstanceType<T>, string>'.
}
static findOne<T extends typeof BaseModel>(this: T, criteria: Criteria<InstanceType<T>>[]): Promise<InstanceType<T>> {
return db.selectFromModel(this._table, criteria, true).then((row: object) => {
return new this(row) as InstanceType<T>;
});
}
_getParams() {
const params = {};
for (const key of Object.keys(this)) {
if (['id', '_table', '_pk'].includes(key)) {
continue;
} else {
params[key] = this[key];
}
}
return params;
}
}
And child class:
import { BaseModel } from './BaseModel';
export class User extends BaseModel {
static _table = 'users';
static _pk = 'id';
id: number;
email: string;
password: string;
// eslint-disable-next-line #typescript-eslint/no-useless-constructor
constructor(props: { id?: number; email: string; password: string }) {
super(props);
}
}
static method from base class works good, but after adding create method I'm getting error while compile... Errors described in comments in code.
One more error could be achieved with code:
new User({ email: 'test#test.com', password: '123123' }).create().then((user) => {//TS2684: The 'this' context of type 'User' is not assignable to method's 'this' of type 'typeof BaseModel'. Type 'User' is missing the following properties from type 'typeof BaseModel': prototype, _pk, _table, findOne, and 2 more
console.log(user);
});
Don't understand what I'm doing wrong, I do all in analogy with static method.
Also full code for experiments available here - https://github.com/kricha/ts_q
thanks.
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...
Coders! I began to research JavaScript Proxy objects and faced some strange behaviour of one.
I'm trying to push a number to proxied array, but, unexpectedly for me, get method invoked, if it exists! However, if I delete get method in arrayHandler, set is invoked as expected, and number is pushed. Why is it possible?
class SomeClass {
public readonly name: String;
public originalData: Array<number> = [];
private arrayHandler = {
set(
target: Array<number>,
prop: number,
val: any,
reciever: any
): boolean {
console.log("PROXY HANDLER SET");
target[prop] = val;
return true;
},
get(target: Array<number>, prop: number): void {
console.log("PROXY HANDLER GET");
},
};
public proxyToArray: any = new Proxy(this.originalData, this.arrayHandler);
constructor(name: String) {
this.name = name;
}
}
let item = new SomeClass("item-1");
item.proxyToArray.push(1);
You need to return the target in the getter:
class SomeClass {
public readonly name: String;
public originalData: Array<number> = [];
private arrayHandler = {
set(
target: Array<number>,
prop: number,
val: any,
reciever: any
): boolean {
console.log("PROXY HANDLER SET");
target[prop] = val;
return true;
},
get(target: Array<number>, prop: number): any {
console.log("PROXY HANDLER GET");
return target[prop];
},
};
public proxyToArray: any = new Proxy(this.originalData, this.arrayHandler);
constructor(name: String) {
this.name = name;
}
}
let item = new SomeClass("item-1");
item.proxyToArray.push(1);
I'm trying to implement binary search tree in Type Script using generic types. However I have an issue with adding child in node, because there is an error when parent.leftChild(newNode) - "Cannot invoke an expression whose type is lacks a call signature. Type Node has no compatible call signatures."
export { };
class Node <T> {
private _key: number;
private _data: T;
private _leftChild: Node <T>;
private _rightChild: Node <T>;
constructor(key: number, data: T) {
this._key = key;
this._data = data;
}
get key(): number {
return this._key;
}
get data(): T {
return this._data;
}
get leftChild(): Node <T> {
return this._leftChild;
}
get rightChild(): Node <T> {
return this._rightChild;
}
set leftChild(child: Node <T>) {
this._leftChild = child;
}
set rightChild(child: Node <T>) {
this._rightChild = child;
}
}
class BinaryTree <H> {
private _root: Node<H>;
public addNode(key: number, data: H): void {
const newNode: Node <H> = new Node <H> (key, data);
if (this._root == null) {
this._root = newNode;
} else {
let focusNode: Node <H> = this._root;
let parent: Node <H>;
while (true) {
parent = focusNode;
if (key < focusNode.key) {
focusNode = focusNode.leftChild;
if (focusNode == null) {
parent.leftChild(newNode);
return;
}
}
}
}
}
}
I'm trying to construct the n-level treeview with rxjs. I achieved the same with plain javascript. Find the js code here
var x = [{"id":1,"name":"Admin","parentDepartmentId":null},{"id":2,"name":"Development","parentDepartmentId":1},{"id":3,"name":"Research and Development","parentDepartmentId":1},{"id":4,"name":"FE","parentDepartmentId":2},{"id":5,"name":"BE","parentDepartmentId":2},{"id":6,"name":"Testing","parentDepartmentId":1},{"id":7,"name":"Unit Test","parentDepartmentId":6},{"id":8,"name":"Integration Test","parentDepartmentId":6},{"id":9,"name":"HR","parentDepartmentId":null}];
function getRecords(parents) {
parents.forEach((parent) => {
var result = getChildData(parent);
parent.items = result;
if (result.length > 0) {
getRecords(result);
}
})
return parents;
}
function getChildData(parent) {
return x.filter(record => record.parentDepartmentId == parent.id)
}
function getParents(x){
return x.filter(record => record.parentDepartmentId == null);
}
var parents = getParents(x)
var result = getRecords(parents);
console.log(result);
Can someone show some lights on this.
Here is what I could generate from an https://jvilk.com/MakeTypes/. You may want to clean it up a bit though, like making Data and Data1 use a same class making isNode optional. Converter can come in handy when you have to do a lot of this.
Also you may want to make these as class instead of interface if what you want is class.
export interface Weather {
id: number;
name: string;
parentDepartmentId?: null;
items?: (ItemsEntity | null)[] | null;
}
export interface ItemsEntity {
id: number;
name: string;
parentDepartmentId: number;
items?: (ItemsEntity1 | null)[] | null;
}
export interface ItemsEntity1 {
id: number;
name: string;
parentDepartmentId: number;
items?: (null)[] | null;
}