Code Monkey home page Code Monkey logo

Comments (5)

DanielRosenwasser avatar DanielRosenwasser commented on April 27, 2024

I think this is working as intended in TypeScript 5.4:

https://devblogs.microsoft.com/typescript/announcing-typescript-5-4/#more-accurate-conditional-type-constraints

In a conditional type like T extends Foo ? TrueBranch : FalseBranch, where T is generic, the type system would look at the constraint of T, substitute it in for T itself, and decide on either the true or false branch.

But this behavior was inaccurate because it was overly-eager.
Even if the constraint of T isn't assignable to Foo, that doesn't mean that it won't be instantiated with something that is.
And so the more correct behavior is to produce a union type for the constraint of the conditional type in cases where it can't be proven that T never or always extends Foo.

TypeScript 5.4 adopts this more accurate behavior.
What this means in practice is that you may begin to find that some conditional type instances are no longer compatible with their branches.

The way you can get around this is by either

  1. changing the constraint of Arr in AssignResult to unknown[] instead of object[] (playground link),
  2. adding an object constraint to Head and an object[] constraint to Tail (playground link).

from typescript.

Alexsey avatar Alexsey commented on April 27, 2024

@DanielRosenwasser Thank you for taking a look, this is probably the change that cause the error!

But here the situation is right the opposite of how it says it should work:
"if the constraint of T isn't assignable to Foo, that doesn't mean that it won't be instantiated with something that is."
-> in the example:
"if the condition of Filter isn't assignable to AssignResult, that doesn't mean that it won't be instantiated with something that is.". And Filter<(object | null)[], null> will be instantiated with object[] - exactly with the type that AssignResult is expecting!
So here we are having a "cases where it can't be proven that T never or always extends Foo", but the compiler is considering that it can be proven

The difference between WorksPrior54 and Works is an additional step we are making the compiler take (in the "full code", the additional step is the intermediate Source type parameter - inlining it is fixing the error). This additional step seems to break the new improved functionality

from typescript.

DanielRosenwasser avatar DanielRosenwasser commented on April 27, 2024

So without having been deeply in the guts of conditional type instantiation, I'm going to do my best on this one!

From the #56004:

A type variable represents any possible type within its constraint. So, given a type parameter T with the constraint C, the constraint of T extends X ? A : B is

  • A when C is known to always extend X,
  • B when C is known to never extend X, or
  • A | B when C possibly extends X.

We previously didn't consider the third possibility.

So previously, Filter would end up saying "S extends object | null, eh? Could that extend [infer Head, ...infer Tail]? Definitely not!"

So we'd always jump into the negative case and end up with []. That satisfies basically every array type, but not every tuple type. Try it out in the 5.3.3 playground.

type Filter<Arr, ToOmit> = Arr extends [infer Head, ...infer Tail]
  ? Head extends ToOmit
    ? Filter<Tail, ToOmit>
    : [Head, ...Filter<Tail, ToOmit>]
  : [];

function f<S extends object | null>(x: Filter<S, null>) {
  const a: object[] = x;
  const b: number[] = x; // WHAT?
  const c: string[] = x; // WHAT?
  
  const d: [] = x; // Okay, that explains it.
  const e: [object] = x; // Yup, this one gets an error - makes sense now.
}

As you can see from the examples, that's not correct though! An object doesn't extend an array, but that doesn't necessarily mean S itself couldn't be an array. In 5.4+, these all error as expected.


inlining it is fixing the error

Not sure exactly what you mean, but it might be related to a tradeoff we consciously made. The new behavior only kicks in on higher-order types (i.e. type parameters). In other words, if you pass in object | null, we don't say "well maybe that object is actually an array type" and try all the branches.

from typescript.

Alexsey avatar Alexsey commented on April 27, 2024

@DanielRosenwasser, I'm terribly sorry. I made a typo when simplifying the initial code. Of course, object | null is never an arrayβ€”it should have been (object | null)[], and this one is working just fine!

I will take another try to simplify the initial code to a short snippet tomorrow. Thank you for your time, I truly appreciate your effort in taking a look at the issue!

from typescript.

typescript-bot avatar typescript-bot commented on April 27, 2024

This issue has been marked as "Working as Intended" and has seen no recent activity. It has been automatically closed for house-keeping purposes.

from typescript.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.