-
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
Intersection type of two array types does not widen in callback #11961
Comments
This happens because we just merge the signatures of But in general it's really unclear how So overall... probably a bad idea to have the type |
That's really weird behaviour, but useful to know. This also highlights another difference in using interfaces vs type definitions: interface foo1 {
val: { id: number }[];
};
interface foo2 extends foo1 {
val: { id: number, value: string }[];
};
var x: foo2;
x.val.forEach(item => {
item.value; // Now okay
}); I suggest that |
@RyanCavanaugh, I'm not sure it follows from that that Lets get rid of function f<X, Y>(x: X, y: Y) {
type Box<T> = {
get(): T;
set(v: T): void;
};
let xAndY: X & Y, xOrY: X | Y;
let xBox: Box<X>, yBox: Box<Y>;
let xAndYBox: Box<X & Y>;
let xOrYBox: Box<X | Y>;
let xBoxAndYBox: Box<X> & Box<Y>;
xBox.set(x);
xBox.set(y); // Error: Y is not X
xBox.set(xAndY);
xBox.set(xOrY); // Error: X | Y is not X
x = xBox.get();
y = xBox.get(); // Error: X is not Y
xAndY = xBox.get(); // Error: X is not X & Y
xOrY = xBox.get();
yBox.set(x); // Error: X is not Y
yBox.set(y);
yBox.set(xAndY);
yBox.set(xOrY); // Error: X | Y is not X
x = yBox.get(); // Error: Y is not X
y = yBox.get();
xAndY = yBox.get(); // Error: Y is not X & Y
xOrY = yBox.get();
xAndYBox.set(x); // Error: X is not X & Y
xAndYBox.set(y); // Error: Y is not X & Y
xAndYBox.set(xAndY);
xAndYBox.set(xOrY); // Error: X | Y is not X & Y
x = xAndYBox.get();
y = xAndYBox.get();
xAndY = xAndYBox.get();
xOrY = xAndYBox.get();
xBoxAndYBox.set(x); // OK? but `yBox.set(x)` is an error.
xBoxAndYBox.set(y); // OK? but `xBox.set(y)` is an error.
xBoxAndYBox.set(xAndY); // OK
xBoxAndYBox.set(xOrY); // Error
x = xBoxAndYBox.get(); // Ok? but `x = yBox.get()` is an error
y = xBoxAndYBox.get(); // Error, but & is asymetrical!
xAndY = xBoxAndYBox.get(); // Error
xOrY = xBoxAndYBox.get(); // OK
// In summary: . = ok, ! = error
// set get
// .!.! .!!. : Box<X>
// !..! !.!. : Box<Y>
// !!.! .... : Box<X & Y>
// .... !!!. : Box<X | Y>
// ...! .!!. : Box<X> & Box<Y>
// A is a B iff A has all .'s B has.
xBox = xBoxAndYBox; // OK
yBox = xBoxAndYBox; // OK? but y = yBox.get() is ok, while `y = xBoxAndYBox.get()` is an error
xAndYBox = xBoxAndYBox; // Error
yOrYBox = xBoxAndYBox; // Error
xBoxAndYBox = xBox; // Error
xBoxAndYBox = yBox; // Error
xBoxAndYBox = xAndYBox; // OK? but `xBoxAndYBox.set(x)` is ok, while `x = xAndYBox.set(x)` is an error
xBoxAndYBox = yOrYBox; // Error
} Clearly the type algebra is wonky here! But by combining acceptable uses of Back to arrays, the typesafe behavior logically should be: const catdogs: Cat[] & Dog[] = ???;
const dogs: Dog[] = catdogs;
const cats: Cat[] = catdogs;
dogs.push(new Dog());
cats.push(new Cat());
// catdogs now has a Dog and a Cat!
let dog: Dog, catdog: Cat & Dog, hysteria: Cat | Dog;
catdogs.push(dog); // Error: Dog is not a Dog & Cat, otherwise cats has a Dog.
catdogs.push(catdog); // OK: dogs only has Dogs, cats only has Cats
dog = catdogs.pop(); // Error: Cat | Dog is not a Dog, otherwise dog could get a Cat from cats.
catdog = catdogs.pop(); // Error: Cat | Dog is not a Cat & Dog, it could come from cats or dogs.
hysteria = catdogs.pop(); // OK: Could be from cats or dogs, but common is still accessible. |
For the curious, this is not an issue specific to overload sets, simple fields have the same issue: interface Cat { meow(): void }
interface Dog { bark(): void }
interface Owner<Pet> { pet: Pet; }
declare let cat: Cat, dog: Dog, catdog: Cat & Dog, pet: Cat | Dog;
declare let petOwner: Owner<Cat | Dog>;
declare let catOwner: Owner<Cat>;
declare let dogOwner: Owner<Dog>;
declare let strangeOwner: Owner<Cat> & Owner<Dog>;
catOwner = strangeOwner;
dogOwner = strangeOwner;
if (Math.random() < 0.5)
catOwner.pet = cat;
else
dogOwner.pet = dog;
strangeOwner.pet = pet; // Error, otherwise catOwner could get a Dog, or dogOwner could get a cat
strangeOwner.pet = catdog; // OK
pet = strangeOwner.pet; // OK: Cat | Dog
catdog = strangeOwner.pet; // OK? but Cat | Dog is not Cat & Dog!
catdog.bark(); // boom if strangeOwner is a catOwner
catdog.meow(); // boom if strangeOwner is a dogOwner |
Hi, Guys,
Something a bit strange here:
TypeScript Version: 2.0.6
Code
Expected behavior:
No error when property
value
is referenced in the body of theforEach
.Actual behavior:
The compiler reports an error.
The text was updated successfully, but these errors were encountered: