mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-06-11 20:14:12 -05:00
Consider identical instances of the same symbol equivalent when creating union and intersection properties (#43560)
* Consider identical instances of the same symbol equivalent when creating union and intersection properties * Also copy over mapper and type (if available) on cloned symbols * Editorial feedback
This commit is contained in:
@@ -11641,6 +11641,7 @@ namespace ts {
|
||||
let optionalFlag = isUnion ? SymbolFlags.None : SymbolFlags.Optional;
|
||||
let syntheticFlag = CheckFlags.SyntheticMethod;
|
||||
let checkFlags = 0;
|
||||
let mergedInstantiations = false;
|
||||
for (const current of containingType.types) {
|
||||
const type = getApparentType(current);
|
||||
if (!(type === errorType || type.flags & TypeFlags.Never)) {
|
||||
@@ -11657,13 +11658,25 @@ namespace ts {
|
||||
singleProp = prop;
|
||||
}
|
||||
else if (prop !== singleProp) {
|
||||
if (!propSet) {
|
||||
propSet = new Map<SymbolId, Symbol>();
|
||||
propSet.set(getSymbolId(singleProp), singleProp);
|
||||
const isInstantiation = (getTargetSymbol(prop) || prop) === (getTargetSymbol(singleProp) || singleProp);
|
||||
// If the symbols are instances of one another with identical types - consider the symbols
|
||||
// equivalent and just use the first one, which thus allows us to avoid eliding private
|
||||
// members when intersecting a (this-)instantiations of a class with it's raw base or another instance
|
||||
if (isInstantiation && isPropertyIdenticalTo(singleProp, prop)) {
|
||||
// If we merged instantiations of a generic type, we replicate the symbol parent resetting behavior we used
|
||||
// to do when we recorded multiple distinct symbols so that we still get, eg, `Array<T>.length` printed
|
||||
// back and not `Array<string>.length` when we're looking at a `.length` access on a `string[] | number[]`
|
||||
mergedInstantiations = !!singleProp.parent && !!length(getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(singleProp.parent));
|
||||
}
|
||||
const id = getSymbolId(prop);
|
||||
if (!propSet.has(id)) {
|
||||
propSet.set(id, prop);
|
||||
else {
|
||||
if (!propSet) {
|
||||
propSet = new Map<SymbolId, Symbol>();
|
||||
propSet.set(getSymbolId(singleProp), singleProp);
|
||||
}
|
||||
const id = getSymbolId(prop);
|
||||
if (!propSet.has(id)) {
|
||||
propSet.set(id, prop);
|
||||
}
|
||||
}
|
||||
}
|
||||
checkFlags |= (isReadonlySymbol(prop) ? CheckFlags.Readonly : 0) |
|
||||
@@ -11697,7 +11710,19 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
if (!propSet && !(checkFlags & CheckFlags.ReadPartial) && !indexTypes) {
|
||||
return singleProp;
|
||||
if (mergedInstantiations) {
|
||||
// No symbol from a union/intersection should have a `.parent` set (since unions/intersections don't act as symbol parents)
|
||||
// Unless that parent is "reconstituted" from the "first value declaration" on the symbol (which is likely different than its instantiated parent!)
|
||||
// They also have a `.containingType` set, which affects some services endpoints behavior, like `getRootSymbol`
|
||||
const clone = createSymbolWithType(singleProp, (singleProp as TransientSymbol).type);
|
||||
clone.parent = singleProp.valueDeclaration?.symbol?.parent;
|
||||
clone.containingType = containingType;
|
||||
clone.mapper = (singleProp as TransientSymbol).mapper;
|
||||
return clone;
|
||||
}
|
||||
else {
|
||||
return singleProp;
|
||||
}
|
||||
}
|
||||
const props = propSet ? arrayFrom(propSet.values()) : [singleProp];
|
||||
let declarations: Declaration[] | undefined;
|
||||
|
||||
@@ -24,8 +24,8 @@ declare const b: (string | number)[] | null[] | undefined[] | {}[];
|
||||
|
||||
let x = a.equalsShallow(b);
|
||||
>x : Symbol(x, Decl(bivariantInferences.ts, 9, 3))
|
||||
>a.equalsShallow : Symbol(Array.equalsShallow, Decl(bivariantInferences.ts, 2, 20), Decl(bivariantInferences.ts, 2, 20), Decl(bivariantInferences.ts, 2, 20), Decl(bivariantInferences.ts, 2, 20))
|
||||
>a.equalsShallow : Symbol(Array.equalsShallow, Decl(bivariantInferences.ts, 2, 20))
|
||||
>a : Symbol(a, Decl(bivariantInferences.ts, 6, 13))
|
||||
>equalsShallow : Symbol(Array.equalsShallow, Decl(bivariantInferences.ts, 2, 20), Decl(bivariantInferences.ts, 2, 20), Decl(bivariantInferences.ts, 2, 20), Decl(bivariantInferences.ts, 2, 20))
|
||||
>equalsShallow : Symbol(Array.equalsShallow, Decl(bivariantInferences.ts, 2, 20))
|
||||
>b : Symbol(b, Decl(bivariantInferences.ts, 7, 13))
|
||||
|
||||
|
||||
@@ -19,8 +19,8 @@ declare const b: (string | number)[] | null[] | undefined[] | {}[];
|
||||
let x = a.equalsShallow(b);
|
||||
>x : boolean
|
||||
>a.equalsShallow(b) : boolean
|
||||
>a.equalsShallow : (<T>(this: readonly T[], other: readonly T[]) => boolean) | (<T>(this: readonly T[], other: readonly T[]) => boolean) | (<T>(this: readonly T[], other: readonly T[]) => boolean) | (<T>(this: readonly T[], other: readonly T[]) => boolean)
|
||||
>a.equalsShallow : <T>(this: readonly T[], other: readonly T[]) => boolean
|
||||
>a : (string | number)[] | null[] | undefined[] | {}[]
|
||||
>equalsShallow : (<T>(this: readonly T[], other: readonly T[]) => boolean) | (<T>(this: readonly T[], other: readonly T[]) => boolean) | (<T>(this: readonly T[], other: readonly T[]) => boolean) | (<T>(this: readonly T[], other: readonly T[]) => boolean)
|
||||
>equalsShallow : <T>(this: readonly T[], other: readonly T[]) => boolean
|
||||
>b : (string | number)[] | null[] | undefined[] | {}[]
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@
|
||||
},
|
||||
{
|
||||
"text": "toString",
|
||||
"kind": "propertyName"
|
||||
"kind": "methodName"
|
||||
},
|
||||
{
|
||||
"text": "(",
|
||||
@@ -198,7 +198,7 @@
|
||||
},
|
||||
{
|
||||
"text": "toLocaleString",
|
||||
"kind": "propertyName"
|
||||
"kind": "methodName"
|
||||
},
|
||||
{
|
||||
"text": "(",
|
||||
@@ -981,7 +981,7 @@
|
||||
},
|
||||
{
|
||||
"text": "join",
|
||||
"kind": "propertyName"
|
||||
"kind": "methodName"
|
||||
},
|
||||
{
|
||||
"text": "(",
|
||||
|
||||
13
tests/baselines/reference/conditionalTypeClassMembers.js
Normal file
13
tests/baselines/reference/conditionalTypeClassMembers.js
Normal file
@@ -0,0 +1,13 @@
|
||||
//// [conditionalTypeClassMembers.ts]
|
||||
declare class MyRecord {
|
||||
private a();
|
||||
b(): unknown;
|
||||
}
|
||||
|
||||
declare class MySet<TSet extends MyRecord> {
|
||||
public item(): TSet;
|
||||
}
|
||||
|
||||
type DS<TRec extends MyRecord | { [key: string]: unknown }> = TRec extends MyRecord ? MySet<TRec> : TRec[];
|
||||
|
||||
//// [conditionalTypeClassMembers.js]
|
||||
@@ -0,0 +1,32 @@
|
||||
=== tests/cases/compiler/conditionalTypeClassMembers.ts ===
|
||||
declare class MyRecord {
|
||||
>MyRecord : Symbol(MyRecord, Decl(conditionalTypeClassMembers.ts, 0, 0))
|
||||
|
||||
private a();
|
||||
>a : Symbol(MyRecord.a, Decl(conditionalTypeClassMembers.ts, 0, 24))
|
||||
|
||||
b(): unknown;
|
||||
>b : Symbol(MyRecord.b, Decl(conditionalTypeClassMembers.ts, 1, 16))
|
||||
}
|
||||
|
||||
declare class MySet<TSet extends MyRecord> {
|
||||
>MySet : Symbol(MySet, Decl(conditionalTypeClassMembers.ts, 3, 1))
|
||||
>TSet : Symbol(TSet, Decl(conditionalTypeClassMembers.ts, 5, 20))
|
||||
>MyRecord : Symbol(MyRecord, Decl(conditionalTypeClassMembers.ts, 0, 0))
|
||||
|
||||
public item(): TSet;
|
||||
>item : Symbol(MySet.item, Decl(conditionalTypeClassMembers.ts, 5, 44))
|
||||
>TSet : Symbol(TSet, Decl(conditionalTypeClassMembers.ts, 5, 20))
|
||||
}
|
||||
|
||||
type DS<TRec extends MyRecord | { [key: string]: unknown }> = TRec extends MyRecord ? MySet<TRec> : TRec[];
|
||||
>DS : Symbol(DS, Decl(conditionalTypeClassMembers.ts, 7, 1))
|
||||
>TRec : Symbol(TRec, Decl(conditionalTypeClassMembers.ts, 9, 8))
|
||||
>MyRecord : Symbol(MyRecord, Decl(conditionalTypeClassMembers.ts, 0, 0))
|
||||
>key : Symbol(key, Decl(conditionalTypeClassMembers.ts, 9, 35))
|
||||
>TRec : Symbol(TRec, Decl(conditionalTypeClassMembers.ts, 9, 8))
|
||||
>MyRecord : Symbol(MyRecord, Decl(conditionalTypeClassMembers.ts, 0, 0))
|
||||
>MySet : Symbol(MySet, Decl(conditionalTypeClassMembers.ts, 3, 1))
|
||||
>TRec : Symbol(TRec, Decl(conditionalTypeClassMembers.ts, 9, 8))
|
||||
>TRec : Symbol(TRec, Decl(conditionalTypeClassMembers.ts, 9, 8))
|
||||
|
||||
22
tests/baselines/reference/conditionalTypeClassMembers.types
Normal file
22
tests/baselines/reference/conditionalTypeClassMembers.types
Normal file
@@ -0,0 +1,22 @@
|
||||
=== tests/cases/compiler/conditionalTypeClassMembers.ts ===
|
||||
declare class MyRecord {
|
||||
>MyRecord : MyRecord
|
||||
|
||||
private a();
|
||||
>a : () => any
|
||||
|
||||
b(): unknown;
|
||||
>b : () => unknown
|
||||
}
|
||||
|
||||
declare class MySet<TSet extends MyRecord> {
|
||||
>MySet : MySet<TSet>
|
||||
|
||||
public item(): TSet;
|
||||
>item : () => TSet
|
||||
}
|
||||
|
||||
type DS<TRec extends MyRecord | { [key: string]: unknown }> = TRec extends MyRecord ? MySet<TRec> : TRec[];
|
||||
>DS : DS<TRec>
|
||||
>key : string
|
||||
|
||||
@@ -387,13 +387,13 @@ function f9(x: ProtectedGeneric<{a: void;}> & ProtectedGeneric<{a:void;b:void;}>
|
||||
>b : Symbol(b, Decl(mixinAccessModifiers.ts, 127, 71))
|
||||
|
||||
x.privateMethod(); // Error, private constituent makes method inaccessible
|
||||
>x.privateMethod : Symbol(ProtectedGeneric.privateMethod, Decl(mixinAccessModifiers.ts, 107, 27), Decl(mixinAccessModifiers.ts, 107, 27))
|
||||
>x.privateMethod : Symbol(ProtectedGeneric.privateMethod, Decl(mixinAccessModifiers.ts, 107, 27))
|
||||
>x : Symbol(x, Decl(mixinAccessModifiers.ts, 127, 12))
|
||||
>privateMethod : Symbol(ProtectedGeneric.privateMethod, Decl(mixinAccessModifiers.ts, 107, 27), Decl(mixinAccessModifiers.ts, 107, 27))
|
||||
>privateMethod : Symbol(ProtectedGeneric.privateMethod, Decl(mixinAccessModifiers.ts, 107, 27))
|
||||
|
||||
x.protectedMethod(); // Error, protected when all constituents are protected
|
||||
>x.protectedMethod : Symbol(ProtectedGeneric.protectedMethod, Decl(mixinAccessModifiers.ts, 108, 27), Decl(mixinAccessModifiers.ts, 108, 27))
|
||||
>x.protectedMethod : Symbol(ProtectedGeneric.protectedMethod, Decl(mixinAccessModifiers.ts, 108, 27))
|
||||
>x : Symbol(x, Decl(mixinAccessModifiers.ts, 127, 12))
|
||||
>protectedMethod : Symbol(ProtectedGeneric.protectedMethod, Decl(mixinAccessModifiers.ts, 108, 27), Decl(mixinAccessModifiers.ts, 108, 27))
|
||||
>protectedMethod : Symbol(ProtectedGeneric.protectedMethod, Decl(mixinAccessModifiers.ts, 108, 27))
|
||||
}
|
||||
|
||||
|
||||
@@ -373,14 +373,14 @@ function f9(x: ProtectedGeneric<{a: void;}> & ProtectedGeneric<{a:void;b:void;}>
|
||||
|
||||
x.privateMethod(); // Error, private constituent makes method inaccessible
|
||||
>x.privateMethod() : void
|
||||
>x.privateMethod : (() => void) & (() => void)
|
||||
>x.privateMethod : () => void
|
||||
>x : ProtectedGeneric<{ a: void; }> & ProtectedGeneric<{ a: void; b: void; }>
|
||||
>privateMethod : (() => void) & (() => void)
|
||||
>privateMethod : () => void
|
||||
|
||||
x.protectedMethod(); // Error, protected when all constituents are protected
|
||||
>x.protectedMethod() : void
|
||||
>x.protectedMethod : (() => void) & (() => void)
|
||||
>x.protectedMethod : () => void
|
||||
>x : ProtectedGeneric<{ a: void; }> & ProtectedGeneric<{ a: void; b: void; }>
|
||||
>protectedMethod : (() => void) & (() => void)
|
||||
>protectedMethod : () => void
|
||||
}
|
||||
|
||||
|
||||
@@ -56,9 +56,9 @@ const AMixin = <K extends Constructor<Initable> & Initable>(SuperClass: K) => {
|
||||
};
|
||||
|
||||
let SomeHowNotOkay = class A extends Serializable(SuperClass) {
|
||||
>SomeHowNotOkay : { new (...args: any[]): A; prototype: AMixin<any>.A; init: (...args: any[]) => void; } & K
|
||||
>class A extends Serializable(SuperClass) { } : { new (...args: any[]): A; prototype: AMixin<any>.A; init: (...args: any[]) => void; } & K
|
||||
>A : { new (...args: any[]): A; prototype: AMixin<any>.A; init: (...args: any[]) => void; } & K
|
||||
>SomeHowNotOkay : { new (...args: any[]): A; prototype: AMixin<any>.A; init(...args: any[]): void; } & K
|
||||
>class A extends Serializable(SuperClass) { } : { new (...args: any[]): A; prototype: AMixin<any>.A; init(...args: any[]): void; } & K
|
||||
>A : { new (...args: any[]): A; prototype: AMixin<any>.A; init(...args: any[]): void; } & K
|
||||
>Serializable(SuperClass) : Serializable<K>.SerializableLocal & Initable
|
||||
>Serializable : <K extends Constructor<Initable> & Initable>(SuperClass: K) => { new (...args: any[]): SerializableLocal; prototype: Serializable<any>.SerializableLocal; init(...args: any[]): void; } & K
|
||||
>SuperClass : K
|
||||
|
||||
7
tests/baselines/reference/sliceResultCast.js
Normal file
7
tests/baselines/reference/sliceResultCast.js
Normal file
@@ -0,0 +1,7 @@
|
||||
//// [sliceResultCast.ts]
|
||||
declare var x: [number, string] | [number, string, string];
|
||||
|
||||
x.slice(1) as readonly string[];
|
||||
|
||||
//// [sliceResultCast.js]
|
||||
x.slice(1);
|
||||
9
tests/baselines/reference/sliceResultCast.symbols
Normal file
9
tests/baselines/reference/sliceResultCast.symbols
Normal file
@@ -0,0 +1,9 @@
|
||||
=== tests/cases/compiler/sliceResultCast.ts ===
|
||||
declare var x: [number, string] | [number, string, string];
|
||||
>x : Symbol(x, Decl(sliceResultCast.ts, 0, 11))
|
||||
|
||||
x.slice(1) as readonly string[];
|
||||
>x.slice : Symbol(Array.slice, Decl(lib.es5.d.ts, --, --))
|
||||
>x : Symbol(x, Decl(sliceResultCast.ts, 0, 11))
|
||||
>slice : Symbol(Array.slice, Decl(lib.es5.d.ts, --, --))
|
||||
|
||||
12
tests/baselines/reference/sliceResultCast.types
Normal file
12
tests/baselines/reference/sliceResultCast.types
Normal file
@@ -0,0 +1,12 @@
|
||||
=== tests/cases/compiler/sliceResultCast.ts ===
|
||||
declare var x: [number, string] | [number, string, string];
|
||||
>x : [number, string] | [number, string, string]
|
||||
|
||||
x.slice(1) as readonly string[];
|
||||
>x.slice(1) as readonly string[] : readonly string[]
|
||||
>x.slice(1) : (string | number)[]
|
||||
>x.slice : (start?: number, end?: number) => (string | number)[]
|
||||
>x : [number, string] | [number, string, string]
|
||||
>slice : (start?: number, end?: number) => (string | number)[]
|
||||
>1 : 1
|
||||
|
||||
@@ -33,9 +33,9 @@ function f<T extends Cat | Dog>(a: T) {
|
||||
>T : Symbol(T, Decl(typeParameterExtendingUnion1.ts, 8, 11))
|
||||
|
||||
a.run();
|
||||
>a.run : Symbol(Animal.run, Decl(typeParameterExtendingUnion1.ts, 0, 14), Decl(typeParameterExtendingUnion1.ts, 0, 14))
|
||||
>a.run : Symbol(Animal.run, Decl(typeParameterExtendingUnion1.ts, 0, 14))
|
||||
>a : Symbol(a, Decl(typeParameterExtendingUnion1.ts, 8, 32))
|
||||
>run : Symbol(Animal.run, Decl(typeParameterExtendingUnion1.ts, 0, 14), Decl(typeParameterExtendingUnion1.ts, 0, 14))
|
||||
>run : Symbol(Animal.run, Decl(typeParameterExtendingUnion1.ts, 0, 14))
|
||||
|
||||
run(a);
|
||||
>run : Symbol(run, Decl(typeParameterExtendingUnion1.ts, 2, 33))
|
||||
|
||||
@@ -30,9 +30,9 @@ function f<T extends Cat | Dog>(a: T) {
|
||||
|
||||
a.run();
|
||||
>a.run() : void
|
||||
>a.run : (() => void) | (() => void)
|
||||
>a.run : () => void
|
||||
>a : Cat | Dog
|
||||
>run : (() => void) | (() => void)
|
||||
>run : () => void
|
||||
|
||||
run(a);
|
||||
>run(a) : void
|
||||
|
||||
@@ -20,9 +20,9 @@ function run(a: Cat | Dog) {
|
||||
>Dog : Symbol(Dog, Decl(typeParameterExtendingUnion2.ts, 1, 33))
|
||||
|
||||
a.run();
|
||||
>a.run : Symbol(Animal.run, Decl(typeParameterExtendingUnion2.ts, 0, 14), Decl(typeParameterExtendingUnion2.ts, 0, 14))
|
||||
>a.run : Symbol(Animal.run, Decl(typeParameterExtendingUnion2.ts, 0, 14))
|
||||
>a : Symbol(a, Decl(typeParameterExtendingUnion2.ts, 4, 13))
|
||||
>run : Symbol(Animal.run, Decl(typeParameterExtendingUnion2.ts, 0, 14), Decl(typeParameterExtendingUnion2.ts, 0, 14))
|
||||
>run : Symbol(Animal.run, Decl(typeParameterExtendingUnion2.ts, 0, 14))
|
||||
}
|
||||
|
||||
function f<T extends Cat | Dog>(a: T) {
|
||||
@@ -34,9 +34,9 @@ function f<T extends Cat | Dog>(a: T) {
|
||||
>T : Symbol(T, Decl(typeParameterExtendingUnion2.ts, 8, 11))
|
||||
|
||||
a.run();
|
||||
>a.run : Symbol(Animal.run, Decl(typeParameterExtendingUnion2.ts, 0, 14), Decl(typeParameterExtendingUnion2.ts, 0, 14))
|
||||
>a.run : Symbol(Animal.run, Decl(typeParameterExtendingUnion2.ts, 0, 14))
|
||||
>a : Symbol(a, Decl(typeParameterExtendingUnion2.ts, 8, 32))
|
||||
>run : Symbol(Animal.run, Decl(typeParameterExtendingUnion2.ts, 0, 14), Decl(typeParameterExtendingUnion2.ts, 0, 14))
|
||||
>run : Symbol(Animal.run, Decl(typeParameterExtendingUnion2.ts, 0, 14))
|
||||
|
||||
run(a);
|
||||
>run : Symbol(run, Decl(typeParameterExtendingUnion2.ts, 2, 33))
|
||||
|
||||
@@ -19,9 +19,9 @@ function run(a: Cat | Dog) {
|
||||
|
||||
a.run();
|
||||
>a.run() : void
|
||||
>a.run : (() => void) | (() => void)
|
||||
>a.run : () => void
|
||||
>a : Cat | Dog
|
||||
>run : (() => void) | (() => void)
|
||||
>run : () => void
|
||||
}
|
||||
|
||||
function f<T extends Cat | Dog>(a: T) {
|
||||
@@ -30,9 +30,9 @@ function f<T extends Cat | Dog>(a: T) {
|
||||
|
||||
a.run();
|
||||
>a.run() : void
|
||||
>a.run : (() => void) | (() => void)
|
||||
>a.run : () => void
|
||||
>a : Cat | Dog
|
||||
>run : (() => void) | (() => void)
|
||||
>run : () => void
|
||||
|
||||
run(a);
|
||||
>run(a) : void
|
||||
|
||||
10
tests/cases/compiler/conditionalTypeClassMembers.ts
Normal file
10
tests/cases/compiler/conditionalTypeClassMembers.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
declare class MyRecord {
|
||||
private a();
|
||||
b(): unknown;
|
||||
}
|
||||
|
||||
declare class MySet<TSet extends MyRecord> {
|
||||
public item(): TSet;
|
||||
}
|
||||
|
||||
type DS<TRec extends MyRecord | { [key: string]: unknown }> = TRec extends MyRecord ? MySet<TRec> : TRec[];
|
||||
3
tests/cases/compiler/sliceResultCast.ts
Normal file
3
tests/cases/compiler/sliceResultCast.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
declare var x: [number, string] | [number, string, string];
|
||||
|
||||
x.slice(1) as readonly string[];
|
||||
Reference in New Issue
Block a user