|
| 1 | +=== tests/cases/compiler/unionPropertyOfProtectedAndIntersectionProperty.ts === |
| 2 | +class Foo { |
| 3 | +>Foo : Symbol(Foo, Decl(unionPropertyOfProtectedAndIntersectionProperty.ts, 0, 0)) |
| 4 | + |
| 5 | + protected foo = 0; |
| 6 | +>foo : Symbol(Foo.foo, Decl(unionPropertyOfProtectedAndIntersectionProperty.ts, 0, 11)) |
| 7 | +} |
| 8 | + |
| 9 | +class Bar { |
| 10 | +>Bar : Symbol(Bar, Decl(unionPropertyOfProtectedAndIntersectionProperty.ts, 2, 1)) |
| 11 | + |
| 12 | + protected foo = 0; |
| 13 | +>foo : Symbol(Bar.foo, Decl(unionPropertyOfProtectedAndIntersectionProperty.ts, 4, 11)) |
| 14 | +} |
| 15 | + |
| 16 | +type Nothing<V extends Foo> = void; |
| 17 | +>Nothing : Symbol(Nothing, Decl(unionPropertyOfProtectedAndIntersectionProperty.ts, 6, 1)) |
| 18 | +>V : Symbol(V, Decl(unionPropertyOfProtectedAndIntersectionProperty.ts, 8, 13)) |
| 19 | +>Foo : Symbol(Foo, Decl(unionPropertyOfProtectedAndIntersectionProperty.ts, 0, 0)) |
| 20 | + |
| 21 | +type Broken<V extends Array<Foo | Bar>> = { |
| 22 | +>Broken : Symbol(Broken, Decl(unionPropertyOfProtectedAndIntersectionProperty.ts, 8, 35)) |
| 23 | +>V : Symbol(V, Decl(unionPropertyOfProtectedAndIntersectionProperty.ts, 10, 12)) |
| 24 | +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) |
| 25 | +>Foo : Symbol(Foo, Decl(unionPropertyOfProtectedAndIntersectionProperty.ts, 0, 0)) |
| 26 | +>Bar : Symbol(Bar, Decl(unionPropertyOfProtectedAndIntersectionProperty.ts, 2, 1)) |
| 27 | + |
| 28 | + readonly [P in keyof V]: V[P] extends Foo ? Nothing<V[P]> : never; |
| 29 | +>P : Symbol(P, Decl(unionPropertyOfProtectedAndIntersectionProperty.ts, 11, 12)) |
| 30 | +>V : Symbol(V, Decl(unionPropertyOfProtectedAndIntersectionProperty.ts, 10, 12)) |
| 31 | +>V : Symbol(V, Decl(unionPropertyOfProtectedAndIntersectionProperty.ts, 10, 12)) |
| 32 | +>P : Symbol(P, Decl(unionPropertyOfProtectedAndIntersectionProperty.ts, 11, 12)) |
| 33 | +>Foo : Symbol(Foo, Decl(unionPropertyOfProtectedAndIntersectionProperty.ts, 0, 0)) |
| 34 | +>Nothing : Symbol(Nothing, Decl(unionPropertyOfProtectedAndIntersectionProperty.ts, 6, 1)) |
| 35 | +>V : Symbol(V, Decl(unionPropertyOfProtectedAndIntersectionProperty.ts, 10, 12)) |
| 36 | +>P : Symbol(P, Decl(unionPropertyOfProtectedAndIntersectionProperty.ts, 11, 12)) |
| 37 | + |
| 38 | +}; |
| 39 | + |
| 40 | +// The issue above, #49517, is fixed very indirectly. Here's some code |
| 41 | +// that shows the direct result of the change: |
| 42 | + |
| 43 | +type _3 = (Foo & Bar)['foo']; // Ok |
| 44 | +>_3 : Symbol(_3, Decl(unionPropertyOfProtectedAndIntersectionProperty.ts, 12, 2)) |
| 45 | +>Foo : Symbol(Foo, Decl(unionPropertyOfProtectedAndIntersectionProperty.ts, 0, 0)) |
| 46 | +>Bar : Symbol(Bar, Decl(unionPropertyOfProtectedAndIntersectionProperty.ts, 2, 1)) |
| 47 | + |
| 48 | +type _4 = (Foo | Bar)['foo']; // Error |
| 49 | +>_4 : Symbol(_4, Decl(unionPropertyOfProtectedAndIntersectionProperty.ts, 17, 29)) |
| 50 | +>Foo : Symbol(Foo, Decl(unionPropertyOfProtectedAndIntersectionProperty.ts, 0, 0)) |
| 51 | +>Bar : Symbol(Bar, Decl(unionPropertyOfProtectedAndIntersectionProperty.ts, 2, 1)) |
| 52 | + |
| 53 | +type _5 = (Foo | (Foo & Bar))['foo']; // Prev error, now ok |
| 54 | +>_5 : Symbol(_5, Decl(unionPropertyOfProtectedAndIntersectionProperty.ts, 18, 29)) |
| 55 | +>Foo : Symbol(Foo, Decl(unionPropertyOfProtectedAndIntersectionProperty.ts, 0, 0)) |
| 56 | +>Foo : Symbol(Foo, Decl(unionPropertyOfProtectedAndIntersectionProperty.ts, 0, 0)) |
| 57 | +>Bar : Symbol(Bar, Decl(unionPropertyOfProtectedAndIntersectionProperty.ts, 2, 1)) |
| 58 | + |
| 59 | +// V[P] in `Nothing<V[P]>` is the substitution type `V[P] & Foo`. When |
| 60 | +// checking if that's assignable to `Foo` in the constraint of `Nothing`, |
| 61 | +// it passes the regular assignability check but then goes into intersection |
| 62 | +// property checks. To pull `foo` from the substitution type, it gets the |
| 63 | +// apparent type, which turns out to be something like `(Foo & Foo') | (Foo & Bar)` |
| 64 | +// where `Foo` and `Foo'` are different this-type instantiations of `Foo`. |
| 65 | +// Those two instantiations have the same `foo` property, but then `(Foo & Bar)['foo']` |
| 66 | +// is a synthesized intersection property with a declaration in `Foo` and a |
| 67 | +// declaration in `Bar`. Because the former was marked as protected and the |
| 68 | +// latter was a different symbol, we previously thought the two symbols were |
| 69 | +// totally unrelated, as in `(Foo | Bar)['foo']`. The fix I implemented is to |
| 70 | +// check not that the two property symbols are identical, but that they share |
| 71 | +// some common declaration. The change is directly observable by seeing whether |
| 72 | +// `(Foo | (Foo & Bar))['foo']` is allowed. |
| 73 | + |
0 commit comments