1

I have trouble understanding why TypeScript infers program.options here as ProgramAOptions | ProgramBOptions. Thus it cannot compile code since optA does not exist in ProgramBOptions. Can you explain or point me to documentation which explains this behaviour?

type ProgramName = 'a' | 'b';

type ProgramAOptions = {
    optA: number;
};

type ProgramBOptions = {
    optB: number;
};

type Program<T extends ProgramName> = {
    name: T;
    options: T extends 'a' ? ProgramAOptions : ProgramBOptions;
};name

function test(p: Program<ProgramName>) : void
{
    if (p.name === 'a')
    {
        p.options.optA = 10; /* this line would not compile with error:

        error TS2339: Property 'optA' does not exist on type 'ProgramAOptions | ProgramBOptions'.
        Property 'optA' does not exist on type 'ProgramBOptions'.*/
    }
}

1 Answer 1

2

Here is how I'd solve your problem, by explicitly declaring interfaces for every couple of names and options:

type ProgramName = 'a' | 'b';

interface ProgramAOptions {
    optA: number;
};

interface ProgramBOptions {
    optB: number;
};

type ProgramOptions = ProgramAOptions | ProgramBOptions;

interface Program {
    name: ProgramName;
    options: ProgramOptions;
}

interface ProgramA extends Program {
    name: 'a';
    options: ProgramAOptions;
}

interface ProgramB extends Program {
    name: 'b';
    options: ProgramBOptions;
}

type Programs = ProgramA | ProgramB;

function test(p: Programs): void {
    if (p.name === 'a') {
        p.name // is of type "a"
        p.options.optA = 10; // Works
    }
}

TypeScript playground

Sign up to request clarification or add additional context in comments.

2 Comments

This works great indeed, +1, nevertheless I would love to understand why 'conditional types' way does not.
Because type narrowing works on single variables and on union types. In your case you have a single generic type in which you provided yourself the T type so you give no room to type narrowing

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.