You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Two types should only be compatible if their members are compatible - even if all these members are optional #12904
I wanted to see if I can update definitions a MongoDB filter object, which utilizes these expressions. Below is just a start, but I already see an issue when the code compiles when it should not. This issue possibly has similar underlying issue as #12769
TypeScript Version: 2.1.4
Code
typeMongoFilter<T>=$and<T>|$or<T>|propertyRule<T>;typelogical<T>=$and<T>|$or<T>;type$and<T>={$and: Array<logical<T>|propertyRule<T>>}type$or<T>={$or: Array<logical<T>|propertyRule<T>>}typepropertyRule<T>={[PinkeyofT]?: T[P]|comparison<T[P]>}typecomparison<V>=$eq<V>|$gt|$lt;type$eq<V>={$eq: V};type$gt={$gt: number};type$lt={$lt: number};// See if MongoFilter works:interfacePerson{id: number;name: string;age: number;}// should compileletf1: MongoFilter<Person>={$or: [{id: 1},{name: 'dima'}]};letf2: MongoFilter<Person>={$and: [{id: 1}]};letf3: MongoFilter<Person>={id: 1};// should not compileletw1: MongoFilter<Person>={wrong: 1};// does not compile - goodletw2: MongoFilter<Person>={$or: [{wrong: 1}]};// compiles
My understanding of trying to do this myself is that since all properties in type propertyRule<T> are optional, the compiler is resolving the type as {}, which is assignable to { wrong: 1}.
wow... so clearly there is a disconnect somewhere. My conclusion is that either type propertyRule<T> should not resolve to { } or the code below should not compile:
types={};lett: s={wrong: 1}
How does it make sense that it compiles given the example above? Is it by design?
changed the title [-]Recursive props of Mapped types don't work as expected[/-][+]two types should only be compatible if their members are compatible - even if all these members are optional[/+]on Dec 15, 2016
changed the title [-]two types should only be compatible if their members are compatible - even if all these members are optional[/-][+]Two types should only be compatible if their members are compatible - even if all these members are optional[/+]on Dec 15, 2016
Thanks for this link. Fantastic FAQ, I've read through the whole thing.
Types with no members can be substituted by any type.
However, FAQ does not really provide the reason WHY it was decided to do it this way. I also didn't find any discussion about this. In my opinion { } != any. I saw a mention of some mysterious side effects which can be caused by creating an object of type { }, but didn't find what the side effects are.
The bottom line is that an object with the wrong property should not be assignable to an object, optional properties of which do not contain property wrong:
interfacePerson{id?: number;name?: string;age?: number;}letwrongObject={wrong: 1};letp: Person=wrongObject;// should not compile
This supports the statement of what is structural typing, which is used by TypeScript (also from FAQ):
two types are compatible if their members are compatible.
Changing the title now that I phrased the issue better.
This proposal will also solve the problem of typing "object bags" where each property is optional, but other primitive types should be disallowed.
Correct me if I'm wrong - that's not the same as my issue. I could not understand, will this PR address this issue?
#9841 and #12914 indeed discusses this issue, but is labeled with "Working as intended"? However, it mentions #3842, which I believe was intended to solve this issue and had a PR, but was never merged.
Side note: the relevance of this issue will only increase now, that Partial<T> type is introduced.
Side note: the relevance of this issue will only increase now, that Partial type is introduced
Yep, i think this behaviour is very strange:
constitFails: Partial<{foo: number}>={bar: 42};// TS2322:Type '{ bar: number; }' is not assignableconstext={bar: 42};constbutItWorks: Partial<{foo: number}>=ext;// OK
Reacted by Andrey Gorbatov, inner-peace and Dmitry Svetlakov
Activity
PyroVortex commentedon Dec 14, 2016
My understanding of trying to do this myself is that since all properties in
type propertyRule<T>are optional, the compiler is resolving the type as{}, which is assignable to{ wrong: 1}.DmitryEfimenko commentedon Dec 14, 2016
that would make sense, but I don't think is the case since the following code does not compile, which is expected behaviour:
In this case the
propertyRule<T>is used to evaluate{ wrong: 1}, and it properly tells that there is no propertywrongon typeTPyroVortex commentedon Dec 15, 2016
Note that this, on the other hand, compiles just fine:
DmitryEfimenko commentedon Dec 15, 2016
wow... so clearly there is a disconnect somewhere. My conclusion is that either
type propertyRule<T>should not resolve to{ }or the code below should not compile:How does it make sense that it compiles given the example above? Is it by design?
normalser commentedon Dec 15, 2016
https://github.com/Microsoft/TypeScript/wiki/FAQ#why-are-all-types-assignable-to-empty-interfaces
[-]Recursive props of Mapped types don't work as expected[/-][+]two types should only be compatible if their members are compatible - even if all these members are optional[/+][-]two types should only be compatible if their members are compatible - even if all these members are optional[/-][+]Two types should only be compatible if their members are compatible - even if all these members are optional[/+]DmitryEfimenko commentedon Dec 15, 2016
Thanks for this link. Fantastic FAQ, I've read through the whole thing.
However, FAQ does not really provide the reason WHY it was decided to do it this way. I also didn't find any discussion about this. In my opinion
{ } != any. I saw a mention of some mysterious side effects which can be caused by creating an object of type{ }, but didn't find what the side effects are.The bottom line is that an object with the
wrongproperty should not be assignable to an object, optional properties of which do not contain propertywrong:This supports the statement of what is structural typing, which is used by TypeScript (also from FAQ):
Changing the title now that I phrased the issue better.
DmitryEfimenko commentedon Dec 15, 2016
Glad to see I'm not the first one to see an issue with this!
So to summarize: #12501 closes #1809, where S. Chris Colbert (@sccolbert) said:
Correct me if I'm wrong - that's not the same as my issue. I could not understand, will this PR address this issue?
#9841 and #12914 indeed discusses this issue, but is labeled with "Working as intended"? However, it mentions #3842, which I believe was intended to solve this issue and had a PR, but was never merged.
Side note: the relevance of this issue will only increase now, that
Partial<T>type is introduced.mhegazy commentedon Dec 15, 2016
Excess property checks would have caught that if it operated on unions. tracked by #12745
inner-peace commentedon Apr 27, 2017
Yep, i think this behaviour is very strange:
mhegazy commentedon Jun 13, 2017
Fixed by #16047