Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript : how to make type system accurately determine output type based on inputs using <generics>

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

like image 484
Arvind Kumar Avatar asked Jan 31 '26 05:01

Arvind Kumar


1 Answers

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);

TS error

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.

like image 90
yts Avatar answered Feb 01 '26 21:02

yts



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!