Comments (5)
I think this is working as intended in TypeScript 5.4:
In a conditional type like
T extends Foo ? TrueBranch : FalseBranch
, whereT
is generic, the type system would look at the constraint ofT
, substitute it in forT
itself, and decide on either the true or false branch.But this behavior was inaccurate because it was overly-eager.
Even if the constraint ofT
isn't assignable toFoo
, 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 thatT
never or always extendsFoo.
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
- changing the constraint of
Arr
inAssignResult
tounknown[]
instead ofobject[]
(playground link), - adding an
object
constraint toHead
and anobject[]
constraint toTail
(playground link).
from typescript.
@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.
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 constraintC
, the constraint ofT extends X ? A : B
is
A
whenC
is known to always extendX
,B
whenC
is known to never extendX
, orA | B
whenC
possibly extendsX
.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.
@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.
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)
- Inconsistent Behavior with Equality Check Using `Pick<Readonly<T>, K>` in Mapped Type HOT 2
- Improve Omit field type check HOT 1
- Class parameter property with initializer before required property emits non-nullable parameter for declaration emit
- TypeScript emits incorrect type for setters in JSDoc
- Function returning `never` in branch doesn't narrow type if it's not explicitly annotated HOT 3
- Type Intersection incorrect for optional parameters in functions with `exactOptionalPropertyTypes: true`. `undefined` type is flip based off Intersection types
- Inconsistent tsc behavior on TS 5.4 HOT 3
- VSCode TypeScript extension writes tracing to the wrong directory on Mac OS HOT 1
- computed property name in an interface error when using `[]` already HOT 3
- Proposal: Type-side `instanceof` keyword and functionality HOT 8
- tsserver.js CPU/Memory Spike HOT 3
- [NewErrors] 5.5.0-dev.20240414 vs 5.4.5 HOT 30
- [ServerErrors][TypeScript] 5.5.0-dev.20240414 HOT 12
- [ServerErrors][JavaScript] 5.5.0-dev.20240414 HOT 12
- TypeScript Fails to Infer Specific Generic Types as Expected HOT 2
- [BUG]: TypeScript throws errors when adding [key: string]: string if other properties are not of type string HOT 2
- Support importing types from a `.mts` file from within a CommonJS file. HOT 7
- `(nullable?.methodOrProperty ?? defaultValue) === defaultValue` should be a type guard HOT 11
- Type refinement on properties no longer refines the whole structure HOT 3
- Import intellisense not working HOT 14
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google β€οΈ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from typescript.