Home > Net >  Type "is assignable to the constraint of type 'T', but 'T' could be instant
Type "is assignable to the constraint of type 'T', but 'T' could be instant

Time:10-31

I'm struggling with this error message in TypeScript. I've looked at the following SO answers and I simply don't understand the solutions.

Here is a stripped down version of my code with the error:

interface R {
    field: string;
    operator: string;
    value: any;
}
interface RG {
    combinator: 'and'|'or';
    rules: (R|RG)[];
}
interface RGIC {
    rules: (RGIC|R|string)[];
}
type RGT = RG | RGIC;

const f = <T extends RGT>(r: T): T => {
    if ('combinator' in r) {
        return { combinator: 'and', rules: [] }; // <-- TS error here
    }
    return { rules: [] }; // <-- TS error here
}

What I want to express is that if r is of type RG, then function f will return an object that is also type RG. If r is of type RGIC, then function f will return an object that is also type RGIC. But this error is confounding me.

TS Playground link.

CodePudding user response:

The problem you're running into is that generic constraints of the form T extends RGT does not mean "T must be exactly either RG or RGIC". All it implies is that T is compatible with RGT, meaning that it is a subtype of or assignable to RGT. There are subtypes of RGT which are more specific than either RG or RGIC, such as:

interface Whaa {
    combinator: 'or',
    rules: R[],
    brainCellCount: number;
}

const whaa: Whaa = {
    combinator: 'or',
    rules: [],
    brainCellCount: 86e9
}

The Whaa interface is assignable to RG; every Whaa is a valid RG. But a Whaa has a combinator which must be "or", and it also has an additional brainCellCount property of type number.

If your function f has a call signature like this:

declare const f: <T extends RGT>(r: T) => T;

you're saying that the return type of f will be exactly the same as the type of the passed-in r. And therefore the following call will produce a result of type Whaa also:

const hmm = f(whaa);
// const hmm: Whaa

And if so, then this will be fine:

hmm.brainCellCount.toFixed(2); // no compiler error
// BUT AT RUNTIME:            
  • Related