You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
constmatcher=Symbol("@ts-pattern/matcher");typeMatcherProtocol<input>={match: <I>(value: I|input)=>void;};interfaceMatcher<input>{[matcher](): MatcherProtocol<input>;}typePattern<a>=|Matcher<a>|(aextendsreadonly[any, ...any]
? {readonly[indexinkeyofa]: Pattern<a[index]>}
: aextendsobject
? {readonly[kinkeyofa]: Pattern<a[k]>}
: a);typeMatch<i>={with<pextendsPattern<i>>(pattern: p): void;};declarefunctionmatch<input>(value: input): Match<input>;declarefunctionunion<input,psextends[Pattern<input>, ...Pattern<input>[]]>(
...patterns: ps): Matcher<input>;declarefunctionwhen<input,pextends(value: input)=>unknown>(predicate: p): Matcher<input>;// those have broken inferencesmatch<"a"|"b">("a").with(union("a"));match<"a"|"b">("a")// @ts-expect-error.with(union("this is wrong"));match<"a"|"b">("a").with(// this should not be an error, since `x` should be `'a' | 'b'` and not `unknown`when((x)=>{leta: "a"|"b"=x;returnx;}));// those have correct inferencesmatch<{type: "a"|"b"}>({type: "a"}).with({type: union("a"),});match<{type: "a"|"b"}>({type: "a"}).with({// @ts-expect-errortype: union("this is wrong"),});match<{type: "a"|"b"}>({type: "a"}).with({type: when((x)=>{leta: "a"|"b"=x;returnx;}),});
π Actual behavior
In cases with match<"a" | "b">("a").with(...) the inference is broken whereas in cases with match<{ type: "a" | "b" }>({ type: "a" }).with(...) things work as expected.
π Expected behavior
Both cases should work the same as the position in the with's argument should not matter.
This is a distilled case from ts-pattern by @gvergnaud . The real thing (equivalent of this repro case) can be tested out in this TS playground.
In the broken case, in inferTypeParameters (for the nested call):
all of this leads to inferring unknown when a better inference could have been found (Pattern<'a' | 'b'>)
In the working case, within the same inferTypeParameters we get those:
inferenceTargetType -> Matcher<input>
contextualType -> Pattern<"a" | "b">
instantiatedType: Pattern<"a" | "b">
It's worth noting down that outerMapper is the same in both cases (p -> never) but in the working case it's simply not used because the contextualType has no type variables so instantiateTypeWithAlias returns early with the supplied argument here (Pattern<"a" | "b">).
Thanks to that the getTypeOfPropertyOfContextualType can return Pattern<"a" | "b">here in the getContextualTypeForObjectLiteralElement.
It's also worth noting that the current version of ts-pattern works OK because we can "observe" this whole silentNeverType in the userland (which, I think, shouldn't be possible) here and we can return there what we originally expected there to be computed for us. The minimal repro case from this issue with this "hack" being applied can be found here
The text was updated successfully, but these errors were encountered:
I'm preparing a new version of TS-Pattern that uses the const T type parameter modifier to get even better inference and it turns out that const T doesn't work with our little IsNever hack, so I would really like to see this fixed in the type checker :)
I'm preparing a new version of TS-Pattern that uses the const T type parameter modifier to get even better inference and it turns out that const T doesn't work with our little IsNever hack, so I would really like to see this fixed in the type checker :)
This is interesting! could you prepare a repro case for this problem? :P I wonder how const modifier affects this stuff.
Bug Report
π Search Terms
contextual type nested inference
π Version & Regression Information
β― Playground Link
Playground link with relevant code
π» Code
π Actual behavior
In cases with
match<"a" | "b">("a").with(...)
the inference is broken whereas in cases withmatch<{ type: "a" | "b" }>({ type: "a" }).with(...)
things work as expected.π Expected behavior
Both cases should work the same as the position in the
with
's argument should not matter.This is a distilled case from ts-pattern by @gvergnaud . The real thing (equivalent of this repro case) can be tested out in this TS playground.
In the broken case, in
inferTypeParameters
(for the nested call):contextualType
isp
inferenceTargetType
isMatcher<input>
instantiatedType
gets calculated asnever
since it's using theouterMapper
withInferenceFlags.NoDefault
. This in turn assignssilentNeverType
to theinferredType
hereunknown
when a better inference could have been found (Pattern<'a' | 'b'>
)In the working case, within the same
inferTypeParameters
we get those:inferenceTargetType
->Matcher<input>
contextualType
->Pattern<"a" | "b">
instantiatedType
:Pattern<"a" | "b">
It's worth noting down that
outerMapper
is the same in both cases (p -> never
) but in the working case it's simply not used because thecontextualType
has no type variables soinstantiateTypeWithAlias
returns early with the supplied argument here (Pattern<"a" | "b">
).Looking at the previous steps we can learn that the returned
contextualType
here is better in the working case becausegetContextualTypeForObjectLiteralElement
checksgetApparentTypeOfContextualType
and there:contextualType
->p
instantiatedType
->p
apparentType
->Pattern<{ type: "a" | "b"; }>
Thanks to that the
getTypeOfPropertyOfContextualType
can returnPattern<"a" | "b">
here in thegetContextualTypeForObjectLiteralElement
.It's also worth noting that the current version of
ts-pattern
works OK because we can "observe" this wholesilentNeverType
in the userland (which, I think, shouldn't be possible) here and we can return there what we originally expected there to be computed for us. The minimal repro case from this issue with this "hack" being applied can be found hereThe text was updated successfully, but these errors were encountered: