Interface method return type conditional on other member

Viewed 18

Assuming that the value of a member (single) is statically known (i.e. a literal boolean in this case), is it possible to pick the return type of another member method based on that?

interface I {
    single: boolean;
    // Want the return type to be 'number'
    // if single === true and 'Set<number>' otherwise.
    returnSingle: () => number | Set<number>
}

Edit: would this be correct?

type SingleOrSet<C extends boolean> = C extends true ? number : Set<number>;

interface I<T extends boolean> {
    single: T;
    // Want the return type to be 'number'
    // if single === true and 'Set<number>' otherwise.
    returnSingle: () => SingleOrSet<T>
}

const p: I<true> = {single: true, returnSingle: () => {return 1}};

Is there a way for T to be inferred in usage so it doesn't have to be passed as a type argument to I?

1 Answers

You can do it with a union type using an object type with single: true and another with single: false:

type I =
    |   {
        single: true;
        returnSingle: () => number;
    }
    |   {
        single: false;
        returnSingle: () => Set<number>;
    };

Playground link

Or you could provide aliases for the union members, which might be clearer:

interface ISingle {
    single: true;
    returnSingle: () => number;
}
interface IMultiple {
    single: false;
    returnSingle: () => Set<number>;
}
type I = ISingle | IMultiple;

Playground link

With either of those, these work:

const i1: I = {
    single: true,
    returnSingle: () => 42,
};

const i2: I = {
    single: false,
    returnSingle: () => new Set([42]),
};

...but these raise errors as desired:

const i3: I = {
    single: false,
    returnSingle: () => 42,
};

const i4: I = {
    single: true,
    returnSingle: () => new Set([42]),
};
Related