1

I have a typescript class inheriting another one. I would like to create a factory class that creates an object of one or the other using basic logic, but it is not working.

This is a basic class for a Customer:

class Customer {
  static member = true;
  id:string;
  static c_type = "Basic Customer";

  makeTransaction():string {
    var transaction_id = Math.random().toString(36).substr(2, 9);
    console.log(this.constructor.toString().split ('(' || /s+/)[0].split (' ' || /s+/)[1]);
    return transaction_id;
  }

  constructor(public name:string, public dob:string) {
    this.id = Math.random().toString(36).substr(2, 9);
  }

}

This class extends customers to create a VIP customer:

class VIPCustomer extends Customer{
  vip_num:string;
  vip_discount:number;
  static c_type = "VIP Customer";
  constructor(public name:string, public dob:string) {
    super(name, dob);
    this.vip_num = Math.random().toString(36).substr(2, 9);
  }
}

The customer creator is intended to create either a VIP customer or regular customer based on a string comparison, but it is not working.

class CustomerCreator {
  static create(event: {name:string; dob: string}, type:string) {
    console.log('Log type' + typeof type);
    if (type === 'Basic') {
      console.log('basic customer created');
      return new Customer(event.name, event.dob);
    }
    if (type === 'VIP') {
      console.log('basic customer created');
      return new VIPCustomer(event.name, event.dob);
    }
  }
}
console.log(Customer.c_type);
console.log(VIPCustomer.c_type);
const customer_1 = CustomerCreator.create({name:'Pii', dob:'03/19'}, 'VIP');
var customer_2 = CustomerCreator.create({name:'Matthew', dob:'12/70'}, 'Basic');

//accessing an attribute
console.log(customer_1.name);
console.log(customer_1.id);
//console.log(customer_1.vip_num)

If you uncomment the last print statement, the code does not compile. The print statements also indicate that a basic customer is being created for both the customers 1 and 2, despite the string comparison. Where am I going wrong?

2 Answers 2

2

Typescript only has type info of compiling time, but not type info only known in run time.

The return type of CustomerCreator.create is Customer|VIPCustomer which is narrowed down to Customer so everything return from that function is recognized to ts compiler as Customer . That's the whole point of Factory pattern, that your code rely on interface but not class

If you really want to let compiler know what exact type of what CustomerCreator.create returns, you could try following code

type CreatorResult = {
    Basic: Customer,
    VIP: VIPCustomer
}

class CustomerCreator {
  static create<T extends 'Basic'| 'VIP'>(event: {name:string; dob: string}, type:T): CreatorResult[T] {

although this is not recommended

Sign up to request clarification or add additional context in comments.

2 Comments

why isn't this recommended?
it's misuse of Typescript's mapped types, and expose too much details to caller
1

Your solution is not working because the create factory method always returns the type Customer as VIPCustomer is also derived from Customer. Also, your create function not only returns just Customer but Customer | undefined because you do not have a default case (when type is neither Basic or VIP). I would just create multiple factory methods for each type of customer. In this case of my example there is almost no shared piece of code or extra processing, the factory pattern is rendered useless.

class CustomerCreator {
    static create(event: { name: string; dob: string }) {
        return new Customer(event.name, event.dob);
    }

    static createVip(event: { name: string; dob: string }) {
        return new VIPCustomer(event.name, event.dob);
    }
}
console.log(Customer.c_type);
console.log(VIPCustomer.c_type);
const customer_1 = CustomerCreator.createVip({ name: 'Pii', dob: '03/19' });
var customer_2 = CustomerCreator.create({ name: 'Matthew', dob: '12/70' });

console.log(customer_1.name);
console.log(customer_1.id);
console.log(customer_1.vip_num)

1 Comment

Updated my answer to include explanation

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.