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.
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: