У меня есть класс typescript, наследующий другой. Я хотел бы создать фабричный класс, который создает объект одного или другого, используя базовую логику, но он не работает.
Это базовый класс для клиента:
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.info(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);
}
}
Этот класс расширяет клиентов для создания VIP-клиента:
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);
}
}
Создатель клиента предназначен для создания либо VIP-клиента, либо обычного клиента на основе сравнения строк, но он не работает.
class CustomerCreator {
static create(event: {name:string; dob: string}, type:string) {
console.info('Log type' + typeof type);
if (type === 'Basic') {
console.info('basic customer created');
return new Customer(event.name, event.dob);
}
if (type === 'VIP') {
console.info('basic customer created');
return new VIPCustomer(event.name, event.dob);
}
}
}
console.info(Customer.c_type);
console.info(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.info(customer_1.name);
console.info(customer_1.id);
//console.info(customer_1.vip_num)
Если вы раскомментируете последний оператор печати, код не скомпилируется. Операторы печати также указывают, что базовый клиент создается как для клиентов 1, так и для клиентов 2, несмотря на сравнение строк. Где я ошибаюсь?
Typescript имеет только информацию о типе времени компиляции, но не информацию о типе, известную только во время выполнения.
Тип возвращаемого значения CustomerCreator.create
— Customer|VIPCustomer
, который сужен до Customer
, поэтому все, что возвращается из этой функции, распознается компилятором ts как Customer
. В этом весь смысл шаблона Factory, что ваш код опирается на интерфейс, а не на класс.
Если вы действительно хотите, чтобы компилятор знал, какой именно тип того, что возвращает CustomerCreator.create
, вы можете попробовать следующий код
type CreatorResult = {
Basic: Customer,
VIP: VIPCustomer
}
class CustomerCreator {
static create<T extends 'Basic'| 'VIP'>(event: {name:string; dob: string}, type:T): CreatorResult[T] {
хотя это не рекомендуется
это неправильное использование сопоставленных типов Typescript и раскрытие слишком большого количества деталей вызывающему абоненту
Ваше решение не работает, потому что фабричный метод create
всегда возвращает тип Customer
, поскольку VIPCustomer
также является производным от Customer
. Кроме того, ваша функция create
возвращает не только Customer
, но и Customer | undefined
, потому что у вас нет регистра по умолчанию (когда тип не является ни Basic
, ни VIP
). Я бы просто создал несколько фабричных методов для каждого типа клиентов. В этом случае моего примера почти нет общего фрагмента кода или дополнительной обработки, фабричный шаблон становится бесполезным.
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.info(Customer.c_type);
console.info(VIPCustomer.c_type);
const customer_1 = CustomerCreator.createVip({ name: 'Pii', dob: '03/19' });
var customer_2 = CustomerCreator.create({ name: 'Matthew', dob: '12/70' });
console.info(customer_1.name);
console.info(customer_1.id);
console.info(customer_1.vip_num)
Обновлен мой ответ, чтобы включить объяснение
почему это не рекомендуется?