Skip to content

Commit 763df85

Browse files
committed
Revise intersection construct signature mixin algorithm
1 parent 89b72ac commit 763df85

File tree

1 file changed

+25
-15
lines changed

1 file changed

+25
-15
lines changed

src/compiler/checker.ts

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4571,33 +4571,43 @@ namespace ts {
45714571
getUnionType([info1.type, info2.type]), info1.isReadonly || info2.isReadonly);
45724572
}
45734573

4574+
function includeMixinType(type: Type, types: Type[], index: number): Type {
4575+
const mixedTypes: Type[] = [];
4576+
for (let i = 0; i < types.length; i++) {
4577+
if (i === index) {
4578+
mixedTypes.push(type);
4579+
}
4580+
else if (isMixinConstructorType(types[i])) {
4581+
mixedTypes.push(getReturnTypeOfSignature(getSignaturesOfType(types[i], SignatureKind.Construct)[0]));
4582+
}
4583+
}
4584+
return getIntersectionType(mixedTypes);
4585+
}
4586+
45744587
function resolveIntersectionTypeMembers(type: IntersectionType) {
45754588
// The members and properties collections are empty for intersection types. To get all properties of an
45764589
// intersection type use getPropertiesOfType (only the language service uses this).
45774590
let callSignatures: Signature[] = emptyArray;
45784591
let constructSignatures: Signature[] = emptyArray;
45794592
let stringIndexInfo: IndexInfo;
45804593
let numberIndexInfo: IndexInfo;
4581-
let mixinTypes: Type[];
4582-
const count = type.types.length;
4583-
for (let i = 0; i < count; i++) {
4594+
const types = type.types;
4595+
const mixinCount = countWhere(types, isMixinConstructorType);
4596+
for (let i = 0; i < types.length; i++) {
45844597
const t = type.types[i];
4585-
// When a type T is preceded by a mixin constructor type, the return type of each construct signature
4586-
// in T is intersected with the return type of the mixin constructor, and the mixin construct signature
4587-
// is removed. For example, the intersection '{ new(...args: any[]) => A } & { new(s: string) => B }'
4588-
// has a single construct signature 'new(s: string) => A & B'.
4589-
let signatures = getSignaturesOfType(t, SignatureKind.Construct);
4590-
if (i < count - 1 && isMixinConstructorType(t)) {
4591-
(mixinTypes || (mixinTypes = [])).push(getReturnTypeOfSignature(signatures[0]));
4592-
}
4593-
else {
4594-
if (mixinTypes) {
4598+
// When an intersection type contains mixin constructor types, the construct signatures from
4599+
// those types are discarded and their return types are mixed into the return types of all
4600+
// other construct signatures in the intersection type. For example, the intersection type
4601+
// '{ new(...args: any[]) => A } & { new(s: string) => B }' has a single construct signature
4602+
// 'new(s: string) => A & B'.
4603+
if (mixinCount === 0 || mixinCount === types.length && i === 0 || !isMixinConstructorType(t)) {
4604+
let signatures = getSignaturesOfType(t, SignatureKind.Construct);
4605+
if (signatures.length && mixinCount > 0) {
45954606
signatures = map(signatures, s => {
45964607
const clone = cloneSignature(s);
4597-
clone.resolvedReturnType = getIntersectionType([...mixinTypes, getReturnTypeOfSignature(s)]);
4608+
clone.resolvedReturnType = includeMixinType(getReturnTypeOfSignature(s), types, i);
45984609
return clone;
45994610
});
4600-
mixinTypes = undefined;
46014611
}
46024612
constructSignatures = concatenate(constructSignatures, signatures);
46034613
}

0 commit comments

Comments
 (0)