-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
Unexpected error: TS2365: Operator '!==' cannot be applied to types 'false' and 'true' #12794
Comments
Indeed, there seems to be a bug in 2.1.4. (maybe a side effect of the new type inference). interface A {
get<T>(key: string, defaultValue: T): T;
}
let a: A;
var x = a.get("key", false); // Ok, x is boolean
var y = a.get("key", false) == true; // Error TS2365 Operator '==' cannot be applied to types 'false' and 'true' |
@ahejlsberg thoughts on how to properly type this? |
you can define interface A {
get(key: string, defaultValue: boolean): boolean;
get<T>(key: string, defaultValue: T): T;
} |
Probably worth providing overloads on all the primitives in that case |
I'm slightly confused - do you mean that all generic methods in all codebase like this will need to have loads of overloads (one for each primitive type)? Surely it's incorrect to assume that if a literal value is passed as a generic arg that the return type is that specific value rather than its type? I can't imagine this would ever be the expected thing? |
I do not see why this is true. the error message in the OP is a valid one. |
I don't understand why this is the case. When someone defines a generic method like
The implementation of this method is in VS Code; I can persist a value and read it back. The default I pass is for if there is no stored value. If the stored value is |
the method declaration says it takes a type |
I can't currently test; but if I pass a I'm sure there are many advantages to treating |
@mhegazy This is a widely used pattern. public virtual T Get<T>(string name, T defaultValue) https://github.com/ninject/Ninject/blob/master/src/Ninject/NinjectSettings.cs public T Get<T>(string key, T defaultValue) get<T>(key: string, defaultValue: T): T; |
This was an intentional change in TS 2.1, please see https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#literal-types-are-inferred-by-default-for-const-variables-and-readonly-properties. not treating literal types as types causes other errors as well. i would recommend adding overloads for the primitive values to ensure you are returning the base type instead of the literal type as suggested above. |
Perhaps I'm missing something here too, but shouldn't this compile as it used to pre 2.1? function getLengthAsStr(strs: string[]): string {
if(strs.length !== 111){
return "111"
}
if(strs.length !== 222){
return "222"
}
return "blah"
} results in:
There are a few things we can do to make it compile, such as asserting the type: function getLengthAsStr2(strs: string[]): string {
if(strs.length !== <number>111){
return "111"
}
if(strs.length !== <number>222){
return "222"
}
return "blah"
} or similarly: function getLengthAsStr3(strs: string[]): string {
if((<number>strs.length) !== 111){
return "111"
}
if((<number>strs.length) !== 222){
return "222"
}
return "blah"
} or weirdly, replacing a return statement with something else: function getLengthAsStr4(strs: string[]): string {
if(strs.length !== 111){
console.log("222")
}
if(strs.length !== 222){
console.log("222")
}
return "blah"
} |
function getLengthAsStr(strs: string[]): string {
if(strs.length !== 111){
return "111"
}
// `strs.length` here can not be any thing other than `111`
// this check is guaranteed to be true
if(strs.length !== 222){
return "222"
}
return "blah"
} |
Oh yeah, I see that, and cool that the compiler now catches that! :) In sanitising the internal code, I removed the mutation: function getLengthAsStr(strs: string[]): string {
if(strs.length !== 222){
return "not 222"
}
//strs.length == 222
strs.push("morestring")
//strs.length == 223
if(strs.length === 223){
return "223"
}
return "blah"
} which also fails to compile. |
Similarly, here's a snippet of from a lexer implementation: if (this.tip == "\"") {
// ...
let isEscaped = false;
// NB: `this._advance()` will cause `this.tip` to differ from above
for (this._advance(); this.tip != ""; this._advance()) { // TS2365
if (this.tip == "\\" && !isEscaped) {
isEscaped = true;
continue;
}
if (this.tip == "\"" && !isEscaped) break; // TS2365
// ...
}
// ...
} At both marked locations, buggy inference in tsc leads to spurious errors
In reality, |
@ghost while (this._advance() && this.tip != "") {
if (this.tip == "\\" && !isEscaped) {
isEscaped = true;
continue;
}
if (this.tip == "\"" && !isEscaped) break; // TS2365
} where _advance(): this is this & { tip: string } { ... } |
I also had this error, and after spending some time I figured out that this error is erroneously appearing when there's unreachable code. And sometimes even for reachable code, without an explanation. Example: class TrueFalse {
one: boolean = true;
two: boolean = false;
public test () {
if (this.one == false && this.two == false) {
console.log("that's true.")
}
else if (this.one == false) {
console.log("that's false.")
}
else if (this.one == false) { // unreacheable code, removing gets rid of the error
console.log("that's false.")
}
}
}
let t = new TrueFalse();
t.test();
|
Your second else if is actually a bug that the compiler is catching for you. It's dead code. The branch will never be taken because the previous else's if checks for the same condition |
@aluanhaddad I know, but the error message provided by the compiler is misleading. I also want to stress that sometimes this is happening even when there's no unreachable code, but I couldn't reproduce/isolate the scenario yet. |
@endel indeed, such as this: #12794 (comment) |
TypeScript Version: 2.1.4
This code in my VS Code extension has stopped compiling (without changes) recently.
It returns the error:
The full source is here and the build output here.
It seems like TypeScript has decided that the call to
get
can only return false, and therefore this comparison is bad; however I don't believe that's true - this method access stored state, so I can put some value in it and then reload my extension and it would receive the stored value.This may be the same as #12772, but the response is confusing to me:
This logic seems flawed - you can't assume something can never have a certain value because it might not be called; surely you have to assume it might have that value because you do not know if it will be called?
The text was updated successfully, but these errors were encountered: