I'm designing a generic Slice<E>
class which represents a slice of instances of a set of instances. For example if we have Todo
instances, then the slice could represent all the ones that are completed.
If the Todo
class has an id
property we would like to set hasID
to true
on the Slice
instance, such that we can also index the Slice
instance by id
. Is there a way to detect at runtime whether the generic argument has and id
property?
There's not a lot here to go on without code.
On the face of it: no, you cannot possibly analyze a generic type argument at runtime since the type system is erased. At runtime you can only analyze runtime values, such as the actual objects you pass in to the constructor. At compile time you can probably make the compiler give a specific true
or false
boolean literal type to hasID
based on the generic type parameter. And you can do both of those things and hope that you've got a solution where the runtime values actually match up with compile-time types.
Let's try it. Here's a sketch of a possible solution:
class Slice<E> {
instances: E[]
hasID: E extends {id: any} ? true : false;
constructor(firstInstance: E, ...restInstances: E[]) {
this.hasID = 'id' in firstInstance as Slice<E>['hasID'];
this.instances = [firstInstance, ...restInstances];
}
}
The hasID
property is given a conditional type that evaluates the E
type parameter and returns true
if E['id']
exists, and false
otherwise.
The constructor accepts at least one parameter of type E
, the first one of which is analyzed at runtime for an id
property in order to set the hasID
runtime value.
Let's see if it works:
const sliceOne = new Slice({a: 1}, {a: 2});
sliceOne.hasID // false at compile time and runtime
const sliceTwo = new Slice({id: 1}, {id: 2});
sliceTwo.hasID // true at compile time and runtime
Looks good. Still there are edge cases you might need to worry about, like:
declare const notSure: object;
const oopsie = new Slice(notSure);
oopsie.hasID; // false at compile time, maybe true at runtime!
The compiler can't verify that object
has an id
property, so it gives hasID
the type false
. But of course an object
may have an id
property, so maybe hasID
will be true
at runtime. Is there a way to deal with this? Maybe. But it's not straightforward. The question is how likely you are to run into these cases and if you care about them.
Anyway, hope that makes sense and gives you some direction. Good luck!