Skip to content

Commit

Permalink
Properly handle private/protected members in unions of object types (#…
Browse files Browse the repository at this point in the history
…38277) (#38334)

* Property handle private/protected properties in unions of object types

* Add regression test

Co-authored-by: Anders Hejlsberg <andersh@microsoft.com>
  • Loading branch information
DanielRosenwasser and ahejlsberg authored May 7, 2020

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent e261cdd commit b7d3459
Showing 5 changed files with 112 additions and 10 deletions.
31 changes: 21 additions & 10 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
@@ -10477,10 +10477,10 @@ namespace ts {
}

function createUnionOrIntersectionProperty(containingType: UnionOrIntersectionType, name: __String): Symbol | undefined {
const propSet = createMap<Symbol>();
let singleProp: Symbol | undefined;
let propSet: Map<Symbol> | undefined;
let indexTypes: Type[] | undefined;
const isUnion = containingType.flags & TypeFlags.Union;
const excludeModifiers = isUnion ? ModifierFlags.NonPublicAccessibilityModifier : 0;
// Flags we want to propagate to the result if they exist in all source symbols
let optionalFlag = isUnion ? SymbolFlags.None : SymbolFlags.Optional;
let syntheticFlag = CheckFlags.SyntheticMethod;
@@ -10490,16 +10490,25 @@ namespace ts {
if (!(type === errorType || type.flags & TypeFlags.Never)) {
const prop = getPropertyOfType(type, name);
const modifiers = prop ? getDeclarationModifierFlagsFromSymbol(prop) : 0;
if (prop && !(modifiers & excludeModifiers)) {
if (prop) {
if (isUnion) {
optionalFlag |= (prop.flags & SymbolFlags.Optional);
}
else {
optionalFlag &= prop.flags;
}
const id = "" + getSymbolId(prop);
if (!propSet.has(id)) {
propSet.set(id, prop);
if (!singleProp) {
singleProp = prop;
}
else if (prop !== singleProp) {
if (!propSet) {
propSet = createMap<Symbol>();
propSet.set("" + getSymbolId(singleProp), singleProp);
}
const id = "" + getSymbolId(prop);
if (!propSet.has(id)) {
propSet.set(id, prop);
}
}
checkFlags |= (isReadonlySymbol(prop) ? CheckFlags.Readonly : 0) |
(!(modifiers & ModifierFlags.NonPublicAccessibilityModifier) ? CheckFlags.ContainsPublic : 0) |
@@ -10526,13 +10535,15 @@ namespace ts {
}
}
}
if (!propSet.size) {
if (!singleProp || isUnion && (propSet || checkFlags & CheckFlags.Partial) && checkFlags & (CheckFlags.ContainsPrivate | CheckFlags.ContainsProtected)) {
// No property was found, or, in a union, a property has a private or protected declaration in one
// constituent, but is missing or has a different declaration in another constituent.
return undefined;
}
const props = arrayFrom(propSet.values());
if (props.length === 1 && !(checkFlags & CheckFlags.ReadPartial) && !indexTypes) {
return props[0];
if (!propSet && !(checkFlags & CheckFlags.ReadPartial) && !indexTypes) {
return singleProp;
}
const props = propSet ? arrayFrom(propSet.values()) : [singleProp];
let declarations: Declaration[] | undefined;
let firstType: Type | undefined;
let nameType: Type | undefined;
24 changes: 24 additions & 0 deletions tests/baselines/reference/privatePropertyInUnion.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//// [privatePropertyInUnion.ts]
// Repro from #38236

type Type = string | object;

class SyncableObject {
private foo: unknown;
}

interface SyncableRef<T extends ISyncableObject> {}

interface ISyncableObject<T = object> extends SyncableObject {}

type __ValueDescriptorType<T extends string | object> = T extends ISyncableObject ? SyncableRef<T> : T;


//// [privatePropertyInUnion.js]
"use strict";
// Repro from #38236
var SyncableObject = /** @class */ (function () {
function SyncableObject() {
}
return SyncableObject;
}());
32 changes: 32 additions & 0 deletions tests/baselines/reference/privatePropertyInUnion.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
=== tests/cases/compiler/privatePropertyInUnion.ts ===
// Repro from #38236

type Type = string | object;
>Type : Symbol(Type, Decl(privatePropertyInUnion.ts, 0, 0))

class SyncableObject {
>SyncableObject : Symbol(SyncableObject, Decl(privatePropertyInUnion.ts, 2, 28))

private foo: unknown;
>foo : Symbol(SyncableObject.foo, Decl(privatePropertyInUnion.ts, 4, 22))
}

interface SyncableRef<T extends ISyncableObject> {}
>SyncableRef : Symbol(SyncableRef, Decl(privatePropertyInUnion.ts, 6, 1))
>T : Symbol(T, Decl(privatePropertyInUnion.ts, 8, 22))
>ISyncableObject : Symbol(ISyncableObject, Decl(privatePropertyInUnion.ts, 8, 51))

interface ISyncableObject<T = object> extends SyncableObject {}
>ISyncableObject : Symbol(ISyncableObject, Decl(privatePropertyInUnion.ts, 8, 51))
>T : Symbol(T, Decl(privatePropertyInUnion.ts, 10, 26))
>SyncableObject : Symbol(SyncableObject, Decl(privatePropertyInUnion.ts, 2, 28))

type __ValueDescriptorType<T extends string | object> = T extends ISyncableObject ? SyncableRef<T> : T;
>__ValueDescriptorType : Symbol(__ValueDescriptorType, Decl(privatePropertyInUnion.ts, 10, 63))
>T : Symbol(T, Decl(privatePropertyInUnion.ts, 12, 27))
>T : Symbol(T, Decl(privatePropertyInUnion.ts, 12, 27))
>ISyncableObject : Symbol(ISyncableObject, Decl(privatePropertyInUnion.ts, 8, 51))
>SyncableRef : Symbol(SyncableRef, Decl(privatePropertyInUnion.ts, 6, 1))
>T : Symbol(T, Decl(privatePropertyInUnion.ts, 12, 27))
>T : Symbol(T, Decl(privatePropertyInUnion.ts, 12, 27))

20 changes: 20 additions & 0 deletions tests/baselines/reference/privatePropertyInUnion.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
=== tests/cases/compiler/privatePropertyInUnion.ts ===
// Repro from #38236

type Type = string | object;
>Type : string | object

class SyncableObject {
>SyncableObject : SyncableObject

private foo: unknown;
>foo : unknown
}

interface SyncableRef<T extends ISyncableObject> {}

interface ISyncableObject<T = object> extends SyncableObject {}

type __ValueDescriptorType<T extends string | object> = T extends ISyncableObject ? SyncableRef<T> : T;
>__ValueDescriptorType : __ValueDescriptorType<T>

15 changes: 15 additions & 0 deletions tests/cases/compiler/privatePropertyInUnion.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// @strict: true

// Repro from #38236

type Type = string | object;

class SyncableObject {
private foo: unknown;
}

interface SyncableRef<T extends ISyncableObject> {}

interface ISyncableObject<T = object> extends SyncableObject {}

type __ValueDescriptorType<T extends string | object> = T extends ISyncableObject ? SyncableRef<T> : T;

0 comments on commit b7d3459

Please sign in to comment.