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
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.
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
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;
}
}
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))
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);
}
//...
}