Problem
class sample<T>
is a generic class where the type of this.prop
depends on the type of the generic type parameter T
for that instance.
Typically a generic class is set up so that the type of T
for an instance will be inferred from the arguments which are passed to the constructor. Your constructor just has (x : any)
, so the type of T
will always be unknown
unless you explicitly set it by calling new sample<string>("")
.
The argument x
is any
, so it's fine to call new sample<number>("")
. In that case T
is number
. But typeof x === "string"
is true
so you will be setting this.prop
, which must be myGenericInterface<number>
to implementationA
which is myGenericInterface<string>
. So hopefully you can see why Typescript gives you an error on this line.
Mediocre Solution
I think what you wanted to do was to have x
be of type T
and to constrain T
such that it can only be string
or number
.
You can do that, but it's not 100% type-safe as it requires us to make an assertion which is probably correct but we can't guarantee it. When we check typeof x === "string"
that will refine the type of x
to just string
, but it won't refine the type of T
because it's technically possible that T
is the union string | number
or that T
is a subset of string
. So we have to use as
to suppress the Typescript error.
class sample<T extends string | number> {
prop: myGenericInterface<T>;
constructor(x: T) {
if (typeof x === "string") {
// x is known to be string, but we don't know that T is string
this.prop = new implementationA() as myGenericInterface<T>
} else {
this.prop = new implementationB() as myGenericInterface<T>
}
}
}
Good Solution
We can guarantee type-safety for any type (not just string | number
) if we just create the object which implements myGenericInterface<T>
ourselves. If we set propX
to x
then we don't need to know or care what the actual type of T
is because we know that x
is assignable to T
so {propX: x}
is assignable to myGenericInterface<T>
.
class sample<T> {
prop: myGenericInterface<T>;
constructor(x: T) {
this.prop = {
propX: x,
}
}
}
You can use this same approach with an extra step by using a generic class to implement myGenericInterface<T>
.
class GenericImplementation<T> implements myGenericInterface<T> {
propX: T;
constructor(value: T) {
this.propX = value;
}
}
class sample<T> {
prop: myGenericInterface<T>;
constructor(x: T) {
this.prop = new GenericImplementation(x);
}
}
Typescript Playground Link