Repository navigation
Closed
Description
🔎 Search Terms
"constraint evaluation", "type infer"
🕗 Version & Regression Information
- This changed between versions 4.9 and 5.0
In Version 5.x, parentheses changes the result of type inference;
In Version 4.9 - 4.7(The minimum version that supports infer X extends ...), parentheses not changes the result of type inference;
⏯ Playground Link
💻 Code
type PositiveInfinity = 1e999;
type NegativeInfinity = -1e999;
export type IsEqual<A, B> =
(<G>() => G extends A ? 1 : 2) extends
(<G>() => G extends B ? 1 : 2)
? true
: false;
export type Add<A extends number, B extends number> = [
IsEqual<A, PositiveInfinity>, IsEqual<A, NegativeInfinity>,
IsEqual<B, PositiveInfinity>, IsEqual<B, NegativeInfinity>,
] extends infer R extends [boolean, boolean, boolean, boolean]
? [true, false] extends ([R[0], R[3]]) // Note: with parentheses
? PositiveInfinity
: 'failed'
: never;
export type AddWithoutParentheses<A extends number, B extends number> = [
IsEqual<A, PositiveInfinity>, IsEqual<A, NegativeInfinity>,
IsEqual<B, PositiveInfinity>, IsEqual<B, NegativeInfinity>,
] extends infer R extends [boolean, boolean, boolean, boolean]
? [true, false] extends [R[0], R[3]] // Note: without parentheses
? PositiveInfinity
: 'failed'
: never;
type AddTest0 = Add<PositiveInfinity, PositiveInfinity>; // failed
type AddTest1 = AddWithoutParentheses<PositiveInfinity, PositiveInfinity>; // in version 5.x: "Infinity"; but in version 4.x, it's "failed"🙁 Actual behavior
type AddTest0 = Add<PositiveInfinity, PositiveInfinity>; // "failed" (both in version 4.x and 5.x, it is)
type AddTest1 = AddWithoutParentheses<PositiveInfinity, PositiveInfinity>; // "Infinity" in version 5.x; but in version 4.x, it's "failed"🙂 Expected behavior
// Better be able to do:
type AddTest0 = Add<PositiveInfinity, PositiveInfinity>; // Infinity (both in version 4.x and 5.x)
type AddTest1 = AddWithoutParentheses<PositiveInfinity, PositiveInfinity>; // Infinity (both in version 4.x and 5.x)
// at least:
type AddTest0 = Add<PositiveInfinity, PositiveInfinity>; // failed (both in version 4.x and 5.x)
type AddTest1 = AddWithoutParentheses<PositiveInfinity, PositiveInfinity>; // failed (both in version 4.x and 5.x)Additional information about the issue
see also: #51090
Reacted by ExE Boss
Metadata
Metadata
Assignees
Labels
No labels
Activity
Andarist commentedon Oct 31, 2023
The original PR that made the non-parenthesized version to work can be found here: #52091
ahejlsberg commentedon Oct 31, 2023
This is working as intended, though I agree it is a bit surprising. We gramatically check for conditional types of the pattern
[X] extends [Y] ? ...(where[X]and[Y]are tuple types of the same arity) and defer their resolution if some of theXorYelements are generic. This is such that non-distributable conditional types can be written[X] extends [Y] ? ...and be deferred similarly toX extends Y ? .... We document this behavior here.We specifically don't first erase parentheses because that is just one of untold number of ways you could write a type that turns out to be a tuple type. The tuple types are in a sense immaterial (when they match on both sides they are effectively erased), but we want the explicit matching brackets on both sides to be the signal that this type is non-distributive.
jakebailey commentedon Oct 31, 2023
Won't this mean that running a formatter which removes or adds parens will change behavior?
Andarist commentedon Oct 31, 2023
It does mean that - it happened to me just today when I was playing with this ;p
jakebailey commentedon Oct 31, 2023
I feel like unwrapping parens is a good idea exactly for that reason. Formatters frequently unparen or reparen expressions depending on their position and complexity. They won't add or remove other syntax, though, so I don't think we're breaking into the realm of the other ways to make a tuple type by handling it.
Max10240 commentedon Nov 1, 2023
Anders Hejlsberg (@ahejlsberg) So using "infer" makes it work, because "infer" leads to something like that(
defer their resolution), right? 😃