Parent static method to return subclass object - javascript

class Vehicle {
constructor (name, type) {
this.name = name;
this.type = type;
console.log(this.constructor.name);
}
getName () {
return this.name;
}
getType () {
return this.type;
}
static create(name, type) {
return new Vehicle(name, type);
}
}
class Car extends Vehicle {
constructor (name) {
super(name, 'car');
}
getName () {
return 'It is a car: ' + super.getName();
}
}
let car = Car.create('Tesla', 'car');
console.log(car.getName()); // It is a car: Tesla
console.log(car.getType()); // car
The above code use ES6 class keyword to define a Vehicle class and a subclass Car from it. How to return Car instance from Vehicle static method.

Try:
let car = new Car('Tesla')

You can pass the ClassName you want to use within your static function create and create an instance from it.
static create(name, type, objClass) {
return new Function(`return new ${objClass ? objClass : 'Vehicle'}('${name}', '${type}');`)();
}
The Function class receives a String with the expression to evaluate, in your case:
new Function(`return new ${objClass}('${name}', '${type}');`)()
Look at this code
class Vehicle {
constructor(name, type) {
this.name = name;
this.type = type;
}
getName() {
return this.name;
}
getType() {
return this.type;
}
static create(name, type, objClass) {
return new Function(`return new ${objClass ? objClass : 'Vehicle'}('${name}', '${type}');`)();
}
}
class Car extends Vehicle {
constructor(name) {
super(name, 'car');
}
getName() {
return 'It is a car: ' + super.getName();
}
}
let car = Car.create('Tesla', 'car', 'Car');
console.log(car.getName()); // It is a car: Tesla
console.log(car.getType()); // car
let superCar = Vehicle.create('Tesla', 'car');
console.log(superCar.getName()); // Tesla
console.log(superCar.getType()); // car
.as-console-wrapper {
max-height: 100% !important
}
See? now is printing the right output.
Resources
Class Function

Related

Javascript arrow func in subclass cannot be found by super class

What I would like to achieve:
Essentially, I would like my subclass to have a lexically-bound this function. However, I would like the super class to check that the subclass has an instantiation of this lexically-bound function.
This is how I would prefer to write the code, but it doesn't work:
class Animal {
constructor(type) {
this.animalType = type;
if (!(this.bark instanceof Function)) {
throw new Error('Found no bark');
}
}
}
class Dog extends Animal {
bark = () => {
console.log('woof');
}
}
let max = new Dog('dog')
max.bark();
Yet this works:
class Animal {
constructor(type) {
this.animalType = type;
if (!(this.bark instanceof Function)) {
throw new Error('Found no bark');
}
}
}
class Dog extends Animal {}
Dog.prototype.bark = () => {
console.log('woof');
}
let max = new Dog('dog')
max.bark();
and this works:
class Animal {
constructor(type) {
this.animalType = type;
if (!(this.bark instanceof Function)) {
throw new Error('Found no bark');
}
}
bark = () => {
console.log('woof');
}
}
class Dog extends Animal {}
let max = new Dog('dog')
max.bark();
Could someone please explain why my first example is failing. It seems to me that bark() isn't in the prototype chain somehow but I'm not sure why.

Javascript Cannot read property 'x' of undefined

I don't understand the error in the code below. I tried calling another class's function from another class. But I gives the error error: Uncaught TypeError: Cannot read property '_name' of undefined
class Person {
constructor() {
this._name = "Name-Person";
}
getName() {
return this._name;
}
}
class Test1 {
constructor() {
let p = new Person();
new Test2(p.getName);
}
}
class Test2 {
constructor(getName) {
console.log(getName());
}
}
new Test1()
How can I fix the error?
When passing the function to Test2 you need to bind p to the function
new Test2(p.getName.bind(p));
class Person {
constructor() {
this._name = "Name-Person";
}
getName() {
return this._name;
}
}
class Test1 {
constructor() {
let p = new Person();
new Test2(p.getName.bind(p));
}
}
class Test2 {
constructor(getName) {
console.log(getName());
}
}
new Test1()
you can use public class field:
class Person {
constructor() {
this._name = "Name-Person";
}
getName = () => {
return this._name;
}
}
class Test1 {
constructor() {
let p = new Person();
new Test2(p.getName);
}
}
class Test2 {
constructor(getName) {
console.log(getName());
}
}
new Test1()
Because you are passing the function and not the entire class or its value, _name does not exist in the context of the Test2 constructor.
A couple of simple solutions is to either pass in the result of getName() to the constructor, or the entire class of Person.
new Test2(p); // And in Test2 use p.getName()
// or
new Test2(p.getName()); // And in Test2 use the result

how to call attributes of more than one class in js

I am studying oojs and I have here the summary of the classes, to demonstrate the problem. Because the classes are large with many attributes and validations. I need the book class to receive the attributes of the Author class (name) and the Category class (category), but in my test I get undefined for my imports in the Book class. What could I have done wrong? I appreciate the help!
class author
export default class Author {
constructor(name){
this.name = name;
}
set name (name){
if(name === '')
throw new Error (`this field cannot be empty`)
this._name = name;
}
get name (){
return this._name;
}
}
class category
export default class Category {
constructor(category){
this.category = category;
}
set category (category){
if(category === '')
throw new Error (`this field cannot be empty`)
this._category = category;
}
get category (){
return this._category;
}
}
class book
import Author from './Author.js'
import Category from './Category.js'
export default class Book{
constructor(name, title, category){
this.name = name;
this.title = title;
this.category = category;
}
set name(name){
if(name instanceof Author)
this._name = name;
}
set category(category){
if(category instanceof Category)
this._category = category;
}
set title (title){
if(title === ' ')
throw new Error (`this field cannot be empty`)
this._title = title;
}
get name(){
return this._name;
}
get category(){
return this._category;
}
get title(){
return this._title;
}
}
test
import Book from './Book.js';
try{
const newBook = new Book(' Anne', 'Design UX/UI', 'Design');
console.log(`saved ${newBook.name} ${newBook.title} ${newBook.category}`)
}catch(err){
console.log(`err ${err}`)
}
//saved undefined Design UX/UI undefined
The arguments to new Book() need to be instances of the Author and Category classes, not strings. So you have to do:
const author = new Author('Anne');
const cat = new Category('Design');
const newBook = new Book(author, 'Design UX/UI', cat);
And when you're printing the properties, you need to access their name properties as well.
console.log(`saved ${newBook.name.name} ${newBook.title} ${newBook.category.category}`)
Alternatively, you could define toString() methods in the other classes:
export default class Author {
constructor(name){
this.name = name;
}
set name (name){
if(name === '')
throw new Error (`this field cannot be empty`)
this._name = name;
}
get name (){
return this._name;
}
toString() {
return this.name;
}
}
export default class Category {
constructor(category){
this.category = category;
}
set category (category){
if(category === '')
throw new Error (`this field cannot be empty`)
this._category = category;
}
get category (){
return this._category;
}
toString() {
return this.category;
}
}

How to stringify an object of the type Map

class test{
name : string
children : Map<string,string> =new Map()
constructor(){
this.name='KIANA'
this.children.set('name','OTTO')
}
}
var t = new test()
console.log(t)
console.log(JSON.stringify(t))
The result is:
test { children: Map { 'name' => 'OTTO' }, name: 'KIANA' }
{"children":{},"name":"KIANA"}
How can I get the whole data like:
{"children":{'name':'OTTO'},"name":"KIANA"}
or
{"children":['name':'OTTO'],"name":"KIANA"}
Or, does it has a simpler way to describe the relationship of 'key value' in JSON and TypeScript
Preface: Class names should start with an uppercase character, so I've changed test to Test in the below.
Since Map isn't stringify-able by default, you have at least three choices:
Implement toJSON on your Test class and return an object with a replacement for children (probably an array of arrays), or
Implement a subclass of Map that has toJSON and use that in Test
Implement a replacer that you use with JSON.stringify that handles Map instances.
While #1 works, it means you have to edit your toJSON method every time you add or remove properties from Test, which seems like a maintenance issue:
class Test {
name: string
children: Map<string, string> = new Map()
constructor() {
this.name = 'KIANA'
this.children.set('name', 'OTTO')
}
toJSON() {
return {
name: this.name,
children: [...this.children.entries()]
}
}
}
var t = new Test()
console.log(JSON.stringify(t))
Live Example:
class Test {
name/*: string*/
children/*: Map<string, string>*/ = new Map()
constructor() {
this.name = 'KIANA'
this.children.set('name', 'OTTO')
}
toJSON() {
return {
name: this.name,
children: [...this.children.entries()]
}
}
}
var t = new Test()
console.log(JSON.stringify(t))
[...this.children.entries()] creates an array of [name, value] arrays for the map.
But I prefer #2, a JSON-compatible Map:
class JSONAbleMap extends Map {
toJSON() {
return [...this.entries()]
}
}
...which you then use in Test:
class Test {
name: string
children: Map<string, string> = new JSONAbleMap()
constructor() {
this.name = 'KIANA'
this.children.set('name', 'OTTO')
}
}
var t = new Test()
console.log(JSON.stringify(t))
Live Example:
class JSONAbleMap extends Map {
toJSON() {
return [...this.entries()]
}
}
class Test {
name/*: string*/
children/*: Map<string, string>*/ = new JSONAbleMap()
constructor() {
this.name = 'KIANA'
this.children.set('name', 'OTTO')
}
}
var t = new Test()
console.log(JSON.stringify(t))
Or #3, a replacer function you use with JSON.stringify:
function mapAwareReplacer(key: string|Symbol, value: any): any {
if (value instanceof Map && typeof value.toJSON !== "function") {
return [...value.entries()]
}
return value
}
...which you use when calling JSON.stringify:
console.log(JSON.stringify(t, mapAwareReplacer))
Live Example:
function mapAwareReplacer(key, value) {
if (value instanceof Map && typeof value.toJSON !== "function") {
return [...value.entries()]
}
return value
}
class Test {
name/*: string*/
children/*: Map<string, string>*/ = new Map()
constructor() {
this.name = 'KIANA'
this.children.set('name', 'OTTO')
}
}
var t = new Test()
console.log(JSON.stringify(t, mapAwareReplacer))

Prototypes in Javascript to Typescript Syntax

Does somebody know how do I write this Javascript code into Typescript? Especially the prototype inside of the class causes me problems...
var Module = (function () {
function Module(name) {
this.name = name;
}
Module.prototype.toString = function () {
return this.name;
};
return Module;
})();
var Student = (function () {
function Student(name, studentNumber) {
this.bookedModules = [];
this.name = name;
this.studentNumber = studentNumber;
}
Student.prototype.bookModule = function (bookedModule) {
this.bookedModules.push(bookedModule);
};
Student.prototype.bookedModuleNames = function () {
return this.bookedModules.map(function (module) {
return module.toString();
});
};
return Student;
})();
In typescript you use classes, the compiler will do the prototype work for you.
You code is equivalent to:
class Module {
public name: string;
constructor(name: string) {
this.name = name;
}
toString(): string {
return this.name;
}
}
class Student {
public name: string;
public studentNumber: number;
public bookedModules: Module[];
constructor(name: string, studentNumber: number) {
this.name = name;
this.bookedModules = [];
this.studentNumber = studentNumber;
}
bookModule(book: Module): void {
this.bookedModules.push(book);
}
bookedModuleNames(): string[] {
return this.bookedModules.map(book => book.name);
}
}
(code in playground)
Which compiles into:
var Module = (function () {
function Module(name) {
this.name = name;
}
Module.prototype.toString = function () {
return this.name;
};
return Module;
}());
var Student = (function () {
function Student(name, studentNumber) {
this.name = name;
this.bookedModules = [];
this.studentNumber = studentNumber;
}
Student.prototype.bookModule = function (book) {
this.bookedModules.push(book);
};
Student.prototype.bookedModuleNames = function () {
return this.bookedModules.map(function (book) { return book.name; });
};
return Student;
}());
Use classes - typescript will generate this code for you:
class Module {
constructor(public name) {
}
toString() {
return this.name;
}
}
class Student {
bookedModules: Module[];
constructor(public name, public studentNumber) {
this.bookedModules = [];
}
bookModule(bookedModule: Module) {
this.bookedModules.push(bookedModule);
}
//...
}

Categories

Resources