-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Type 'X<false>' is not assignable to type 'X<boolean>'. #48150
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
I believe this is a correct error which was not handled properly in old versions. |
@whzx5byb Suppose this is correct behavior, can you suggest how to continue to use the sketched concept? The interface in fact represents the core concept of Knockout.js library. To set value to If |
A way to make your example work: function observable<T>(value: T extends infer U ? U: never): Observable<T>
{
return undefined as any; // the implementation is not important
}
const x: Observable<boolean> = observable(false); // no error |
@whzx5byb Just perfect, thanks! |
@tomhanax perhaps this should be referred to the knockout project so that the necessary changes can be made there. |
|
@tomhanax I don't care, you could post it yourself. |
For the record, the reason for the type error: const oFalse: Observable<false> = observable(false);
const oBool: Observable<boolean> = oFalse;
oBool(true);
// now Observable<false> has value true |
The answer in #48150 (comment) is not perfect, as it will produce const bad = observable(false);
// ^ unknown, which should be Observable<boolean> The issue here is the inference Here is an alternative workaround which will produce the same behavior without a type annotation, but a bit more verbose. I have to introduce two type parameter. declare function observable<T, U>(value: T extends infer R ? R : U): unknown extends T ? Observable<U> : Observable<T>; I'm not sure if there is a better solution. If you find some please let me know. |
Cross-posting here, just for the record. Make sure that also both these lines are valid with any new proposal. const x = observable<false | undefined>(false);
const y = observable(false); |
@tomhanax I think this should be fixed in TypeScript. The "fixes" for the Knockout code don't make much sense. Consider that this code has the correct result:
But this one errors:
Also that this works:
But not this:
|
@mbest, the "most updated" playground (see also knockout/tko#168 (comment)) contains many examples and seems OK, of course anybody can check and correct if needed, I am no expert in this. I am not saying anything about what "should" be fixed, I am no expert at all. Simply trying to help to solve existing issue, because now we are forced to remain on TS 4.5 until this is solved. |
@tomhanax why was the issue closed? Isn't this a valid bug in Typescript? const w = observable(false); // Typescript identifies 'w' as Observable<boolean>
const x: Observable<boolean> = observable(false); // Type being Observable<false> now Typescript seems to change it's mind of what type |
@maskmaster to be honest I am not sure. I only know that what worked previously does not work now. As @whzx5byb pointed out, the whole thing may be "reversed" - it was working because of less perfect previous TS version that allowed this and now it is not working because simply it should not work in the first place. I am not good at type theory at all, so anybody else have to say funded words. |
This should be related to the "type widening" mechiasm. For more details: #10676, #11126, #24310. And here declare function fn<T>(x: T): T[];
const b1 = fn(true); // T is boolean (widening)
const n1 = fn(1); // T is number (widening)
const b2: boolean[] = fn(true); // T is true (non-widening)
const n2: number[] = fn(1); // T is number (widening)
const b3: (true | false)[] = fn(true); // T is true (non-widening)
const n3: (1 | 0)[] = fn(1); // T is 1 (non-widening) |
The following is valid const w = observable(false);
const v: Observable<boolean> = w; However, this is not valid const v: Observable<boolean> = observable(false); To me, it makes no sense. |
@maskmaster any type with a contravariant position has this problem |
Bisected to #47341. @ahejlsberg this seems like an unintended consequence? |
Before 4.6 we'd measure variance of declare let xb: Observable<boolean>;
declare let xf: Observable<false>;
xb = xf; // Error, but wasn't before 4.6
xf = xb; // Error The type parameter |
@ahejlsberg I totally agree with the correctness of your example and that the new behavior as you describe is correct. I think we are talking about two different things. const w = observable(false); According to Typescript 4.6 'w' is an const w: Observable<boolean> = observable(false); According to you, what is the correct type of 'w' in |
@maskmaster Hmm, yeah, that is a known design limitation, and I agree it's unfortunate. See my comment here. The workaround is to write: const w: Observable<boolean> = observable(false as boolean); |
@maskmaster Regarding the type of const w = observable(false); it is producing the intended |
Bluntly said this is absolutely silly. In year 2022 you can not possibly tell this to the "average programmer". |
This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes. |
Yeah, as I said, it's unfortunate. I'll open an issue on this specifically and we'll see if we can do better. |
Thank you @ahejlsberg for the explanation. It makes sense if it a design limitation. I'll try to wrap my head around this later on so I understand exaxtly what happens. |
Bug Report
Since 4.6 TS is complaining about that
false
is not assignable toboolean
, when it is "wrapped by interface".🔎 Search Terms
4.6 type
🕗 Version & Regression Information
⏯ Playground Link
Playground Link
💻 Code
🙁 Actual behavior
🙂 Expected behavior
No compiler problem as in previous versions.
The text was updated successfully, but these errors were encountered: