This example doesn't typecheck:
type Subset1 = "one" | "two";
type Subset2 = "three" | "four";
type All = Subset1 | Subset2;
type Other = {
"one": number,
"two": string,
"three": boolean,
"four": object,
};
type Extra<V> = V extends Subset1 ? string : undefined;
function doOtherThing(stuff: string){}
function doThing<V extends All>(value: V, params: Other[V], extra: Extra<V>) { }
function doSubset1Thing<V extends Subset1>(value: V, params: Other[V], extra: string) {
doThing(value, params, extra);
doOtherThing(extra);
}
function doSubset2Thing<V extends Subset2>(value: V, params: Other[V]) {
doThing(value, params, undefined);
}
(TS Playground)
The error is because extra
is hardcoded string
in doSubset1Thing
, but logically it is always a string because value
is limited to Subset1
, and Extra<Subset1>
properly resolves to string
, but for some reason, the call to doThing
does not recognize that.
Similarly, inverting it for doSubset2Thing
errors even though the third param will always be undefined
.
For the second one, I can sort of see issues if Subset1
and Subset2
overlapped, but they don't, so I'd have assumed that TS would flatted that all out to undefined
for doSubset2Thing
.
Is there any way to make this work? Alternatively, am I missing something that actually does make this invalid?
As far as I can tell, this is an instance where your code is logically correct and type-safe, but Typescript just isn't able to prove it because it lacks a rule that would be able to prove it. A simple rule like "V
must extend Subset1
because that's its upper bound" would be good enough, but apparently Typescript isn't (currently) programmed to use such a rule.
One fix, which may make more sense for your use-case than a conditional type anyway, is to use function overloads: this also saves you from having to pass an explicit undefined
in the second case.
function doThing<V extends Subset1>(value: V, params: Other[V], extra: string): void;
function doThing<V extends Subset2>(value: V, params: Other[V]): void;
function doThing<V extends All>(value: V, params: Other[V], extra?: string): void {
// ...
}
Playground Link
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With