@@ -2465,7 +2465,8 @@ namespace ts {
24652465 const symbol = type.symbol;
24662466 if (symbol) {
24672467 // Always use 'typeof T' for type of class, enum, and module objects
2468- if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Enum | SymbolFlags.ValueModule)) {
2468+ if (symbol.flags & SymbolFlags.Class && !getBaseTypeVariableOfClass(symbol) ||
2469+ symbol.flags & (SymbolFlags.Enum | SymbolFlags.ValueModule)) {
24692470 writeTypeOfSymbol(type, flags);
24702471 }
24712472 else if (shouldWriteTypeOfFunctionSymbol()) {
@@ -3639,6 +3640,11 @@ namespace ts {
36393640 return links.type;
36403641 }
36413642
3643+ function getBaseTypeVariableOfClass(symbol: Symbol) {
3644+ const baseConstructorType = getBaseConstructorTypeOfClass(getDeclaredTypeOfClassOrInterface(symbol));
3645+ return baseConstructorType.flags & TypeFlags.TypeVariable ? baseConstructorType : undefined;
3646+ }
3647+
36423648 function getTypeOfFuncClassEnumModule(symbol: Symbol): Type {
36433649 const links = getSymbolLinks(symbol);
36443650 if (!links.type) {
@@ -3647,8 +3653,13 @@ namespace ts {
36473653 }
36483654 else {
36493655 const type = createObjectType(ObjectFlags.Anonymous, symbol);
3650- links.type = strictNullChecks && symbol.flags & SymbolFlags.Optional ?
3651- includeFalsyTypes(type, TypeFlags.Undefined) : type;
3656+ if (symbol.flags & SymbolFlags.Class) {
3657+ const baseTypeVariable = getBaseTypeVariableOfClass(symbol);
3658+ links.type = baseTypeVariable ? getIntersectionType([type, baseTypeVariable]) : type;
3659+ }
3660+ else {
3661+ links.type = strictNullChecks && symbol.flags & SymbolFlags.Optional ? includeFalsyTypes(type, TypeFlags.Undefined) : type;
3662+ }
36523663 }
36533664 }
36543665 return links.type;
@@ -3812,8 +3823,26 @@ namespace ts {
38123823 return concatenate(getOuterTypeParametersOfClassOrInterface(symbol), getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol));
38133824 }
38143825
3826+ // A type is a mixin constructor if it has a single construct signature taking no type parameters and a single
3827+ // rest parameter of type any[].
3828+ function isMixinConstructorType(type: Type) {
3829+ const signatures = getSignaturesOfType(type, SignatureKind.Construct);
3830+ if (signatures.length === 1) {
3831+ const s = signatures[0];
3832+ return !s.typeParameters && s.parameters.length === 1 && s.hasRestParameter && getTypeOfParameter(s.parameters[0]) === anyArrayType;
3833+ }
3834+ return false;
3835+ }
3836+
38153837 function isConstructorType(type: Type): boolean {
3816- return isValidBaseType(type) && getSignaturesOfType(type, SignatureKind.Construct).length > 0;
3838+ if (isValidBaseType(type) && getSignaturesOfType(type, SignatureKind.Construct).length > 0) {
3839+ return true;
3840+ }
3841+ if (type.flags & TypeFlags.TypeVariable) {
3842+ const constraint = getBaseConstraintOfType(<TypeVariable>type);
3843+ return isValidBaseType(constraint) && isMixinConstructorType(constraint);
3844+ }
3845+ return false;
38173846 }
38183847
38193848 function getBaseTypeNodeOfClass(type: InterfaceType): ExpressionWithTypeArguments {
@@ -3892,7 +3921,7 @@ namespace ts {
38923921
38933922 function resolveBaseTypesOfClass(type: InterfaceType): void {
38943923 type.resolvedBaseTypes = type.resolvedBaseTypes || emptyArray;
3895- const baseConstructorType = <ObjectType> getBaseConstructorTypeOfClass(type);
3924+ const baseConstructorType = getApparentType( getBaseConstructorTypeOfClass(type) );
38963925 if (!(baseConstructorType.flags & (TypeFlags.Object | TypeFlags.Intersection))) {
38973926 return;
38983927 }
@@ -4547,11 +4576,32 @@ namespace ts {
45474576 // intersection type use getPropertiesOfType (only the language service uses this).
45484577 let callSignatures: Signature[] = emptyArray;
45494578 let constructSignatures: Signature[] = emptyArray;
4550- let stringIndexInfo: IndexInfo = undefined;
4551- let numberIndexInfo: IndexInfo = undefined;
4552- for (const t of type.types) {
4579+ let stringIndexInfo: IndexInfo;
4580+ let numberIndexInfo: IndexInfo;
4581+ let mixinTypes: Type[];
4582+ const count = type.types.length;
4583+ for (let i = 0; i < count; i++) {
4584+ 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) {
4595+ signatures = map(signatures, s => {
4596+ const clone = cloneSignature(s);
4597+ clone.resolvedReturnType = getIntersectionType([...mixinTypes, getReturnTypeOfSignature(s)]);
4598+ return clone;
4599+ });
4600+ mixinTypes = undefined;
4601+ }
4602+ constructSignatures = concatenate(constructSignatures, signatures);
4603+ }
45534604 callSignatures = concatenate(callSignatures, getSignaturesOfType(t, SignatureKind.Call));
4554- constructSignatures = concatenate(constructSignatures, getSignaturesOfType(t, SignatureKind.Construct));
45554605 stringIndexInfo = intersectIndexInfos(stringIndexInfo, getIndexInfoOfType(t, IndexKind.String));
45564606 numberIndexInfo = intersectIndexInfos(numberIndexInfo, getIndexInfoOfType(t, IndexKind.Number));
45574607 }
@@ -4593,7 +4643,7 @@ namespace ts {
45934643 constructSignatures = getDefaultConstructSignatures(classType);
45944644 }
45954645 const baseConstructorType = getBaseConstructorTypeOfClass(classType);
4596- if (baseConstructorType.flags & (TypeFlags.Object | TypeFlags.Intersection)) {
4646+ if (baseConstructorType.flags & (TypeFlags.Object | TypeFlags.Intersection | TypeFlags.TypeVariable )) {
45974647 members = createSymbolTable(getNamedMembers(members));
45984648 addInheritedMembers(members, getPropertiesOfType(baseConstructorType));
45994649 }
@@ -4890,6 +4940,7 @@ namespace ts {
48904940
48914941 function createUnionOrIntersectionProperty(containingType: UnionOrIntersectionType, name: string): Symbol {
48924942 const types = containingType.types;
4943+ const excludeModifiers = containingType.flags & TypeFlags.Union ? ModifierFlags.Private | ModifierFlags.Protected : 0;
48934944 let props: Symbol[];
48944945 // Flags we want to propagate to the result if they exist in all source symbols
48954946 let commonFlags = (containingType.flags & TypeFlags.Intersection) ? SymbolFlags.Optional : SymbolFlags.None;
@@ -4899,7 +4950,7 @@ namespace ts {
48994950 const type = getApparentType(current);
49004951 if (type !== unknownType) {
49014952 const prop = getPropertyOfType(type, name);
4902- if (prop && !(getDeclarationModifierFlagsFromSymbol(prop) & (ModifierFlags.Private | ModifierFlags.Protected) )) {
4953+ if (prop && !(getDeclarationModifierFlagsFromSymbol(prop) & excludeModifiers )) {
49034954 commonFlags &= prop.flags;
49044955 if (!props) {
49054956 props = [prop];
@@ -6965,9 +7016,12 @@ namespace ts {
69657016 result.properties = resolved.properties;
69667017 result.callSignatures = emptyArray;
69677018 result.constructSignatures = emptyArray;
6968- type = result;
7019+ return result;
69697020 }
69707021 }
7022+ else if (type.flags & TypeFlags.Intersection) {
7023+ return getIntersectionType(map((<IntersectionType>type).types, getTypeWithoutSignatures));
7024+ }
69717025 return type;
69727026 }
69737027
@@ -18387,7 +18441,8 @@ namespace ts {
1838718441 const baseTypes = getBaseTypes(type);
1838818442 if (baseTypes.length && produceDiagnostics) {
1838918443 const baseType = baseTypes[0];
18390- const staticBaseType = getBaseConstructorTypeOfClass(type);
18444+ const baseConstructorType = getBaseConstructorTypeOfClass(type);
18445+ const staticBaseType = getApparentType(baseConstructorType);
1839118446 checkBaseTypeAccessibility(staticBaseType, baseTypeNode);
1839218447 checkSourceElement(baseTypeNode.expression);
1839318448 if (baseTypeNode.typeArguments) {
@@ -18401,6 +18456,9 @@ namespace ts {
1840118456 checkTypeAssignableTo(typeWithThis, getTypeWithThisArgument(baseType, type.thisType), node.name || node, Diagnostics.Class_0_incorrectly_extends_base_class_1);
1840218457 checkTypeAssignableTo(staticType, getTypeWithoutSignatures(staticBaseType), node.name || node,
1840318458 Diagnostics.Class_static_side_0_incorrectly_extends_base_class_static_side_1);
18459+ if (baseConstructorType.flags & TypeFlags.TypeVariable && !isMixinConstructorType(staticType)) {
18460+ error(node.name || node, Diagnostics.A_mixin_class_must_have_a_constructor_with_a_single_rest_parameter_of_type_any);
18461+ }
1840418462
1840518463 if (baseType.symbol && baseType.symbol.valueDeclaration &&
1840618464 !isInAmbientContext(baseType.symbol.valueDeclaration) &&
@@ -18410,7 +18468,7 @@ namespace ts {
1841018468 }
1841118469 }
1841218470
18413- if (!(staticBaseType.symbol && staticBaseType.symbol.flags & SymbolFlags.Class)) {
18471+ if (!(staticBaseType.symbol && staticBaseType.symbol.flags & SymbolFlags.Class) && !(baseConstructorType.flags & TypeFlags.TypeVariable) ) {
1841418472 // When the static base type is a "class-like" constructor function (but not actually a class), we verify
1841518473 // that all instantiated base constructor signatures return the same type. We can simply compare the type
1841618474 // references (as opposed to checking the structure of the types) because elsewhere we have already checked
0 commit comments