-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Generic type changes depending if its parameter type is wrapped or not. #56675
Comments
The divergence in expectation actually starts one line earlier: type IfOrWithIsString<T> = IfOr<false, IsString<T>>;
// ^?
// type IfOrWithIsString = true, wat?
// Which branch of IfOr is being hit? Let's label them
type IfOrSimpler1<P, Q> = IfIdentical<P, false, IfIdentical<Q, false, "Both False", "Q is not false">, "P is not false">;
type IfOrSimpler1WithIfString<T> = IfOrSimpler1<false, IsString<T>>;
// ^?
// "Q is not false"
// It's hitting IsIdentical<IsString<T>, false>. Why?
// Simplify IfOr to just the P case
type IfOrSimpler2<Q> = IfIdentical<Q, false, "Both False", "Q is not false">;
type IfOrSimpler2WithIsString<T> = IfOrSimpler2<IsString<T>>;
// ^?
// ^? "Q is not false"
// IfIdentical<IsString<T>, false> hits the 'false' condition
type IfIdenticalSimpler<X, Y> =
(<T>() => T extends X ? 1 : 2) extends
(<T>() => T extends Y ? 1 : 2) ? "Seems to extend" : "Seems not to";
type IfSimple3<Q> = IfIdenticalSimpler<Q, false>;
type IfSimple3WithIsString<T> = IfSimple3<IsString<T>>;
// ^?
// ^? "Seems not to" You could instead write this, which works more reliably: type IfOr<P, Q, True = true, False = false> = IfIdentical<P, true, True, IfIdentical<Q, true, True, False>>; or use a more usual definition of type IfIdentical<X, Y, A = true, B = false> = X extends Y ? Y extends X ? A : B : B; TL;DR you can't take falsy results of conditional type evaluations as definitive. Restrictive instantiations can and do occur; for most conditional types, they should be of the form |
cc @weswigham please check my work on the above π |
I'm not sure I follow this:
Are you saying that if my conditional type
I have seen this terminology ("restrictive instantiation") in the codebase, but I don't fully understand it. Given the following code type Foo<T> = β¦
type Bar<U extends string> = β¦
type Qux<V extends "v1" | "v2"> = β¦ would the most restrictive and permissive instantiations be as follows:
More generally:
Also, when does a restrictive instantiation occur, and how are restrictive/permissive instantiations used to determine whether to evaluate conditional types? I tried to read the code but got confused. It seems to me that if you have type Foo<A extends A'> = A extends B ? X : Y where type Bar<A extends A'> = A extends B ? X : Y where
Is this saying that most conditional types should be written in the form type Config<T extends "dev" | "stg" | "prod"> = T extends "prod" ? "safe config" : "allow experiments" we should actually write type Config<T extends "dev" | "stg" | "prod"> = T extends "prod" ? "safe config" : ("allow experiments" | "safe config") otherwise |
π Search Terms
generic type bug
π Version & Regression Information
latest version as of today.
β― Playground Link
https://www.typescriptlang.org/play?#code/C4TwDgpgBAkgZjAJhAdsAlgYwIYBsA8AGgDRQCapAglALxTABOArhKQEK1Rx4DOEAfLQBQUUVAAU+ACr9xASlqCpUCAA9gqRDyiEoAfigBGKAC4oAJgVqNKLSLGSZ8xVGXXN2svqOmLCg9RmbADcQmGgkLBwAPIM+AAKpACKpFLM0HSMLKQAYrwZXPmCdPBIqBg4BImFuHykpchoWHj4KTV1UHm1rK7p-Kl9oUIR0AD6xiUxcVk9M-zBUAD0i-Tpw+Bj5pzwsfgzpNzd80srM+uRowDM21P4hx1zC8urLOdjACw3u-c9P8fPPzCb1gPAAyox0CgAObSYpRMpNSrSUg8CHQ+bhDZQUYAVm2YLRMJQTAAtgAjCAMDFAkbYqQQVETKLffL1AkMSFE0kUqn-FY-AB0UEwAHsGAwIJhgABCIHAnYMADq6GAAAt4OCOdDYV84j82ZrObDqbTRvTUVtJrFlWqNYT8MTyZS+S8IAYyUwobKgA
π» Code
π Actual behavior
types
_Test1
and_Test2
are different even though they are calculated the same way.π Expected behavior
types
_Test1
and_Test2
should be the same.Additional information about the issue
Not sure if it's the best minimal reproduction, because it's difficult to pinpoint the actual problem.
The example is based on the a type suggested by Matt McCutchen here, but I can't tell if the issue is unique to that type or if it happens in other scenarios.
Based on that
IsEqual
type I created a "if or" typeIfOr<P, Q>
and a "is string" typeIsString<T>
.The difference between
_Test1
and_Test2
is that the first passesIsString<number>
result (false
) directly toIfOr
, while the second passesnumber
to an intermediary type, sort of wrapping theIsString
.The text was updated successfully, but these errors were encountered: