Kuba Wyrostek
Kuba Wyrostek

Reputation: 6221

Understanding type inference with conditional types

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'.*/
    }
}

Upvotes: 1

Views: 63

Answers (1)

Guerric P
Guerric P

Reputation: 31815

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

Upvotes: 2

Related Questions