-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Control flow not working as expected outside of if block #10706
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
@iDev0urer your original example compiles fine for me with TS2.0.2. However your repro is not self-contained since interface IRecord { someProp }
function test(records: IRecord | IRecord[]) {
if (!Array.isArray(records)) {
records = Array(records); // or records = [records]
}
return records.length;
} ...and that works as expected. However if you declared |
Here's an equivalent example to the OP, showing how narrowing behaviour depends on whether the types in the union are unrelated or supertype/subtype. But I don't know why the compiler acts differently at (1) and (2) below, and whether the compiler could do better here, since it seems inconsistent. Someone from the team, probably @ahejlsberg, would be best able to answer that. class Animal {species}
class Cat extends Animal {meow}
class Dog extends Animal {woof}
declare function isDog(x): x is Dog;
function test1(x: Cat | Dog) {
if (!isDog(x)) {
x // x is Cat
x = new Dog();
x // x is Dog <===== (1)
}
return x.woof; // OK
}
function test2(x: Animal | Dog) {
if (!isDog(x)) {
x // x is Animal
x = new Dog();
x // x is Animal|Dog --- but why not Dog here? <===== (2)
}
return x.woof; // ERROR: property 'woof' does not exist on type 'Animal | Dog'
} |
@yortus do you think it might make a difference that my examples were done inside of a class rather than as regular functions? |
@iDev0urer I suspect the issue in the OP is that you've declared @yortus The same is true at (2) in your example. A |
@ahejlsberg yeah that was the intention as I had already pointed out the supertype/subtype problem in the OP. I was more interested in why the compiler can't make the |
@iDev0urer the narrowing behaviour is the same in both class methods and regular functions. |
@ahejlsberg actually my export interface IRecord {
Id?: string;
type?: string;
attributes?: IRecordAttributes;
extIdField?: string;
} but I have a feeling that I am running into the issue you are describing since all of the params in |
@ahejlsberg not really. I'd rather describe this situation as a limitation of structural typing, rather than a problem with the source code, which is often perfectly acceptable runtime-valid code complete with valid type guards. Perhaps it's not possible, but it would be great for this to be given further attention to see if there is some way to get narrowing to work the way the programmer expects (and to match runtime behaviour) for unions of unintentionally related types like in this issue. Or at least an FAQ entry to point people to the indicators and workarounds for this situation. |
@yortus thanks for the help! |
TypeScript Version: 2.0.2 and nightly (2.0.0-dev.20160904)
Code
Expected behavior:
Length should return the length of the array since I casted
records
to an array if it comes in as a singleIRecord
.Actual behavior:
I get the error:
Property length does not exist on type 'IRecord | Record[]'
.Workaround:
Not ideal
The text was updated successfully, but these errors were encountered: