From 91245acda4ec6ea4ef2507d995f891ebc5f9485b Mon Sep 17 00:00:00 2001 From: Vakhurin Sergey Date: Tue, 30 Oct 2018 02:12:43 +0300 Subject: [PATCH] Support parameter types for mixin constructors --- src/compiler/checker.ts | 94 ++++---- src/compiler/diagnosticMessages.json | 2 +- .../genericWildcardBaseClass.errors.txt | 73 ++++++ .../reference/genericWildcardBaseClass.js | 154 +++++++++++++ .../genericWildcardBaseClass.symbols | 173 ++++++++++++++ .../reference/genericWildcardBaseClass.types | 211 ++++++++++++++++++ .../compiler/genericWildcardBaseClass.ts | 56 +++++ 7 files changed, 711 insertions(+), 52 deletions(-) create mode 100644 tests/baselines/reference/genericWildcardBaseClass.errors.txt create mode 100644 tests/baselines/reference/genericWildcardBaseClass.js create mode 100644 tests/baselines/reference/genericWildcardBaseClass.symbols create mode 100644 tests/baselines/reference/genericWildcardBaseClass.types create mode 100644 tests/cases/compiler/genericWildcardBaseClass.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 418be79878e35..d8b23c91289b9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5634,15 +5634,23 @@ namespace ts { return concatenate(getOuterTypeParametersOfClassOrInterface(symbol), getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol)); } - // A type is a mixin constructor if it has a single construct signature taking no type parameters and a single - // rest parameter of type any[]. + // A type is a mixin constructor if it has all construct signatures taking no type parameters + // and the same parameter types. function isMixinConstructorType(type: Type) { const signatures = getSignaturesOfType(type, SignatureKind.Construct); - if (signatures.length === 1) { - const s = signatures[0]; - return !s.typeParameters && s.parameters.length === 1 && s.hasRestParameter && getTypeOfParameter(s.parameters[0]) === anyArrayType; + if (signatures.length === 0) { + return false; } - return false; + for (let i = 0; i < signatures.length; ++i) { + const signature = signatures[i]; + if (signature.typeParameters) { + return false; + } + if ((i !== 0) && !compareSignaturesIdentical(signature, signatures[0], /*partialMatch*/ false, /*ignoreThisTypes*/ false, /*ignoreReturnTypes*/ false, compareTypesIdentical)) { + return false; + } + } + return true; } function isConstructorType(type: Type): boolean { @@ -5651,7 +5659,7 @@ namespace ts { } if (type.flags & TypeFlags.TypeVariable) { const constraint = getBaseConstraintOfType(type); - return !!constraint && isValidBaseType(constraint) && isMixinConstructorType(constraint); + return !!constraint && isConstructorType(constraint); } return isJSConstructorType(type); } @@ -6672,19 +6680,6 @@ namespace ts { getUnionType([info1.type, info2.type]), info1.isReadonly || info2.isReadonly); } - function includeMixinType(type: Type, types: ReadonlyArray, 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])) { - mixedTypes.push(getReturnTypeOfSignature(getSignaturesOfType(types[i], SignatureKind.Construct)[0])); - } - } - return getIntersectionType(mixedTypes); - } - function resolveIntersectionTypeMembers(type: IntersectionType) { // The members and properties collections are empty for intersection types. To get all properties of an // intersection type use getPropertiesOfType (only the language service uses this). @@ -6693,29 +6688,33 @@ namespace ts { let stringIndexInfo: IndexInfo | undefined; let numberIndexInfo: IndexInfo | undefined; const types = type.types; - const mixinCount = countWhere(types, isMixinConstructorType); 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)) { - 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); - return clone; - }); - } - constructSignatures = concatenate(constructSignatures, signatures); - } + constructSignatures = concatenate(constructSignatures, getSignaturesOfType(t, SignatureKind.Construct)); callSignatures = concatenate(callSignatures, getSignaturesOfType(t, SignatureKind.Call)); stringIndexInfo = intersectIndexInfos(stringIndexInfo, getIndexInfoOfType(t, IndexKind.String)); numberIndexInfo = intersectIndexInfos(numberIndexInfo, getIndexInfoOfType(t, IndexKind.Number)); } + + if (constructSignatures.length) { + // When an intersection type contains constructor types, the construct signature return types + // are replaced with the single intersection of all return types. + // For example, the intersection type + // '{ new(s: string) => A } & { new(s: string) => B }' has a single construct signature + // 'new(s: string) => A & B'. + const eq = (s0: Signature, s1: Signature) => compareSignaturesIdentical(s0, s1, /*partialMatch*/ false, /*ignoreThisTypes*/ true, /*ignoreReturnTypes*/ true, compareTypesIdentical) === Ternary.True; + const returnType = getIntersectionType(constructSignatures.map(s => getReturnTypeOfSignature(s))); + const uniqConstructSignatures = Array(); + for (const signature of constructSignatures) { + if (!contains(uniqConstructSignatures, signature, eq)) { + const clone = cloneSignature(signature); + clone.resolvedReturnType = returnType; + uniqConstructSignatures.push(clone); + } + } + constructSignatures = uniqConstructSignatures; + } + setStructuredTypeMembers(type, emptySymbols, callSignatures, constructSignatures, stringIndexInfo, numberIndexInfo); } @@ -20004,20 +20003,13 @@ namespace ts { } const firstBase = baseTypes[0]; if (firstBase.flags & TypeFlags.Intersection) { - const types = (firstBase as IntersectionType).types; - const mixinCount = countWhere(types, isMixinConstructorType); - 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 (getObjectFlags(intersectionMember) & (ObjectFlags.Class | ObjectFlags.Interface)) { - if (intersectionMember.symbol === target) { - return true; - } - if (typeHasProtectedAccessibleBase(target, intersectionMember as InterfaceType)) { - return true; - } + if (getObjectFlags(intersectionMember) & (ObjectFlags.Class | ObjectFlags.Interface)) { + if (intersectionMember.symbol === target) { + return true; + } + if (typeHasProtectedAccessibleBase(target, intersectionMember as InterfaceType)) { + return true; } } } @@ -26017,7 +26009,7 @@ namespace ts { Diagnostics.Class_static_side_0_incorrectly_extends_base_class_static_side_1); } if (baseConstructorType.flags & TypeFlags.TypeVariable && !isMixinConstructorType(staticType)) { - error(node.name || node, Diagnostics.A_mixin_class_must_have_a_constructor_with_a_single_rest_parameter_of_type_any); + error(node.name || node, Diagnostics.A_mixin_class_must_have_a_constructor_with_the_same_parameter_types_as_the_base_class); } if (!(staticBaseType.symbol && staticBaseType.symbol.flags & SymbolFlags.Class) && !(baseConstructorType.flags & TypeFlags.TypeVariable)) { diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 37cab0359d48f..afb88d3ff327c 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1960,7 +1960,7 @@ "category": "Error", "code": 2544 }, - "A mixin class must have a constructor with a single rest parameter of type 'any[]'.": { + "A mixin class must have a constructor with the same parameter types as the base class.": { "category": "Error", "code": 2545 }, diff --git a/tests/baselines/reference/genericWildcardBaseClass.errors.txt b/tests/baselines/reference/genericWildcardBaseClass.errors.txt new file mode 100644 index 0000000000000..eb11cf7d5ba97 --- /dev/null +++ b/tests/baselines/reference/genericWildcardBaseClass.errors.txt @@ -0,0 +1,73 @@ +tests/cases/compiler/genericWildcardBaseClass.ts(23,18): error TS2545: A mixin class must have a constructor with the same parameter types as the base class. +tests/cases/compiler/genericWildcardBaseClass.ts(42,49): error TS2345: Argument of type 'typeof BadClass' is not assignable to parameter of type 'typeof BaseClass'. + Types of parameters 'n' and 's' are incompatible. + Type 'string' is not assignable to type 'number'. +tests/cases/compiler/genericWildcardBaseClass.ts(46,30): error TS2345: Argument of type '2' is not assignable to parameter of type 'string'. + + +==== tests/cases/compiler/genericWildcardBaseClass.ts (3 errors) ==== + abstract class BaseClass { + constructor(s: string = '', ...args: any[]) { } + base() { return 0; } + static staticBase() { return ''; } + } + + function extendNoConstructor(Base: T) { + return class ExN extends Base { + ext() { return 0; } + static staticExt() { return ''; } + }; + } + + function extendCompatibleConstructor(Base: T) { + return class ExC extends Base { + constructor(x?: string, ...args: any[]) { + super(x, args); + } + }; + } + + function fails_IncompatibleConstructor(Base: T) { + return class Fail extends Base { + ~~~~ +!!! error TS2545: A mixin class must have a constructor with the same parameter types as the base class. + constructor(x?: string, ...args: string[]) { + super(x, args); + } + }; + } + + abstract class ExtClass extends BaseClass { + thing() { return 0; } + static staticThing() { return ''; } + } + + abstract class BadClass extends BaseClass { + constructor(n: number) { + super(); + } + } + + const Thing2 = extendCompatibleConstructor(extendNoConstructor(ExtClass)); + extendCompatibleConstructor(extendNoConstructor(BadClass)); + ~~~~~~~~ +!!! error TS2345: Argument of type 'typeof BadClass' is not assignable to parameter of type 'typeof BaseClass'. +!!! error TS2345: Types of parameters 'n' and 's' are incompatible. +!!! error TS2345: Type 'string' is not assignable to type 'number'. + + const thing2 = new Thing2(); + const thing2arg = new Thing2(''); + const fails_arg = new Thing2(2); + ~ +!!! error TS2345: Argument of type '2' is not assignable to parameter of type 'string'. + + const str2 = Thing2.staticExt() + Thing2.staticThing() + Thing2.staticBase(); + const num2 = thing2.ext() + thing2.thing() + thing2.base(); + + class Thing3 extends Thing2 { + constructor() { + super('', 1, 2); + Math.round(this.base() + this.thing() + this.ext()); + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/genericWildcardBaseClass.js b/tests/baselines/reference/genericWildcardBaseClass.js new file mode 100644 index 0000000000000..89866b2aa00eb --- /dev/null +++ b/tests/baselines/reference/genericWildcardBaseClass.js @@ -0,0 +1,154 @@ +//// [genericWildcardBaseClass.ts] +abstract class BaseClass { + constructor(s: string = '', ...args: any[]) { } + base() { return 0; } + static staticBase() { return ''; } +} + +function extendNoConstructor(Base: T) { + return class ExN extends Base { + ext() { return 0; } + static staticExt() { return ''; } + }; +} + +function extendCompatibleConstructor(Base: T) { + return class ExC extends Base { + constructor(x?: string, ...args: any[]) { + super(x, args); + } + }; +} + +function fails_IncompatibleConstructor(Base: T) { + return class Fail extends Base { + constructor(x?: string, ...args: string[]) { + super(x, args); + } + }; +} + +abstract class ExtClass extends BaseClass { + thing() { return 0; } + static staticThing() { return ''; } +} + +abstract class BadClass extends BaseClass { + constructor(n: number) { + super(); + } +} + +const Thing2 = extendCompatibleConstructor(extendNoConstructor(ExtClass)); +extendCompatibleConstructor(extendNoConstructor(BadClass)); + +const thing2 = new Thing2(); +const thing2arg = new Thing2(''); +const fails_arg = new Thing2(2); + +const str2 = Thing2.staticExt() + Thing2.staticThing() + Thing2.staticBase(); +const num2 = thing2.ext() + thing2.thing() + thing2.base(); + +class Thing3 extends Thing2 { + constructor() { + super('', 1, 2); + Math.round(this.base() + this.thing() + this.ext()); + } +} + + +//// [genericWildcardBaseClass.js] +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var BaseClass = /** @class */ (function () { + function BaseClass(s) { + if (s === void 0) { s = ''; } + var args = []; + for (var _i = 1; _i < arguments.length; _i++) { + args[_i - 1] = arguments[_i]; + } + } + BaseClass.prototype.base = function () { return 0; }; + BaseClass.staticBase = function () { return ''; }; + return BaseClass; +}()); +function extendNoConstructor(Base) { + return /** @class */ (function (_super) { + __extends(ExN, _super); + function ExN() { + return _super !== null && _super.apply(this, arguments) || this; + } + ExN.prototype.ext = function () { return 0; }; + ExN.staticExt = function () { return ''; }; + return ExN; + }(Base)); +} +function extendCompatibleConstructor(Base) { + return /** @class */ (function (_super) { + __extends(ExC, _super); + function ExC(x) { + var args = []; + for (var _i = 1; _i < arguments.length; _i++) { + args[_i - 1] = arguments[_i]; + } + return _super.call(this, x, args) || this; + } + return ExC; + }(Base)); +} +function fails_IncompatibleConstructor(Base) { + return /** @class */ (function (_super) { + __extends(Fail, _super); + function Fail(x) { + var args = []; + for (var _i = 1; _i < arguments.length; _i++) { + args[_i - 1] = arguments[_i]; + } + return _super.call(this, x, args) || this; + } + return Fail; + }(Base)); +} +var ExtClass = /** @class */ (function (_super) { + __extends(ExtClass, _super); + function ExtClass() { + return _super !== null && _super.apply(this, arguments) || this; + } + ExtClass.prototype.thing = function () { return 0; }; + ExtClass.staticThing = function () { return ''; }; + return ExtClass; +}(BaseClass)); +var BadClass = /** @class */ (function (_super) { + __extends(BadClass, _super); + function BadClass(n) { + return _super.call(this) || this; + } + return BadClass; +}(BaseClass)); +var Thing2 = extendCompatibleConstructor(extendNoConstructor(ExtClass)); +extendCompatibleConstructor(extendNoConstructor(BadClass)); +var thing2 = new Thing2(); +var thing2arg = new Thing2(''); +var fails_arg = new Thing2(2); +var str2 = Thing2.staticExt() + Thing2.staticThing() + Thing2.staticBase(); +var num2 = thing2.ext() + thing2.thing() + thing2.base(); +var Thing3 = /** @class */ (function (_super) { + __extends(Thing3, _super); + function Thing3() { + var _this = _super.call(this, '', 1, 2) || this; + Math.round(_this.base() + _this.thing() + _this.ext()); + return _this; + } + return Thing3; +}(Thing2)); diff --git a/tests/baselines/reference/genericWildcardBaseClass.symbols b/tests/baselines/reference/genericWildcardBaseClass.symbols new file mode 100644 index 0000000000000..257d5c0df751b --- /dev/null +++ b/tests/baselines/reference/genericWildcardBaseClass.symbols @@ -0,0 +1,173 @@ +=== tests/cases/compiler/genericWildcardBaseClass.ts === +abstract class BaseClass { +>BaseClass : Symbol(BaseClass, Decl(genericWildcardBaseClass.ts, 0, 0)) + + constructor(s: string = '', ...args: any[]) { } +>s : Symbol(s, Decl(genericWildcardBaseClass.ts, 1, 16)) +>args : Symbol(args, Decl(genericWildcardBaseClass.ts, 1, 31)) + + base() { return 0; } +>base : Symbol(BaseClass.base, Decl(genericWildcardBaseClass.ts, 1, 51)) + + static staticBase() { return ''; } +>staticBase : Symbol(BaseClass.staticBase, Decl(genericWildcardBaseClass.ts, 2, 24)) +} + +function extendNoConstructor(Base: T) { +>extendNoConstructor : Symbol(extendNoConstructor, Decl(genericWildcardBaseClass.ts, 4, 1)) +>T : Symbol(T, Decl(genericWildcardBaseClass.ts, 6, 29)) +>BaseClass : Symbol(BaseClass, Decl(genericWildcardBaseClass.ts, 0, 0)) +>Base : Symbol(Base, Decl(genericWildcardBaseClass.ts, 6, 57)) +>T : Symbol(T, Decl(genericWildcardBaseClass.ts, 6, 29)) + + return class ExN extends Base { +>ExN : Symbol(ExN, Decl(genericWildcardBaseClass.ts, 7, 10)) +>Base : Symbol(Base, Decl(genericWildcardBaseClass.ts, 6, 57)) + + ext() { return 0; } +>ext : Symbol(ExN.ext, Decl(genericWildcardBaseClass.ts, 7, 35)) + + static staticExt() { return ''; } +>staticExt : Symbol(ExN.staticExt, Decl(genericWildcardBaseClass.ts, 8, 27)) + + }; +} + +function extendCompatibleConstructor(Base: T) { +>extendCompatibleConstructor : Symbol(extendCompatibleConstructor, Decl(genericWildcardBaseClass.ts, 11, 1)) +>T : Symbol(T, Decl(genericWildcardBaseClass.ts, 13, 37)) +>BaseClass : Symbol(BaseClass, Decl(genericWildcardBaseClass.ts, 0, 0)) +>Base : Symbol(Base, Decl(genericWildcardBaseClass.ts, 13, 65)) +>T : Symbol(T, Decl(genericWildcardBaseClass.ts, 13, 37)) + + return class ExC extends Base { +>ExC : Symbol(ExC, Decl(genericWildcardBaseClass.ts, 14, 10)) +>Base : Symbol(Base, Decl(genericWildcardBaseClass.ts, 13, 65)) + + constructor(x?: string, ...args: any[]) { +>x : Symbol(x, Decl(genericWildcardBaseClass.ts, 15, 20)) +>args : Symbol(args, Decl(genericWildcardBaseClass.ts, 15, 31)) + + super(x, args); +>super : Symbol(T, Decl(genericWildcardBaseClass.ts, 13, 37)) +>x : Symbol(x, Decl(genericWildcardBaseClass.ts, 15, 20)) +>args : Symbol(args, Decl(genericWildcardBaseClass.ts, 15, 31)) + } + }; +} + +function fails_IncompatibleConstructor(Base: T) { +>fails_IncompatibleConstructor : Symbol(fails_IncompatibleConstructor, Decl(genericWildcardBaseClass.ts, 19, 1)) +>T : Symbol(T, Decl(genericWildcardBaseClass.ts, 21, 39)) +>BaseClass : Symbol(BaseClass, Decl(genericWildcardBaseClass.ts, 0, 0)) +>Base : Symbol(Base, Decl(genericWildcardBaseClass.ts, 21, 67)) +>T : Symbol(T, Decl(genericWildcardBaseClass.ts, 21, 39)) + + return class Fail extends Base { +>Fail : Symbol(Fail, Decl(genericWildcardBaseClass.ts, 22, 10)) +>Base : Symbol(Base, Decl(genericWildcardBaseClass.ts, 21, 67)) + + constructor(x?: string, ...args: string[]) { +>x : Symbol(x, Decl(genericWildcardBaseClass.ts, 23, 20)) +>args : Symbol(args, Decl(genericWildcardBaseClass.ts, 23, 31)) + + super(x, args); +>super : Symbol(T, Decl(genericWildcardBaseClass.ts, 21, 39)) +>x : Symbol(x, Decl(genericWildcardBaseClass.ts, 23, 20)) +>args : Symbol(args, Decl(genericWildcardBaseClass.ts, 23, 31)) + } + }; +} + +abstract class ExtClass extends BaseClass { +>ExtClass : Symbol(ExtClass, Decl(genericWildcardBaseClass.ts, 27, 1)) +>BaseClass : Symbol(BaseClass, Decl(genericWildcardBaseClass.ts, 0, 0)) + + thing() { return 0; } +>thing : Symbol(ExtClass.thing, Decl(genericWildcardBaseClass.ts, 29, 43)) + + static staticThing() { return ''; } +>staticThing : Symbol(ExtClass.staticThing, Decl(genericWildcardBaseClass.ts, 30, 25)) +} + +abstract class BadClass extends BaseClass { +>BadClass : Symbol(BadClass, Decl(genericWildcardBaseClass.ts, 32, 1)) +>BaseClass : Symbol(BaseClass, Decl(genericWildcardBaseClass.ts, 0, 0)) + + constructor(n: number) { +>n : Symbol(n, Decl(genericWildcardBaseClass.ts, 35, 16)) + + super(); +>super : Symbol(BaseClass, Decl(genericWildcardBaseClass.ts, 0, 0)) + } +} + +const Thing2 = extendCompatibleConstructor(extendNoConstructor(ExtClass)); +>Thing2 : Symbol(Thing2, Decl(genericWildcardBaseClass.ts, 40, 5)) +>extendCompatibleConstructor : Symbol(extendCompatibleConstructor, Decl(genericWildcardBaseClass.ts, 11, 1)) +>extendNoConstructor : Symbol(extendNoConstructor, Decl(genericWildcardBaseClass.ts, 4, 1)) +>ExtClass : Symbol(ExtClass, Decl(genericWildcardBaseClass.ts, 27, 1)) + +extendCompatibleConstructor(extendNoConstructor(BadClass)); +>extendCompatibleConstructor : Symbol(extendCompatibleConstructor, Decl(genericWildcardBaseClass.ts, 11, 1)) +>extendNoConstructor : Symbol(extendNoConstructor, Decl(genericWildcardBaseClass.ts, 4, 1)) +>BadClass : Symbol(BadClass, Decl(genericWildcardBaseClass.ts, 32, 1)) + +const thing2 = new Thing2(); +>thing2 : Symbol(thing2, Decl(genericWildcardBaseClass.ts, 43, 5)) +>Thing2 : Symbol(Thing2, Decl(genericWildcardBaseClass.ts, 40, 5)) + +const thing2arg = new Thing2(''); +>thing2arg : Symbol(thing2arg, Decl(genericWildcardBaseClass.ts, 44, 5)) +>Thing2 : Symbol(Thing2, Decl(genericWildcardBaseClass.ts, 40, 5)) + +const fails_arg = new Thing2(2); +>fails_arg : Symbol(fails_arg, Decl(genericWildcardBaseClass.ts, 45, 5)) +>Thing2 : Symbol(Thing2, Decl(genericWildcardBaseClass.ts, 40, 5)) + +const str2 = Thing2.staticExt() + Thing2.staticThing() + Thing2.staticBase(); +>str2 : Symbol(str2, Decl(genericWildcardBaseClass.ts, 47, 5)) +>Thing2.staticExt : Symbol(ExN.staticExt, Decl(genericWildcardBaseClass.ts, 8, 27)) +>Thing2 : Symbol(Thing2, Decl(genericWildcardBaseClass.ts, 40, 5)) +>staticExt : Symbol(ExN.staticExt, Decl(genericWildcardBaseClass.ts, 8, 27)) +>Thing2.staticThing : Symbol(ExtClass.staticThing, Decl(genericWildcardBaseClass.ts, 30, 25)) +>Thing2 : Symbol(Thing2, Decl(genericWildcardBaseClass.ts, 40, 5)) +>staticThing : Symbol(ExtClass.staticThing, Decl(genericWildcardBaseClass.ts, 30, 25)) +>Thing2.staticBase : Symbol(staticBase, Decl(genericWildcardBaseClass.ts, 2, 24), Decl(genericWildcardBaseClass.ts, 2, 24), Decl(genericWildcardBaseClass.ts, 2, 24)) +>Thing2 : Symbol(Thing2, Decl(genericWildcardBaseClass.ts, 40, 5)) +>staticBase : Symbol(staticBase, Decl(genericWildcardBaseClass.ts, 2, 24), Decl(genericWildcardBaseClass.ts, 2, 24), Decl(genericWildcardBaseClass.ts, 2, 24)) + +const num2 = thing2.ext() + thing2.thing() + thing2.base(); +>num2 : Symbol(num2, Decl(genericWildcardBaseClass.ts, 48, 5)) +>thing2.ext : Symbol(ExN.ext, Decl(genericWildcardBaseClass.ts, 7, 35)) +>thing2 : Symbol(thing2, Decl(genericWildcardBaseClass.ts, 43, 5)) +>ext : Symbol(ExN.ext, Decl(genericWildcardBaseClass.ts, 7, 35)) +>thing2.thing : Symbol(ExtClass.thing, Decl(genericWildcardBaseClass.ts, 29, 43)) +>thing2 : Symbol(thing2, Decl(genericWildcardBaseClass.ts, 43, 5)) +>thing : Symbol(ExtClass.thing, Decl(genericWildcardBaseClass.ts, 29, 43)) +>thing2.base : Symbol(BaseClass.base, Decl(genericWildcardBaseClass.ts, 1, 51)) +>thing2 : Symbol(thing2, Decl(genericWildcardBaseClass.ts, 43, 5)) +>base : Symbol(BaseClass.base, Decl(genericWildcardBaseClass.ts, 1, 51)) + +class Thing3 extends Thing2 { +>Thing3 : Symbol(Thing3, Decl(genericWildcardBaseClass.ts, 48, 59)) +>Thing2 : Symbol(Thing2, Decl(genericWildcardBaseClass.ts, 40, 5)) + + constructor() { + super('', 1, 2); + Math.round(this.base() + this.thing() + this.ext()); +>Math.round : Symbol(Math.round, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>round : Symbol(Math.round, Decl(lib.es5.d.ts, --, --)) +>this.base : Symbol(BaseClass.base, Decl(genericWildcardBaseClass.ts, 1, 51)) +>this : Symbol(Thing3, Decl(genericWildcardBaseClass.ts, 48, 59)) +>base : Symbol(BaseClass.base, Decl(genericWildcardBaseClass.ts, 1, 51)) +>this.thing : Symbol(ExtClass.thing, Decl(genericWildcardBaseClass.ts, 29, 43)) +>this : Symbol(Thing3, Decl(genericWildcardBaseClass.ts, 48, 59)) +>thing : Symbol(ExtClass.thing, Decl(genericWildcardBaseClass.ts, 29, 43)) +>this.ext : Symbol(ExN.ext, Decl(genericWildcardBaseClass.ts, 7, 35)) +>this : Symbol(Thing3, Decl(genericWildcardBaseClass.ts, 48, 59)) +>ext : Symbol(ExN.ext, Decl(genericWildcardBaseClass.ts, 7, 35)) + } +} + diff --git a/tests/baselines/reference/genericWildcardBaseClass.types b/tests/baselines/reference/genericWildcardBaseClass.types new file mode 100644 index 0000000000000..1ed6f200345fa --- /dev/null +++ b/tests/baselines/reference/genericWildcardBaseClass.types @@ -0,0 +1,211 @@ +=== tests/cases/compiler/genericWildcardBaseClass.ts === +abstract class BaseClass { +>BaseClass : BaseClass + + constructor(s: string = '', ...args: any[]) { } +>s : string +>'' : "" +>args : any[] + + base() { return 0; } +>base : () => number +>0 : 0 + + static staticBase() { return ''; } +>staticBase : () => string +>'' : "" +} + +function extendNoConstructor(Base: T) { +>extendNoConstructor : (Base: T) => { new (s?: string, ...args: any[]): ExN; prototype: extendNoConstructor.ExN; staticExt(): string; staticBase(): string; } & T +>BaseClass : typeof BaseClass +>Base : T + + return class ExN extends Base { +>class ExN extends Base { ext() { return 0; } static staticExt() { return ''; } } : { new (s?: string, ...args: any[]): ExN; prototype: extendNoConstructor.ExN; staticExt(): string; staticBase(): string; } & T +>ExN : { new (s?: string, ...args: any[]): ExN; prototype: extendNoConstructor.ExN; staticExt(): string; staticBase(): string; } & T +>Base : BaseClass + + ext() { return 0; } +>ext : () => number +>0 : 0 + + static staticExt() { return ''; } +>staticExt : () => string +>'' : "" + + }; +} + +function extendCompatibleConstructor(Base: T) { +>extendCompatibleConstructor : (Base: T) => { new (x?: string, ...args: any[]): ExC; prototype: extendCompatibleConstructor.ExC; staticBase(): string; } & T +>BaseClass : typeof BaseClass +>Base : T + + return class ExC extends Base { +>class ExC extends Base { constructor(x?: string, ...args: any[]) { super(x, args); } } : { new (x?: string, ...args: any[]): ExC; prototype: extendCompatibleConstructor.ExC; staticBase(): string; } & T +>ExC : { new (x?: string, ...args: any[]): ExC; prototype: extendCompatibleConstructor.ExC; staticBase(): string; } & T +>Base : BaseClass + + constructor(x?: string, ...args: any[]) { +>x : string +>args : any[] + + super(x, args); +>super(x, args) : void +>super : T +>x : string +>args : any[] + } + }; +} + +function fails_IncompatibleConstructor(Base: T) { +>fails_IncompatibleConstructor : (Base: T) => { new (x?: string, ...args: string[]): Fail; prototype: fails_IncompatibleConstructor.Fail; staticBase(): string; } & T +>BaseClass : typeof BaseClass +>Base : T + + return class Fail extends Base { +>class Fail extends Base { constructor(x?: string, ...args: string[]) { super(x, args); } } : { new (x?: string, ...args: string[]): Fail; prototype: fails_IncompatibleConstructor.Fail; staticBase(): string; } & T +>Fail : { new (x?: string, ...args: string[]): Fail; prototype: fails_IncompatibleConstructor.Fail; staticBase(): string; } & T +>Base : BaseClass + + constructor(x?: string, ...args: string[]) { +>x : string +>args : string[] + + super(x, args); +>super(x, args) : void +>super : T +>x : string +>args : string[] + } + }; +} + +abstract class ExtClass extends BaseClass { +>ExtClass : ExtClass +>BaseClass : BaseClass + + thing() { return 0; } +>thing : () => number +>0 : 0 + + static staticThing() { return ''; } +>staticThing : () => string +>'' : "" +} + +abstract class BadClass extends BaseClass { +>BadClass : BadClass +>BaseClass : BaseClass + + constructor(n: number) { +>n : number + + super(); +>super() : void +>super : typeof BaseClass + } +} + +const Thing2 = extendCompatibleConstructor(extendNoConstructor(ExtClass)); +>Thing2 : { new (x?: string, ...args: any[]): extendCompatibleConstructor<{ new (s?: string, ...args: any[]): extendNoConstructor.ExN; prototype: extendNoConstructor.ExN; staticExt(): string; staticBase(): string; } & typeof ExtClass>.ExC; prototype: extendCompatibleConstructor.ExC; staticBase(): string; } & { new (s?: string, ...args: any[]): extendNoConstructor.ExN; prototype: extendNoConstructor.ExN; staticExt(): string; staticBase(): string; } & typeof ExtClass +>extendCompatibleConstructor(extendNoConstructor(ExtClass)) : { new (x?: string, ...args: any[]): extendCompatibleConstructor<{ new (s?: string, ...args: any[]): extendNoConstructor.ExN; prototype: extendNoConstructor.ExN; staticExt(): string; staticBase(): string; } & typeof ExtClass>.ExC; prototype: extendCompatibleConstructor.ExC; staticBase(): string; } & { new (s?: string, ...args: any[]): extendNoConstructor.ExN; prototype: extendNoConstructor.ExN; staticExt(): string; staticBase(): string; } & typeof ExtClass +>extendCompatibleConstructor : (Base: T) => { new (x?: string, ...args: any[]): ExC; prototype: extendCompatibleConstructor.ExC; staticBase(): string; } & T +>extendNoConstructor(ExtClass) : { new (s?: string, ...args: any[]): extendNoConstructor.ExN; prototype: extendNoConstructor.ExN; staticExt(): string; staticBase(): string; } & typeof ExtClass +>extendNoConstructor : (Base: T) => { new (s?: string, ...args: any[]): ExN; prototype: extendNoConstructor.ExN; staticExt(): string; staticBase(): string; } & T +>ExtClass : typeof ExtClass + +extendCompatibleConstructor(extendNoConstructor(BadClass)); +>extendCompatibleConstructor(extendNoConstructor(BadClass)) : any +>extendCompatibleConstructor : (Base: T) => { new (x?: string, ...args: any[]): ExC; prototype: extendCompatibleConstructor.ExC; staticBase(): string; } & T +>extendNoConstructor(BadClass) : any +>extendNoConstructor : (Base: T) => { new (s?: string, ...args: any[]): ExN; prototype: extendNoConstructor.ExN; staticExt(): string; staticBase(): string; } & T +>BadClass : typeof BadClass + +const thing2 = new Thing2(); +>thing2 : extendCompatibleConstructor<{ new (s?: string, ...args: any[]): extendNoConstructor.ExN; prototype: extendNoConstructor.ExN; staticExt(): string; staticBase(): string; } & typeof ExtClass>.ExC & extendNoConstructor.ExN & ExtClass +>new Thing2() : extendCompatibleConstructor<{ new (s?: string, ...args: any[]): extendNoConstructor.ExN; prototype: extendNoConstructor.ExN; staticExt(): string; staticBase(): string; } & typeof ExtClass>.ExC & extendNoConstructor.ExN & ExtClass +>Thing2 : { new (x?: string, ...args: any[]): extendCompatibleConstructor<{ new (s?: string, ...args: any[]): extendNoConstructor.ExN; prototype: extendNoConstructor.ExN; staticExt(): string; staticBase(): string; } & typeof ExtClass>.ExC; prototype: extendCompatibleConstructor.ExC; staticBase(): string; } & { new (s?: string, ...args: any[]): extendNoConstructor.ExN; prototype: extendNoConstructor.ExN; staticExt(): string; staticBase(): string; } & typeof ExtClass + +const thing2arg = new Thing2(''); +>thing2arg : extendCompatibleConstructor<{ new (s?: string, ...args: any[]): extendNoConstructor.ExN; prototype: extendNoConstructor.ExN; staticExt(): string; staticBase(): string; } & typeof ExtClass>.ExC & extendNoConstructor.ExN & ExtClass +>new Thing2('') : extendCompatibleConstructor<{ new (s?: string, ...args: any[]): extendNoConstructor.ExN; prototype: extendNoConstructor.ExN; staticExt(): string; staticBase(): string; } & typeof ExtClass>.ExC & extendNoConstructor.ExN & ExtClass +>Thing2 : { new (x?: string, ...args: any[]): extendCompatibleConstructor<{ new (s?: string, ...args: any[]): extendNoConstructor.ExN; prototype: extendNoConstructor.ExN; staticExt(): string; staticBase(): string; } & typeof ExtClass>.ExC; prototype: extendCompatibleConstructor.ExC; staticBase(): string; } & { new (s?: string, ...args: any[]): extendNoConstructor.ExN; prototype: extendNoConstructor.ExN; staticExt(): string; staticBase(): string; } & typeof ExtClass +>'' : "" + +const fails_arg = new Thing2(2); +>fails_arg : any +>new Thing2(2) : any +>Thing2 : { new (x?: string, ...args: any[]): extendCompatibleConstructor<{ new (s?: string, ...args: any[]): extendNoConstructor.ExN; prototype: extendNoConstructor.ExN; staticExt(): string; staticBase(): string; } & typeof ExtClass>.ExC; prototype: extendCompatibleConstructor.ExC; staticBase(): string; } & { new (s?: string, ...args: any[]): extendNoConstructor.ExN; prototype: extendNoConstructor.ExN; staticExt(): string; staticBase(): string; } & typeof ExtClass +>2 : 2 + +const str2 = Thing2.staticExt() + Thing2.staticThing() + Thing2.staticBase(); +>str2 : string +>Thing2.staticExt() + Thing2.staticThing() + Thing2.staticBase() : string +>Thing2.staticExt() + Thing2.staticThing() : string +>Thing2.staticExt() : string +>Thing2.staticExt : () => string +>Thing2 : { new (x?: string, ...args: any[]): extendCompatibleConstructor<{ new (s?: string, ...args: any[]): extendNoConstructor.ExN; prototype: extendNoConstructor.ExN; staticExt(): string; staticBase(): string; } & typeof ExtClass>.ExC; prototype: extendCompatibleConstructor.ExC; staticBase(): string; } & { new (s?: string, ...args: any[]): extendNoConstructor.ExN; prototype: extendNoConstructor.ExN; staticExt(): string; staticBase(): string; } & typeof ExtClass +>staticExt : () => string +>Thing2.staticThing() : string +>Thing2.staticThing : () => string +>Thing2 : { new (x?: string, ...args: any[]): extendCompatibleConstructor<{ new (s?: string, ...args: any[]): extendNoConstructor.ExN; prototype: extendNoConstructor.ExN; staticExt(): string; staticBase(): string; } & typeof ExtClass>.ExC; prototype: extendCompatibleConstructor.ExC; staticBase(): string; } & { new (s?: string, ...args: any[]): extendNoConstructor.ExN; prototype: extendNoConstructor.ExN; staticExt(): string; staticBase(): string; } & typeof ExtClass +>staticThing : () => string +>Thing2.staticBase() : string +>Thing2.staticBase : () => string +>Thing2 : { new (x?: string, ...args: any[]): extendCompatibleConstructor<{ new (s?: string, ...args: any[]): extendNoConstructor.ExN; prototype: extendNoConstructor.ExN; staticExt(): string; staticBase(): string; } & typeof ExtClass>.ExC; prototype: extendCompatibleConstructor.ExC; staticBase(): string; } & { new (s?: string, ...args: any[]): extendNoConstructor.ExN; prototype: extendNoConstructor.ExN; staticExt(): string; staticBase(): string; } & typeof ExtClass +>staticBase : () => string + +const num2 = thing2.ext() + thing2.thing() + thing2.base(); +>num2 : number +>thing2.ext() + thing2.thing() + thing2.base() : number +>thing2.ext() + thing2.thing() : number +>thing2.ext() : number +>thing2.ext : () => number +>thing2 : extendCompatibleConstructor<{ new (s?: string, ...args: any[]): extendNoConstructor.ExN; prototype: extendNoConstructor.ExN; staticExt(): string; staticBase(): string; } & typeof ExtClass>.ExC & extendNoConstructor.ExN & ExtClass +>ext : () => number +>thing2.thing() : number +>thing2.thing : () => number +>thing2 : extendCompatibleConstructor<{ new (s?: string, ...args: any[]): extendNoConstructor.ExN; prototype: extendNoConstructor.ExN; staticExt(): string; staticBase(): string; } & typeof ExtClass>.ExC & extendNoConstructor.ExN & ExtClass +>thing : () => number +>thing2.base() : number +>thing2.base : () => number +>thing2 : extendCompatibleConstructor<{ new (s?: string, ...args: any[]): extendNoConstructor.ExN; prototype: extendNoConstructor.ExN; staticExt(): string; staticBase(): string; } & typeof ExtClass>.ExC & extendNoConstructor.ExN & ExtClass +>base : () => number + +class Thing3 extends Thing2 { +>Thing3 : Thing3 +>Thing2 : extendCompatibleConstructor<{ new (s?: string, ...args: any[]): extendNoConstructor.ExN; prototype: extendNoConstructor.ExN; staticExt(): string; staticBase(): string; } & typeof ExtClass>.ExC & extendNoConstructor.ExN & ExtClass + + constructor() { + super('', 1, 2); +>super('', 1, 2) : void +>super : { new (x?: string, ...args: any[]): extendCompatibleConstructor<{ new (s?: string, ...args: any[]): extendNoConstructor.ExN; prototype: extendNoConstructor.ExN; staticExt(): string; staticBase(): string; } & typeof ExtClass>.ExC; prototype: extendCompatibleConstructor.ExC; staticBase(): string; } & { new (s?: string, ...args: any[]): extendNoConstructor.ExN; prototype: extendNoConstructor.ExN; staticExt(): string; staticBase(): string; } & typeof ExtClass +>'' : "" +>1 : 1 +>2 : 2 + + Math.round(this.base() + this.thing() + this.ext()); +>Math.round(this.base() + this.thing() + this.ext()) : number +>Math.round : (x: number) => number +>Math : Math +>round : (x: number) => number +>this.base() + this.thing() + this.ext() : number +>this.base() + this.thing() : number +>this.base() : number +>this.base : () => number +>this : this +>base : () => number +>this.thing() : number +>this.thing : () => number +>this : this +>thing : () => number +>this.ext() : number +>this.ext : () => number +>this : this +>ext : () => number + } +} + diff --git a/tests/cases/compiler/genericWildcardBaseClass.ts b/tests/cases/compiler/genericWildcardBaseClass.ts new file mode 100644 index 0000000000000..f1ef105c6ffc1 --- /dev/null +++ b/tests/cases/compiler/genericWildcardBaseClass.ts @@ -0,0 +1,56 @@ +abstract class BaseClass { + constructor(s: string = '', ...args: any[]) { } + base() { return 0; } + static staticBase() { return ''; } +} + +function extendNoConstructor(Base: T) { + return class ExN extends Base { + ext() { return 0; } + static staticExt() { return ''; } + }; +} + +function extendCompatibleConstructor(Base: T) { + return class ExC extends Base { + constructor(x?: string, ...args: any[]) { + super(x, args); + } + }; +} + +function fails_IncompatibleConstructor(Base: T) { + return class Fail extends Base { + constructor(x?: string, ...args: string[]) { + super(x, args); + } + }; +} + +abstract class ExtClass extends BaseClass { + thing() { return 0; } + static staticThing() { return ''; } +} + +abstract class BadClass extends BaseClass { + constructor(n: number) { + super(); + } +} + +const Thing2 = extendCompatibleConstructor(extendNoConstructor(ExtClass)); +extendCompatibleConstructor(extendNoConstructor(BadClass)); + +const thing2 = new Thing2(); +const thing2arg = new Thing2(''); +const fails_arg = new Thing2(2); + +const str2 = Thing2.staticExt() + Thing2.staticThing() + Thing2.staticBase(); +const num2 = thing2.ext() + thing2.thing() + thing2.base(); + +class Thing3 extends Thing2 { + constructor() { + super('', 1, 2); + Math.round(this.base() + this.thing() + this.ext()); + } +}