Having a problem with the type system not accurately determining the output type based on the inputs.
Types Used:
type inputs = "alpha" | "beta" | "gamma";
type AlphaSchema = {
pet: string,
house: string
}
type BetaSchema = {
boss: string,
work: string,
}
type GammaSchema = {
monk: string,
temple: string,
}
type Schemas = {
'alpha': AlphaSchema,
'beta': BetaSchema,
'gamma': GammaSchema
}
type BuilderStore = {
buildersFor: {
[key in inputs] : {
build: (str1: string, str2: string) => Schemas[key]
}
};
}
Working Example:
const exampleSchemaResults = {
'alpha' : {pet: 'scooby', house: 'West Garden Home'},
'beta' : {boss: 'Jake', work: 'The Royal Palace'},
'gamma' : {monk: 'Tai', temple: 'Shaolin'},
}
const getExampleSchema: <T extends inputs>(input: T) => Schemas[T] = (input) => exampleSchemaResults[input];
const alphaExample = getExampleSchema('alpha');
In this case typescript is able to accurately determine that the obtained result will be of type AplhaSchema
Example that does not resolve:
const store: BuilderStore = {
buildersFor : {
'alpha': {
build: (str1: string, str2: string): AlphaSchema => { return {pet: str1, house: str2} },
},
'beta': {
build: (str1: string, str2: string): BetaSchema => { return {boss: str1, work: str2} },
},
'gamma': {
build: (str1: string, str2: string): GammaSchema => { return {monk: str1, temple: str2} }
}
}
}
const getBuilder = <T extends inputs>(input : T) => store.buildersFor[input];
const build = <T extends inputs>(input: T, str1: string, str2: string) => getBuilder(input).build(str1, str2);
const schema = build('alpha', 'scooby', 'West Garden Home');
Issue here is that the final schema obtained is of type :
AlphaSchema | BetaSchema | GammaSchema
I want it to be type :
AlphaSchema
I understand this code generates the schema using the build function, but shouldn't this resolve similar to the working snippet mentioned above ?
Can we not resolve output schema based of the inputs (given that 'alpha' is used in the generic) ? Or is this not possible ?
Is there something wrong in the way the functions are typed ?
TS playground link
Interesting behavior. I also would have expected that to work. To me it seems that when TS is statically creating the "type" of getBuilder(input).build it doesn't know which builder function is going to be called. If you add an explicit return type to the build function you can see the error more clearly.
const build = <T extends inputs>(input: T, str1: string, str2: string): Schemas[T] => getBuilder(input).build(str1, str2);

This seems to confirm that TS isn't aware of which build function is going to be called, so the return type is all of them.
To fix it, you can cast the return type to the correct Schema:
const build = <T extends inputs>(input: T, str1: string, str2: string): Schemas[T] => (getBuilder(input).build(str1, str2) as Schemas[T]);
This removes all errors and gives that final schema variable the right type.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With