Tengo una lista de Componentes en una Entidad de clase. Estos componentes amplían la interfaz Component .
class Entity { ... const components: Component[] = []; ... }Donde componentes específicos implementan la interfaz Componente
class SpecificComponent0 implements Component { ... } Ahora quiero consultar la instancia de entidad e y obtener un componente si coincide con el tipo introducido en la consulta, algo como esto:
const specificComponent0 = e.getSpecificComponent<SpecificComponentClass0>();O tal vez así
const specificComponent0 = e.getSpecificComponent(instanceof SpecificComponentClass0) Pero parece que no puedo encontrar una manera de hacerlo en la función de get de la entidad.
Esta es complicada ya que está mezclando preocupaciones de tiempo de ejecución y tiempo de compilación. Refiriéndose a los ejemplos que sugirió:
const specificComponent0 = e.getSpecificComponent<SpecificComponentClass0>();Esto definitivamente no va a funcionar, porque los paréntesis angulares especifican un "Parámetro de tipo", que solo existe en el momento de la compilación . Dado que lo que intenta hacer implica lógica , debe pasar algo a la función en tiempo de ejecución para ayudarla a elegir el elemento correcto.
const specificComponent0 = e.getSpecificComponent(instanceof SpecificComponentClass0) El valor de retorno del operador instanceof es un valor booleano. Está pasando true o false a esta función, lo cual no es muy útil.
Tienes dos problemas aquí.
Component El problema 1 se puede resolver pasando la función constructora de tipo y luego combinándola con la propiedad constructora del Component instanciado.
class Entity { constructor(private components: Component[]) {} getSpecificComponent(thing: new () => Component): Component | undefined { return this.components.find(component => component.constructor === thing) } } Esto funciona perfectamente bien, pero su función getSpecificComponent devolverá un valor escrito como Component | undefined , que no es muy útil si desea utilizar propiedades que solo existen en uno de los tipos específicos.
Para resolver el Problema 2 (sin emitir el valor de retorno, lo que realmente no deberías hacer), necesitamos
true , puede reducir el tipo de forma segura al tipo genérico class Component {} class OtherThing1 extends Component { name = 'thing1' } class OtherThing2 extends Component { name = 'thing2' } class OtherThing3 extends Component { name = 'thing3' } const getNarrower = <T extends Component>(thingConstructor: new () => T) => (thing: Component): thing is T => thing.constructor === thingConstructor class Entity { constructor(private components: Component[]) {} getSpecificComponent<T extends Component>(thing: new () => T): T | undefined { return this.components.find(getNarrower(thing)) } } const e = new Entity([new OtherThing1(), new OtherThing2()]) const thing = e.getSpecificComponent(OtherThing1) console.log(thing) // [LOG]: OtherThing1: { "name": "thing1" } const thingNotHere = e.getSpecificComponent(OtherThing3) console.log(thingNotHere) // [LOG]: undefined