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
Homomorphic mapped generic array type sometimes no longer seen as an arrayΒ #57744
typeMustBeArray<Textendsany[]>=TtypeHmm<Textendsany[]>=Textendsnumber[] ?
MustBeArray<{[IinkeyofT]: 1}> : // <-- error!// ~~~~~~~~~~~~~~~~~~~~~// Type '{ [I in keyof T]: 1; }' does not satisfy the constraint 'any[]'.// Types of property 'toString' are incompatible.// Type 'number' is not assignable to type '() => string'.(2344)never;typeX=Hmm<[3,4,5]>// ^? type X = [1, 1, 1]
π Actual behavior
{[I in keyof T]: 1} is not seen as an array type inside of Hmm<T>, causing a compiler error. Something about the conditional type re-constraining T to another array type causes it to fail.
I'm a bit concerned that I can't seem to work around this without //@ts-ignore, since the normal approaches of Extract<T, any[]> don't help (they end up just doing the same conditional type thing again which break things) and T & any[] kind of works but you end up with intersections of arrays which nobody likes.
changed the title [-]Homomorphic mapped generic array type no longer seen as an array[/-][+]Homomorphic mapped generic array type sometimes no longer seen as an array[/+]on Mar 12, 2024
The constraint of T becomes number[] & any[] here (it comes from getBaseConstraint(getSubstitutionIntersection(t))) and that's not exactly something that satisfies isArrayOrTupleType check in getResolvedApparentTypeOfMappedType.
The reason the error shows up in the OP is that with #57122 (which is in 5.4) we now recognize that the conditional type adds the constraint number[] to T in the true branch, meaning that when T is any[] we get the constraint any[] & number[].
The fix here is probably to distribute the homomorphic mapped type over intersections, i.e. treat Mappy<string[] & number[]> as Mappy<string[]> & Mappy<number[]>, but I need to think on this a bit.
Yeah, homomorphic mapped types "should" distribute over intersections, but I guess I find it a little surprising that the combination of two array constraints just produces an intersection to begin with, since intersections of array types are so problematic.
I would (perhaps naively) expect that number[] & string[] reduces to never or never[] and number[] & any[] reduces to number[] (or if any[] is weird, then at least with unknown[]). Is that (deliberately) not the case?
Could someone triage this? Is it a bug/limitation/intended? If it's going to stick around like this for a while is there any workaround for people who run into it? Thanks!
I'm considering the best way to fix this. The core issue is that mapped types applied to array intersections do not produce arrays, and that's is a design limitation. However, the error in the repro is a regression, which is unfortunate.
The workaround is to remove the constraint from the type parameter and instead add it through an indirection:
Activity
[-]Homomorphic mapped generic array type no longer seen as an array[/-][+]Homomorphic mapped generic array type sometimes no longer seen as an array[/+]Andarist commentedon Mar 12, 2024
The constraint of
Tbecomesnumber[] & any[]here (it comes fromgetBaseConstraint(getSubstitutionIntersection(t))) and that's not exactly something that satisfiesisArrayOrTupleTypecheck ingetResolvedApparentTypeOfMappedType.ahejlsberg commentedon Mar 12, 2024
Right, the core issue is that when a homomorphic mapped type is applied to an intersection of array types, the resulting type isn't an array type:
The reason the error shows up in the OP is that with #57122 (which is in 5.4) we now recognize that the conditional type adds the constraint
number[]toTin the true branch, meaning that whenTisany[]we get the constraintany[] & number[].The fix here is probably to distribute the homomorphic mapped type over intersections, i.e. treat
Mappy<string[] & number[]>asMappy<string[]> & Mappy<number[]>, but I need to think on this a bit.jcalz commentedon Mar 13, 2024
Yeah, homomorphic mapped types "should" distribute over intersections, but I guess I find it a little surprising that the combination of two array constraints just produces an intersection to begin with, since intersections of array types are so problematic.
ehoogeveen-medweb commentedon Mar 13, 2024
I would (perhaps naively) expect that
number[] & string[]reduces toneverornever[]andnumber[] & any[]reduces tonumber[](or ifany[]is weird, then at least withunknown[]). Is that (deliberately) not the case?jcalz commentedon Mar 14, 2024
Could someone triage this? Is it a bug/limitation/intended? If it's going to stick around like this for a while is there any workaround for people who run into it? Thanks!
ahejlsberg commentedon Mar 14, 2024
I'm considering the best way to fix this. The core issue is that mapped types applied to array intersections do not produce arrays, and that's is a design limitation. However, the error in the repro is a regression, which is unfortunate.
The workaround is to remove the constraint from the type parameter and instead add it through an indirection: