#javascript #typescript #generics #subclass
#javascript #typescript #общие #подкласс
Вопрос:
В моем приложении я разрабатываю Entity-Component-System, и мне нужен способ получения объектов по их типу с карты. В настоящее время я делаю это:
class Entity {
components: Map<CT, Component>;
constructor() {
this.components = new Map();
}
get(componentType: CT): any {
return this.components.get(componentType);
}
}
const enum CT {
Null,
Health,
Position,
// etc..
}
class Component {
type: CT;
constructor(type: CT) {
this.type = type;
}
}
class HealthComponent extends Component {
amount: number;
constructor() {
super(CT.Health);
this.amount = 0;
}
}
И затем вот несколько примеров клиентского кода:
let healthComponent = new HealthComponent();
healthComponent.amount = 100;
let entity = new Entity();
entity.components.set(healthComponent.type, healthComponent);
// Later, elsewhere..
let health = entity.get(CT.Health);
console.log(health.amount);
Все это работает, но немного громоздко. Каждый новый компонент, который я создаю, требует, чтобы я вносил новую запись в CT
перечисление, и чтобы я убедился, что она соответствует в конструкторе нового типа. В идеале я хотел бы иметь возможность просто делать что-то вроде:
let healthComponent = new HealthComponent();
healthComponent.amount = 100;
let entity = new Entity();
entity.components.set(HealthComponent, healthComponent);
// Later, elsewhere..
let health = entity.get(HealthComponent);
console.log(health.amount);
Но я не уверен, возможно ли это. Можно ли это сделать?
Ответ №1:
Я бы просто использовал string в качестве ключа и сделал что-то вроде:
class Entity {
components: Map<string, Component>;
constructor() {
this.components = new Map();
}
get(componentType: string): any {
return this.components.get(componentType);
}
}
А затем используйте name
свойство класса следующим образом:
let health = entity.get(Health.name);
console.log(health.amount);
Однако вам нужно будет указать target
в своем tconfig
файле to es6
или esnext
file, чтобы он заработал.
Это не будет работать для интерфейсов, поэтому, если вы планируете их использовать, это также не очень хороший подход.
Комментарии:
1. Хм, да, я слышал, что у этого подхода могут быть проблемы с совместимостью, а также кажется, что производительность при этом может быть хуже, поскольку ключи представляют собой несколько длинные строки вместо простых целых чисел. Тем не менее, я мог бы это сделать … не уверен..
2. Подождите, какой длины эти строки, что это влияет на производительность? Мне это кажется очень маловероятным.
3. @RyanPeschel Строки содержат именно ваше имя класса. Разница с int не так уж велика. С другой стороны, если у вас есть два класса с одинаковыми именами, этот подход также не будет работать. С третьей (?) стороны, Что вы говорите о совместимости — где вы собираетесь запускать этот код? Если на сервере, просто упакуйте его в docker, и у вас не возникнет проблем. Если в браузере, то это не очень хорошая идея.