#type EntityClass<T extends Entity> = typeof T;

1 messages · Page 1 of 1 (latest)

loud mossBOT
#
drwarpman#0

Preview:```ts
/* Entities */
class Entity {}
class Shape extends Entity {}
class Square extends Shape {}
class Circle extends Shape {}

type EntityClass = typeof Entity;

class EntityManager {
#entities: Map<EntityClass, Entity[]> = new Map();

clearEntities() {
this.#entities = new Map();
...```

fervent viper
#

Does anyone know if there is a way, so that methods like getEntities, would return the correct entity type based on provided entity subclass?

#

this is wrong.

#

I think I found a solution, but this still bugs me, is there no way around this?

class Entity {}
const foo = (bar: typeof Entity) => {};
foo(Date);

No error.

flat hollow
#

ts is structurally typed. if you make Date unassignable to Entity, then ts won't allow that assignment

flat hollow
fervent viper
flat hollow
#

that's just simply not how ts works

#

just add properties to Entity

#

then structural typing will say that Date isn't a valid Entity

#

ts isn't nominally typed

#

you could add a private key or a key with a symbol value to make it unique, though

fervent viper
#

so i have to add something that is special to entity only

flat hollow
#

like, literally any data

fervent viper
#

but what if someone makes another class, that will have the same data? 😄

#

or similar

flat hollow
#

i'm sure your real Entity, Shape, Square, and Circle, aren't empty

fervent viper
#
class Entity {
  static narrowType = "unique" as const;
}
const foo = (bar: typeof Entity & {narrowType: string}) => {};

foo(Entity);
foo(Date);

This seems to work, but its ugly? 😄
And how do I make it work with private if you cant access private props?

#

you could add a private key or a key with a symbol value to make it unique, though
Or mby im not getting what u mean here

flat hollow
#

you don't need the intersection

#

And how do I make it work with private if you cant access private props?
what do you mean by this? this isn't really an issue

#

typeof Entity is already a { new () => Entity; narrowType: "unique" } (where Entity is {})

#

the property doesn't have to be static

fervent viper
#

AHAAAAAAAAAAA

#

i think im getting u now!

#

im so silly

#

u are awesome

loud mossBOT
#
that_guy977#0

Preview:```ts
...
getEntities(): Generator<Entity, void, undefined>
getEntities<
T extends new (...args: never) => Entity

(
type?: T
): Generator<InstanceType<T>, void, undefined>
getEntities<
T extends new (...args: never) => Entity
(type?: T) {
if (!type) {
for (const entities of this.#entities.values()) {
yield
entities
}

  return
}

yield* this.#entities.get(type) || []

}

addEn
...```

flat hollow
#

requires overload because of the optional generic.

fervent viper
#

is there no way around it?

flat hollow
#

yeah, because you can specify the generic manually without passing the argument

#

then that becomes:

#

!:thats%

loud mossBOT
#
tjjfvi#0
`!tjjfvi:thats-no-generic-its-a-type-cast`:

When you use a type parameter in the function, it should generally be inferrable from the arguments.

For example, this is a misuse of generics:

function getMeA<T>(): T {
   /* magic */
}

because getMeA<string>() and getMeA<number>() compile to the same code at runtime, there's so way to implement this function safely (other than always throwing); this is just a type cast in disguise. Instead of using a generic here, you should return unknown, and cast at the call site if necessary, to be clear it's an unsafe operation:

-function getMeA<T>(): T {
+function getMeA(): unknown {
    /* magic */
 }

-getMeA<number>()
+getMeA() as number

One exception to this rule is if you're returning a possibly-empty container of T. For example, these are all perfectly safe, even though the generic can't be inferred from the parameters:

function emptyArray<T>(): Array<T> {
  return []
}

function useRef<T>(): { current?: T } {
  return {}
}
flat hollow
#

there's been quite a few recent threads about it

#

this for example

fervent viper
#

And in this piece of code, the as is necessary?

  addEntity(entity: Entity) {
    const entityClass = entity.constructor as EntityClass;

right?

#

there is no more "correct" way to type it

flat hollow
#

yeah, because constructor isn't typed.

#

although something to consider

#

what about doing getEntities(Shape)?

fervent viper
#

what about that?

flat hollow
#

what should it return?

#

or getEntities(Entity) for that matter

fervent viper
#

oh.. in my app both entity and shape are abstract 😄

#

but i get what u mean

flat hollow
fervent viper
#

it does, it doesnt allow me to pass abstract classes

flat hollow
#

right, i forgot to put abstract in there

#

but they still exist at runtime;

fervent viper
#

yea but no one cares about that xdd

flat hollow
#

should they not be passable to a getEntities to get all of the type?

#

so your Square is concrete, what if i make a class ColoredSquare extends Square? doing getEntities(Square) should still give the ColoredSquare, no?

fervent viper
#

You are correct, and to solve this problem, the most simple (but not efficient) solution is to change the Map to array, and instead of looking up entity types, just use instanceof comparisons?

flat hollow
#

yeah, that'd be pretty easy to do

#

also yeah just add an abstract so it's abstract new (...args: never) => Entity so you could pass abstract classes to get all the appropriate classes

fervent viper
#

do you think it will get much slower if u imagine 1000 entities inside the array?

flat hollow
#

no

#

computers are fast

#

once you get into the millions is where you should start to worry about perf

#

(of course that also applies to if you have 1000 entities but you're looking that up 1000 times)