Fix mixin logic to preserve at least one constructor type even when the (#27701)

intersection also contains non-constructor types.

Fixes #17388.
This commit is contained in:
Matt McCutchen 2019-03-12 17:30:43 -04:00 committed by Wesley Wigham
parent 6c6f216d0d
commit ab8153602a
5 changed files with 56 additions and 8 deletions

View file

@ -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]));
}
}
@ -7047,7 +7057,8 @@ 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
@ -7055,12 +7066,12 @@ namespace ts {
// 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;
});
}
@ -21104,12 +21115,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;
@ -21119,6 +21129,7 @@ namespace ts {
}
}
}
i++;
}
return false;
}

View file

@ -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();

View file

@ -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))

View file

@ -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)

View file

@ -0,0 +1,4 @@
// Repro for #17388
declare let x: {foo: undefined} & {new(...args: any[]): any};
new x();