Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 19 additions & 8 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7026,13 +7026,23 @@ namespace ts {
getUnionType([info1.type, info2.type]), info1.isReadonly || info2.isReadonly);
}

function includeMixinType(type: Type, types: ReadonlyArray<Type>, index: number): Type {
function findMixins(types: ReadonlyArray<Type>): ReadonlyArray<boolean> {
const constructorTypeCount = countWhere(types, (t) => getSignaturesOfType(t, SignatureKind.Construct).length > 0);
const mixinFlags = map(types, isMixinConstructorType);
if (constructorTypeCount > 0 && constructorTypeCount === countWhere(mixinFlags, (b) => b)) {
const firstMixinIndex = mixinFlags.indexOf(/*searchElement*/ true);
mixinFlags[firstMixinIndex] = false;
}
return mixinFlags;
}

function includeMixinType(type: Type, types: ReadonlyArray<Type>, mixinFlags: ReadonlyArray<boolean>, index: number): Type {
const mixedTypes: Type[] = [];
for (let i = 0; i < types.length; i++) {
if (i === index) {
mixedTypes.push(type);
}
else if (isMixinConstructorType(types[i])) {
else if (mixinFlags[i]) {
mixedTypes.push(getReturnTypeOfSignature(getSignaturesOfType(types[i], SignatureKind.Construct)[0]));
}
}
Expand All @@ -7047,20 +7057,21 @@ namespace ts {
let stringIndexInfo: IndexInfo | undefined;
let numberIndexInfo: IndexInfo | undefined;
const types = type.types;
const mixinCount = countWhere(types, isMixinConstructorType);
const mixinFlags = findMixins(types);
const mixinCount = countWhere(mixinFlags, (b) => b);
for (let i = 0; i < types.length; i++) {
const t = type.types[i];
// When an intersection type contains mixin constructor types, the construct signatures from
// those types are discarded and their return types are mixed into the return types of all
// other construct signatures in the intersection type. For example, the intersection type
// '{ new(...args: any[]) => A } & { new(s: string) => B }' has a single construct signature
// 'new(s: string) => A & B'.
if (mixinCount === 0 || mixinCount === types.length && i === 0 || !isMixinConstructorType(t)) {
if (!mixinFlags[i]) {
let signatures = getSignaturesOfType(t, SignatureKind.Construct);
if (signatures.length && mixinCount > 0) {
signatures = map(signatures, s => {
const clone = cloneSignature(s);
clone.resolvedReturnType = includeMixinType(getReturnTypeOfSignature(s), types, i);
clone.resolvedReturnType = includeMixinType(getReturnTypeOfSignature(s), types, mixinFlags, i);
return clone;
});
}
Expand Down Expand Up @@ -21092,12 +21103,11 @@ namespace ts {
const firstBase = baseTypes[0];
if (firstBase.flags & TypeFlags.Intersection) {
const types = (firstBase as IntersectionType).types;
const mixinCount = countWhere(types, isMixinConstructorType);
const mixinFlags = findMixins(types);
let i = 0;
for (const intersectionMember of (firstBase as IntersectionType).types) {
i++;
// We want to ignore mixin ctors
if (mixinCount === 0 || mixinCount === types.length && i === 0 || !isMixinConstructorType(intersectionMember)) {
if (!mixinFlags[i]) {
if (getObjectFlags(intersectionMember) & (ObjectFlags.Class | ObjectFlags.Interface)) {
if (intersectionMember.symbol === target) {
return true;
Expand All @@ -21107,6 +21117,7 @@ namespace ts {
}
}
}
i++;
}
return false;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//// [intersectionOfMixinConstructorTypeAndNonConstructorType.ts]
// Repro for #17388

declare let x: {foo: undefined} & {new(...args: any[]): any};
new x();


//// [intersectionOfMixinConstructorTypeAndNonConstructorType.js]
// Repro for #17388
new x();
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
=== tests/cases/compiler/intersectionOfMixinConstructorTypeAndNonConstructorType.ts ===
// Repro for #17388

declare let x: {foo: undefined} & {new(...args: any[]): any};
>x : Symbol(x, Decl(intersectionOfMixinConstructorTypeAndNonConstructorType.ts, 2, 11))
>foo : Symbol(foo, Decl(intersectionOfMixinConstructorTypeAndNonConstructorType.ts, 2, 16))
>args : Symbol(args, Decl(intersectionOfMixinConstructorTypeAndNonConstructorType.ts, 2, 39))

new x();
>x : Symbol(x, Decl(intersectionOfMixinConstructorTypeAndNonConstructorType.ts, 2, 11))

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
=== tests/cases/compiler/intersectionOfMixinConstructorTypeAndNonConstructorType.ts ===
// Repro for #17388

declare let x: {foo: undefined} & {new(...args: any[]): any};
>x : { foo: undefined; } & (new (...args: any[]) => any)
>foo : undefined
>args : any[]

new x();
>new x() : any
>x : { foo: undefined; } & (new (...args: any[]) => any)

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Repro for #17388

declare let x: {foo: undefined} & {new(...args: any[]): any};
new x();