-
Notifications
You must be signed in to change notification settings - Fork 12.8k
[true, false] extends [false, false] sometimes it's true(when use infer,happen with a probability ), and sometime it's false(in most cases)??? #51090
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
It's a bit of an aside, but in newer versions of TypeScript you can actually capture and constrain an export type Sub2<A extends number, B extends number> =
[IsNegative<A>, IsNegative<B>] extends (infer R extends [false, false])
? ['Why?', R]
: 'Expected'
export type Sub3<A extends number, B extends number> =
[IsNegative<A>, IsNegative<B>][number] extends (infer R extends false)
? ['Why?', R]
: 'Expected' |
@DanielRosenwasser Yes |
@DanielRosenwasser A bit of an aside, but I've always wondered why TypeScript can't capture the constraint of an infer type parameter on its own 😅 would you mind shedding some light on this one? |
I might be missing something. Let me try to answer both of these points
I think that's what my example is doing - your first two examples had to do this by using two nested conditionals. Your second two examples seemed to drop the second tuple element - I assumed this was so that you could simplify with one conditional. I'm just mentioning that you can still capture the type with an
Disclaimer: you might be more familiar with the checker than me at this point. Technically we propagate the implicit constraint through an internal kind of type called "substitution types", which you can sort of think of as intersection (and in a sense, you can think of intersections as an alternative model of constraining type parameters). Substitution types would need some sort of new notation, or we would have to just replace them with intersection types. Here's one reason I think we don't keep them around: If we captured these types, you'd start seeing intersections you didn't really write which could be an odd experience; plus, when you instantiate a type sufficiently for a conditional type to resolve, you don't need the substitution type anymore - so it's really better to substitute the type as it was written instead of writing an intersection which won't always be simplified away. I think there's another element of keeping the design space open. Right now substitution types are mostly an implementation detail. The way that they're used is to validate that a type variable satisfies the requirements in how they're used (e.g. with Others might have more thoughts here, and I'd welcome corrections or other thoughts. |
I spoke with @ahejlsberg and I totally confused myself - I was thinking about substitution types from the left side of the Yes, we could possibly do something like that. It might be worth opening a separate issue for it. |
Here's what I want to do:( export type Sub<A extends number, B extends number> = [IsNegative<A>, IsNegative<B>] extends (infer R extends [boolean, boolean])
? R extends [true, true]
? '--'
: R extends [true, false]
? '-+'
: R extends [false, true]
? '+-'
: '++'
: never;
type T1 = Sub<-15, 90>; // '-+' work well
export type Sub1<A extends number, B extends number> = [IsNegative<A>, IsNegative<B>] extends (infer R extends [boolean, boolean])
? R extends [false, false]
? '++'
: R extends [true, false]
? '-+'
: R extends [false, true]
? '+-'
: '--'
: never;
type T2 = Sub1<-15, 90>; // '++' Oh no!, expected to '-+' Just switch the order of the judgment statements, and the result actually changes. Now I'm almost certain there's some kind of problem here. |
This is working as intended. The issue is that the A better way to write the type is: export type StartsWith<S extends string, SearchString extends string> =
string extends S ? boolean :
S extends `${SearchString}${string}` ? true :
false; This appropriately accounts for arbitrary strings by answering |
@ahejlsberg Good explanation and even helped me find bugs in other places😉, thank you |
@ahejlsberg Sorry to ask you for help again, a similar problem arises, but this time it looks even weirder! 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>; // Infinity I guess the parentheses make it a complex type? |
Thank God, I found it works well in this way(with export type AddWithInfer<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 (infer _ extends [R[0], R[3]]) // Note: with infer
? PositiveInfinity
: 'failed'
: never;
type AddTest2 = AddWithInfer<PositiveInfinity, PositiveInfinity>; // Infinity, works too! |
Bug Report
🔎 Search Terms
extends when infer, lazy evaluation
🕗 Version & Regression Information
⏯ Playground Link
Playground link with relevant code
more detail can also see: stackOverflow
💻 Code
🙁 Actual behavior
type T1 = ["Why?", [true, false]]'
🙂 Expected behavior
type T1 = 'Expected'
(Same as T2)The text was updated successfully, but these errors were encountered: