Skip to content
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

This condition will always return 'false' since the types have no overlap. #25642

Closed
Busyrev opened this issue Jul 13, 2018 · 40 comments
Closed
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@Busyrev
Copy link
Contributor

Busyrev commented Jul 13, 2018

TypeScript Version: 3.0.0-dev.20180712
Error: This condition will always return 'false' since the types 'A' and 'I' have no overlap.
TypeScript Version: 2.9.2
Error: Operator '===' cannot be applied to types 'A' and 'I'

Search Terms:
Operator '===' cannot be applied to types
This condition will always return 'false' since the types have no overlap.
Code

class A { 
  private field: string;
}
interface I { 
  pub: string;
}
class B extends A implements I { 
  pub: string;
}
let b = new B();
let a: A = b
let i: I = b;
if (a === i) { // on TS 2.9.2: Operator '===' cannot be applied to types 'A' and 'I'
// on TS 3.0.0-dev.20180712:  This condition will always return 'false' since the types 'A' and 'I' have no overlap.
  console.log('qq');
}

Expected behavior:
no error
Actual behavior:
error
Playground Link: link

Related Issues: no

This case found from real project. Class and interface may be implemented and extended by another class, and in this case this error is pointless.

@Busyrev Busyrev changed the title This condition will always return 'false' since the types 'A' and 'I' have no overlap. This condition will always return 'false' since the types have no overlap. Jul 13, 2018
@Busyrev
Copy link
Contributor Author

Busyrev commented Jul 13, 2018

Another case for classes without interfaces

class A { 
  private field: string;
}
class B { 
  public anotherField: string;
}
class C extends A implements B { 
  public anotherField: string;
}
let c = new C();
let a: A = c
let b: B = c;
if (a === b) { // on TS 2.9.2: Operator '===' cannot be applied to types 'A' and 'B'
// on TS 3.0.0-dev.20180712:  This condition will always return 'false' since the types 'A' and 'B' have no overlap.
  console.log('qq');
}

Playground

@mhegazy
Copy link
Contributor

mhegazy commented Jul 13, 2018

//cc @RyanCavanaugh and @DanielRosenwasser for error message details.

@mhegazy mhegazy added the Working as Intended The behavior described is the intended behavior; this is not a bug label Jul 13, 2018
@mhegazy
Copy link
Contributor

mhegazy commented Jul 13, 2018

The rule for the type assertion is that the compiler can verify that either of the two types is comparable to the other. In simpler terms, can you assign one to the other either upcast or downcast. if neither is true, then it is likely an error.. e.g. 3 === "string".

@AnyhowStep
Copy link
Contributor

On one hand, it looks like a code smell to compare types like this.

On the other, if such a check passes, I would expect it to behave like some sort of type guard, where both a and i are now of type A & I or something to that effect.

@Busyrev
Copy link
Contributor Author

Busyrev commented Jul 13, 2018

In simple cases yes, looks good. But in some cases it alerts on absolutely correct code. Found this case in complicated code that works with heirechical system using abstract interfaces. All parent - child links is interfaces, and nodes implemets this interfaces.
Think thak this error should be emitted if both arguments primitive or nominal types only. If type of one side is not nominal, its incorrect to prevent equality check.
To be more clear. I think that its design issue.
No error in С# but there is error comparing int and string

@RyanCavanaugh
Copy link
Member

The current rule covers a large class of errors with good precision; it will necessarily have some false positives but we have carefully considered this and are satisfied with the current balance of value vs "incorrect" errors.

@vovkasm
Copy link

vovkasm commented Jul 16, 2018

@RyanCavanaugh And how do you propose to correct these "incorrect" errors? What developers should do?

@Busyrev
Copy link
Contributor Author

Busyrev commented Jul 16, 2018

Oops!...I Did It Again

@AntiMoron
Copy link

image

How you guys solve this?

@AnyhowStep
Copy link
Contributor

AnyhowStep commented Dec 5, 2018

@AntiMoron
Shouldn't it be process.env.<someIdentifier> == 'development'?

[EDIT]

If env is also your identifier, then, process.env.env == 'development'

@AntiMoron
Copy link

@AnyhowStep Thank you...I redefined process.env as a string...
Seems to be my problem.

@AnyhowStep
Copy link
Contributor

AnyhowStep commented Dec 6, 2018

@AntiMoron How did you even get that to work? o.0

process.env is supposed to be an object, not a string.

https://nodejs.org/api/process.html#process_process_env


Are you using process outside of node? Are you somehow overwriting process.env in code elsewhere?

@vikrampariharcodereadr
Copy link

image

How you guys solve this?

use String(process.env) == 'development'

Typecasting :)

@thinwire
Copy link

How about giving us the ability to silence an error - in my case I have a build script that generates code that contains constants based on the target platform (browser, android, ios...), and in this case if(BUILDTYPE === 'ANDROID') generates this error when the condition is false.

@RyanCavanaugh
Copy link
Member

How about giving us the ability to silence an error

// @ts-ignore

@thinwire
Copy link

Derp. Seems my google-fu failed me again. Thank you, Ryan.

@Drageaux
Copy link

image

How you guys solve this?

If you're using node, it's process.env.NODE_ENV. It's a string.

image
How you guys solve this?

use String(process.env) == 'development'

Typecasting :)

process.env is an interface ProcessEnv with [key: string]: string | undefined property. Why you're typecasting something totally unrelated from a string to be a string is beyond me.

please use process.env.<someIdentifier>

@tenedor
Copy link

tenedor commented Aug 18, 2019

Here's another variant of this problem, in case it's useful. I have code where T and U may be the same type or different types.

const x = <T extends SomeType, U extends SomeType> (t: T, u: U) => {
    if (t === u) {
        ...
    } else {
        ...
    }
}

@Aloso
Copy link

Aloso commented Sep 25, 2019

Here's another simplified example:

import { Foo, Bar } from './foo'

class Baz implements Foo, Bar {
    foo() {}
    bar() {}
}

const baz = new Baz()
const foo: Foo = baz
const bar: Bar = baz

if (foo === bar) { // ERROR!
}

foo.ts:

export interface Foo {
    foo(): void
}

export interface Bar {
    bar(): void
}

The comparison foo === bar causes the error "This condition will always return 'false' since the types 'Foo' and 'Bar' have no overlap."

Surprisingly, this error only appears if the interfaces are in a different module.

@snarfblam
Copy link

snarfblam commented Dec 8, 2019

What irks me here is that the error message implies that the code is provably incorrect ("This condition will always return 'false'"). It is clearly demonstrated that such a comparison may be perfectly valid.

The premise of the error is not factual, it's heuristic, and that should be reflected in the error message. "This condition may always return 'false' since the declared types have no overlap."

It might sound like a nitpick, but the phrasing cues the user on how to resolve the issue. It should reduce confusion instead of creating confusion.

@niezw
Copy link

niezw commented Jan 11, 2020

image

How you guys solve this?

import _ from 'lodash';
if(_.isEqual(process.env,'development')){
than .....
}

In order to avoid reporting mistakes, I can only do this for the time being.

@snarfblam
Copy link

@niezw A correct answer was already provided above. All you're doing is misusing a library function because it happens to circumvent the type system and suppress an error message, when in fact the error message is correctly identifying a problem with the program logic.

process.env is not a string. It is an object. If for some strange reason it ever were a string, the correct solution would be to fix the type declaration or, as a bandaid, use //@ts-ignore or perform a cast: if(process.env as unknown as string === 'development') {.... But due consideration should be given to the fact that TS is reporting process.env as type ProcessEnv and not string.

@prog-24
Copy link

prog-24 commented Jan 27, 2020

Cast whatever value to any like this

const a:any = problematicvalue;

You can then compare as normal

@snarfblam
Copy link

@prog-24 The problem isn't that we don't know how to work around the error. It's an issue of whether the error is meaningful and instructive. Blindly casting is asking for trouble. In the above case of process.env, the error message is correctly identifying a problem. However, a more accurate error message would help us deduce whether there is a flaw in our logic or if it is a false positive that we should work around via a cast to any or unknown

@prog-24
Copy link

prog-24 commented Jan 27, 2020

@snarfblam I think it is one of those compiler things that are really annoying but somehow necessary evil. I had a similar issues that caused me to produce this work around. There are times when yes you can make the necessary changes but if you are sure that it would not cause any issues for instance a simple string to struct string comparison as was my case then you can safely make the adjustments.

I think the error message is clear but unhelpful which I think is what you are pointing out.

@snarfblam
Copy link

Well, no. I think it is helpful but unclear. As I said above, the error is heuristic (the heuristic being that comparing two values whose declared types have no overlap is moderately indicative of a potentially hard-to-diagnose programmer mistake—very helpful), and it could be more clearly stated along the lines of, "This condition may always return 'false' since the declared types have no overlap."

Currently the error misstates the problem, making it more difficult to understand the root cause. Typescript errors can be confusing to parse (it's not that unusual to get a single error that is several lines of deeply nested symbol soup). If the error message isn't clear and accurate, it can be extremely difficult to work out. I'm imagining a scenario where a programmer develops a knee-jerk habit of casting to any whenever this error message pops up because it's phrasing doesn't make sense (or more to the point, it states a falsehood), negating the genuine utility of the error.

@kevincox
Copy link

This is a very poorly worded "error". For example the following code: "1" == 1 triggered the error. While the error claims "This condition will always return 'false'" it in fact evaluates to true. This is incredibly misleading.

Playground Link

@BertCatsburg
Copy link

BertCatsburg commented Feb 24, 2020

I got the same error-message with the following code:

let x = null;
if ( some-condition ) {
  x = 0;
} else {
  x = 1;
}
if ( x === 0 ) {
  // Do something
}

On the x===0 condition I got the error, which was strange in my view because I was sure the x is either 0 or 1.
But the culprit was the initialization to null.
Changed that to:

let x;

And it solved the problem.
If it's a casting problem then its still strange because the program has x casted to an int the moment of the first condition. (x = 0 or x = 1)

@victor-zy
Copy link

typeof a === b,试一下咯

@douglasmatosdev
Copy link

I solved this situation
image

using String()
image

@snarfblam
Copy link

I solved this situation

This is just another example of someone introducing a bug into their program to try to fix this error message. There is generally no good reason to use String, and now you have a function that is supposed to return a boolean that is instead returning String('true') or String('false'), both of which are objects, thus truthy.

@RyanCavanaugh Is there any chance we can re-open this issue to consider re-wording the error message? I think it should convey:

  • That the comparison is probably erroneous
  • That the comparison may also merely require a cast to help unconfuse the compiler.

Perhaps something along the lines of:

This condition may always return 'false' since the declared types have no overlap. Consider casting if the compared values are known to be of compatible types.

@Aloso
Copy link

Aloso commented Oct 9, 2020

@NeliHarbuzava Your code is wrong, it currently always returns true, even if imageFormat is "png" or "jpg". The correct code would be

if (typeof imageFormat !== 'string' || (imageFormat !== 'png' && imageFormat !== 'jpg')) {
    //                                                        ^^ problem solved
    console.warn('Invalid "imageFormat" property. Expected one of: "png", "jpg".');
}

@snarfblam
Copy link

This condition will always return 'true' since the types '"png"' and '"jpg"' have no overlap

This demonstrates the actual utility of the error message. imageFormat !== 'png' || imageFormat !== 'jpg' will always return true, because it will always be inequal to at least one of the two ('jpg' or 'png'). It would be nice if TS could distinguish between genuine "will always return true" versus heuristic "may always return true", but I'd settle for any improvement to how the error is reported.

@OliverEvans96
Copy link

@tenedor - thanks for the succinct MWE - I'm in the same situation.

const x = <T extends SomeType, U extends SomeType> (t: T, u: U) => {
    if (t === u) {
        ...
    } else {
        ...
    }
}

One simple workaround is to set u: T | U, although that may have unintended consequences I'm not aware of.

@galenwinsor
Copy link

As far as I can tell, this is still an issue. As @kevincox points out, the simple expression '1' == 1 throws this error, which is extremely misleading. @RyanCavanaugh can you clarify why the wording of the error is useful? (I.e., "will always return false" as opposed to "may return false"). Thank you!

@RyanCavanaugh
Copy link
Member

See #26592

@galenwinsor
Copy link

@RyanCavanaugh I guess I don't see how that answers the question. I can see the use of pointing out the potential bug-iness of using ==, but the language of the error message is misleading and also incorrect, isn't it?

@RyanCavanaugh
Copy link
Member

Yes, and the linked issue says that we'd love a PR to change the language of that error message.

@galenwinsor
Copy link

Ahhh okay, sorry for the confusion. Thank you!

@galenwinsor
Copy link

@RyanCavanaugh could you point me to where this check happens in the code?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests