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
{{ message }}
Repository navigation
Reverse mapped types don't use their constraint types when no candidates are present unlike the regular type parametersΒ #56241
Type '(_: { count: number; }, { foo }: { foo: string; }) => boolean' is not assignable to type '(context: { count: number; }, params: Record<string, unknown> | undefined) => void'.
Types of parameters '__1' and 'params' are incompatible.
Type 'Record<string, unknown> | undefined' is not assignable to type '{ foo: string; }'.
Type 'undefined' is not assignable to type '{ foo: string; }'.(2322)
π Expected behavior
I'd expect the inference to succeed here
Additional information about the issue
TGuards is inferred as { checkFoo: { foo: string; }; alwaysTrue: unknown; } and thus rejected by the constraint check in getInferredType. alwaysTrue is inferred as unknown because inferReverseMappedType has such a return:
And getTypeFromInference doesn't attempt to read the type parameter's constraint at all. It simply handles .candidates and .contraCandidates and that's it.
Since Ryan Cavanaugh (@RyanCavanaugh) closed #61228 as duplicate, I'm adding the context from that one, since it's slightly different from what is mentioned here:
π Search Terms
tuple inference with function literal
inference tuple function and object literal
mixing tuple objects and function widening
π Version & Regression Information
This is the behavior in every version I tried (from 4.0.5 to 5.8.1-rc), and I reviewed the FAQ for entries possibly related to this
interfaceMatcherObj{name: string;}typeMatcherFn=(name: string)=>booleantypeMatcher=MatcherObj|MatcherFntypeMapTuple<KeysTupleextendsunknown[],MapToValue>={[IndexinkeyofKeysTuple]: MapToValue;}interfaceDefinition<MatchersTextendsMatcher[]>{matches: [...MatchersT];computeValueFromMatches: (
...matches: MapTuple<MatchersT,Matcher[]>)=>number;}interfaceConfig<Defsextends{[KinkeyofDefs]: Definition<any>}>{definition: Defs;}functioncreate<Defsextends{[KinkeyofDefs]: Matcher[]}>(config: Config<{[KinkeyofDefs]: Definition<Defs[K]>}>){// ...}create({definition: {valid: {matches: [{name: "m3"}],computeValueFromMatches: (m)=>m.length,},too_mang_args: {matches: [{name: "m1"},{name: "m2"}],// below correctly errors "Target signature provides too few arguments. Expected 3 or more, but got 2."computeValueFromMatches: (m1,m2,m3)=>m1.length+m2.length,},not_enough_args: {matches: [],// below correctly errors "Target signature provides too few arguments. Expected 1 or more, but got 0."computeValueFromMatches: (m)=>123},// valid_using_function: {// matches: [(a) => a === 'x'],// computeValueFromMatches: (m) => m.length// },// as soon as you uncomment 'valid_using_function' all the errors go away, and inference breaks!},})
π Actual behavior
When all elements in the matches tuple are object literals, TypeScript infers an exact tuple type (e.g. [MatcherObj] or [MatcherObj, MatcherObj]), and the signature of computeValueFromMatches enforces the correct number of arguments.
However, if a function literal (or an object with a function as a property) is included in the tupleβas shown in the commented-out valid_using_function case β the inference for ALL the cases breaks (not just the one property!), as the entire type widens to MatcherObj[].
This causes the relationship between the number of tuple elements and the number of parameters expected by computeValueFromMatches to be lost, so TypeScript no longer emits errors for other keys when the number of parameters is incorrect.
π Expected behavior
TypeScript should preserve the literal tuple type even when a function literal is included.
In the example case, the tuple length of the matches property should be inferred exactly as provided, so that computeValueFromMatches enforces a parameter count matching the tuple length.
This behavior should be consistent regardless of whether the tuple elements are object literals or function literals.
Additional information about the issue
This issue does not occur when using object literals exclusively in the matches tuple. It only manifests when a function literal is included, suggesting that the inference algorithm incorrectly widens the tuple type in that scenario.
What's even more bizarre, even if you change the Matcher type to be an object whose a property can be a function, the same issue occurs! i.e.
Activity
niieani commentedon Feb 27, 2025
Since Ryan Cavanaugh (@RyanCavanaugh) closed #61228 as duplicate, I'm adding the context from that one, since it's slightly different from what is mentioned here:
π Search Terms
π Version & Regression Information
β― Playground Link
Playground
π» Code
π Actual behavior
When all elements in the
matchestuple are object literals, TypeScript infers an exact tuple type (e.g.[MatcherObj]or[MatcherObj, MatcherObj]), and the signature ofcomputeValueFromMatchesenforces the correct number of arguments.However, if a function literal (or an object with a function as a property) is included in the tupleβas shown in the commented-out
valid_using_functioncase β the inference for ALL the cases breaks (not just the one property!), as the entire type widens toMatcherObj[].This causes the relationship between the number of tuple elements and the number of parameters expected by
computeValueFromMatchesto be lost, so TypeScript no longer emits errors for other keys when the number of parameters is incorrect.π Expected behavior
TypeScript should preserve the literal tuple type even when a function literal is included.
In the example case, the tuple length of the
matchesproperty should be inferred exactly as provided, so thatcomputeValueFromMatchesenforces a parameter count matching the tuple length.This behavior should be consistent regardless of whether the tuple elements are object literals or function literals.
Additional information about the issue
This issue does not occur when using object literals exclusively in the
matchestuple. It only manifests when a function literal is included, suggesting that the inference algorithm incorrectly widens the tuple type in that scenario.What's even more bizarre, even if you change the Matcher type to be an object whose a property can be a function, the same issue occurs! i.e.
As soon as any of the defined
matchesuses a function under thenameproperty, e.g.{ name: () => true }, causes same widening behavior!I have thoroughly searched the TypeScript repository and online discussions but have not found a related issue.