I have a abstract base class with a generic type parameter like this:
abstract class Base<T> {
constructor(protected value: T) {}
}
I inherit multiple classes from the base. For example:
class TBase extends Base<number> {
constructor(value: number) {
super(value);
}
}
class XBase extends Base<string> {
constructor(value: string) {
super(value);
}
}
Now I want to write a factory function that returns a new Base based on my input properties:
type ValidTypes = 'string' | 'integer' | 'decimal' | 'boolean' | 'datetime';
type validFor<T> = T extends 'string' | 'datetime'
? string
: T extends 'integer' | 'decimal'
? number
: T extends 'boolean'
? boolean
: never;
function getBase<T extends ValidTypes, P extends validFor<T>>(type: T, value: P): Base<P> {
switch(type) {
case 'number': new TBase(coerceNumber(value)); break;
...
}
}
When passing 'string' as first parameter, the second can only be of type string. For 'decimal', type P can only be a number.
But I have two problems. When calling the function like this:
getBase('string', '5');
It works, but it says that the signature is
function getBase<'string', '5'>(type: 'string', value: '5'): Base<'5'>
It don't understand why it's not resolving to string but instead to the value of value?
The other problem is, that when I return a new TBase() it states that it could also be a string or boolean:
"'number' is assignable to the constraint of type 'P', but 'P' could be instantiated with a different subtype of constraint 'string | number | boolean'."
I searched a lot about this, but couldn't get around it. Could someone explain to me why excactly this happens? Even when i explicit cast it to a Base it throws the error (return new TBase() as Base)
Another approach I tried was with using function overloads, but this looks kinda weird and not right:
getBase(type: 'decimal' | 'integer', value: number): Base<number>
getBase(type: 'string' | 'datetime', value: string): Base<string>
getBase(type: 'boolean', value: boolean): Base<boolean>
getBase(type: ValidTypes, value: number | boolean | string): Base<number | boolean | string> {
...
}
I want to something like this:
getBase('string', 'thisMustBeAString'); // => Base<string>;
getBase('decimal', 54 /* Must be a number */) // => Base<number>;
What am I missing? I'm quiet struggling with this for a long time now.. Thanks in advance
Edit:
coerceNumberUnable to reproduce your code. Could you please share it in typescript playground? - captain-yossariangetBasefunction. Please let me know if it is ok for you - captain-yossarian