From 07283deac65948e5a1c0dab2d9d4a63c091ebf15 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 29 Aug 2019 14:03:09 -0700 Subject: [PATCH 1/4] Relax the constraints of isValidBaseType to allow base types to be constructor types --- src/compiler/checker.ts | 8 +- .../mixinIntersectionIsValidbaseType.js | 77 +++++++++++++++++++ .../mixinIntersectionIsValidbaseType.symbols | 74 ++++++++++++++++++ .../mixinIntersectionIsValidbaseType.types | 67 ++++++++++++++++ .../mixinIntersectionIsValidbaseType.ts | 27 +++++++ .../TypeScript-React-Starter | 2 +- .../user/create-react-app/create-react-app | 2 +- tests/cases/user/prettier/prettier | 2 +- tests/cases/user/puppeteer/puppeteer | 2 +- tests/cases/user/webpack/webpack | 2 +- 10 files changed, 255 insertions(+), 8 deletions(-) create mode 100644 tests/baselines/reference/mixinIntersectionIsValidbaseType.js create mode 100644 tests/baselines/reference/mixinIntersectionIsValidbaseType.symbols create mode 100644 tests/baselines/reference/mixinIntersectionIsValidbaseType.types create mode 100644 tests/cases/compiler/mixinIntersectionIsValidbaseType.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cfad47b65336f..dab01d357911b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5985,7 +5985,9 @@ namespace ts { function getBaseTypeVariableOfClass(symbol: Symbol) { const baseConstructorType = getBaseConstructorTypeOfClass(getDeclaredTypeOfClassOrInterface(symbol)); - return baseConstructorType.flags & TypeFlags.TypeVariable ? baseConstructorType : undefined; + return baseConstructorType.flags & TypeFlags.TypeVariable ? baseConstructorType : + baseConstructorType.flags & TypeFlags.Intersection ? find((baseConstructorType as IntersectionType).types, t => !!(t.flags & TypeFlags.TypeVariable)) : + undefined; } function getTypeOfFuncClassEnumModule(symbol: Symbol): Type { @@ -6429,11 +6431,11 @@ namespace ts { return true; } - // A valid base type is `any`, any non-generic object type or intersection of non-generic + // A valid base type is `any`, any non-generic object type or intersection of constructor types and non-generic // object types. function isValidBaseType(type: Type): type is BaseType { return !!(type.flags & (TypeFlags.Object | TypeFlags.NonPrimitive | TypeFlags.Any)) && !isGenericMappedType(type) || - !!(type.flags & TypeFlags.Intersection) && every((type).types, isValidBaseType); + !!(type.flags & TypeFlags.Intersection) && every(filter((type).types, t => !isConstructorType(t)), isValidBaseType); } function resolveBaseTypesOfInterface(type: InterfaceType): void { diff --git a/tests/baselines/reference/mixinIntersectionIsValidbaseType.js b/tests/baselines/reference/mixinIntersectionIsValidbaseType.js new file mode 100644 index 0000000000000..ed02aecd53f84 --- /dev/null +++ b/tests/baselines/reference/mixinIntersectionIsValidbaseType.js @@ -0,0 +1,77 @@ +//// [mixinIntersectionIsValidbaseType.ts] +export type Constructor = new (...args: any[]) => T; + +export interface Initable { + init(...args: any[]): void; +} + +/** + * Plain mixin where the superclass must be Initable + */ +export const Serializable = & Initable>( + SuperClass: K +) => { + const LocalMixin = (InnerSuperClass: K) => { + return class SerializableLocal extends InnerSuperClass { + } + }; + let ResultClass = LocalMixin(SuperClass); + return ResultClass; +}; + +const AMixin = & Initable>(SuperClass: K) => { + let SomeHowOkay = class A extends SuperClass { + }; + + let SomeHowNotOkay = class A extends Serializable(SuperClass) { + }; +}; + +//// [mixinIntersectionIsValidbaseType.js] +"use strict"; +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 __()); + }; +})(); +exports.__esModule = true; +/** + * Plain mixin where the superclass must be Initable + */ +exports.Serializable = function (SuperClass) { + var LocalMixin = function (InnerSuperClass) { + return /** @class */ (function (_super) { + __extends(SerializableLocal, _super); + function SerializableLocal() { + return _super !== null && _super.apply(this, arguments) || this; + } + return SerializableLocal; + }(InnerSuperClass)); + }; + var ResultClass = LocalMixin(SuperClass); + return ResultClass; +}; +var AMixin = function (SuperClass) { + var SomeHowOkay = /** @class */ (function (_super) { + __extends(A, _super); + function A() { + return _super !== null && _super.apply(this, arguments) || this; + } + return A; + }(SuperClass)); + var SomeHowNotOkay = /** @class */ (function (_super) { + __extends(A, _super); + function A() { + return _super !== null && _super.apply(this, arguments) || this; + } + return A; + }(exports.Serializable(SuperClass))); +}; diff --git a/tests/baselines/reference/mixinIntersectionIsValidbaseType.symbols b/tests/baselines/reference/mixinIntersectionIsValidbaseType.symbols new file mode 100644 index 0000000000000..69fe63bab23b9 --- /dev/null +++ b/tests/baselines/reference/mixinIntersectionIsValidbaseType.symbols @@ -0,0 +1,74 @@ +=== tests/cases/compiler/mixinIntersectionIsValidbaseType.ts === +export type Constructor = new (...args: any[]) => T; +>Constructor : Symbol(Constructor, Decl(mixinIntersectionIsValidbaseType.ts, 0, 0)) +>T : Symbol(T, Decl(mixinIntersectionIsValidbaseType.ts, 0, 24)) +>args : Symbol(args, Decl(mixinIntersectionIsValidbaseType.ts, 0, 58)) +>T : Symbol(T, Decl(mixinIntersectionIsValidbaseType.ts, 0, 24)) + +export interface Initable { +>Initable : Symbol(Initable, Decl(mixinIntersectionIsValidbaseType.ts, 0, 79)) + + init(...args: any[]): void; +>init : Symbol(Initable.init, Decl(mixinIntersectionIsValidbaseType.ts, 2, 27)) +>args : Symbol(args, Decl(mixinIntersectionIsValidbaseType.ts, 3, 9)) +} + +/** + * Plain mixin where the superclass must be Initable + */ +export const Serializable = & Initable>( +>Serializable : Symbol(Serializable, Decl(mixinIntersectionIsValidbaseType.ts, 9, 12)) +>K : Symbol(K, Decl(mixinIntersectionIsValidbaseType.ts, 9, 29)) +>Constructor : Symbol(Constructor, Decl(mixinIntersectionIsValidbaseType.ts, 0, 0)) +>Initable : Symbol(Initable, Decl(mixinIntersectionIsValidbaseType.ts, 0, 79)) +>Initable : Symbol(Initable, Decl(mixinIntersectionIsValidbaseType.ts, 0, 79)) + + SuperClass: K +>SuperClass : Symbol(SuperClass, Decl(mixinIntersectionIsValidbaseType.ts, 9, 73)) +>K : Symbol(K, Decl(mixinIntersectionIsValidbaseType.ts, 9, 29)) + +) => { + const LocalMixin = (InnerSuperClass: K) => { +>LocalMixin : Symbol(LocalMixin, Decl(mixinIntersectionIsValidbaseType.ts, 12, 9)) +>InnerSuperClass : Symbol(InnerSuperClass, Decl(mixinIntersectionIsValidbaseType.ts, 12, 24)) +>K : Symbol(K, Decl(mixinIntersectionIsValidbaseType.ts, 9, 29)) + + return class SerializableLocal extends InnerSuperClass { +>SerializableLocal : Symbol(SerializableLocal, Decl(mixinIntersectionIsValidbaseType.ts, 13, 14)) +>InnerSuperClass : Symbol(InnerSuperClass, Decl(mixinIntersectionIsValidbaseType.ts, 12, 24)) + } + }; + let ResultClass = LocalMixin(SuperClass); +>ResultClass : Symbol(ResultClass, Decl(mixinIntersectionIsValidbaseType.ts, 16, 7)) +>LocalMixin : Symbol(LocalMixin, Decl(mixinIntersectionIsValidbaseType.ts, 12, 9)) +>SuperClass : Symbol(SuperClass, Decl(mixinIntersectionIsValidbaseType.ts, 9, 73)) + + return ResultClass; +>ResultClass : Symbol(ResultClass, Decl(mixinIntersectionIsValidbaseType.ts, 16, 7)) + +}; + +const AMixin = & Initable>(SuperClass: K) => { +>AMixin : Symbol(AMixin, Decl(mixinIntersectionIsValidbaseType.ts, 20, 5)) +>K : Symbol(K, Decl(mixinIntersectionIsValidbaseType.ts, 20, 16)) +>Constructor : Symbol(Constructor, Decl(mixinIntersectionIsValidbaseType.ts, 0, 0)) +>Initable : Symbol(Initable, Decl(mixinIntersectionIsValidbaseType.ts, 0, 79)) +>Initable : Symbol(Initable, Decl(mixinIntersectionIsValidbaseType.ts, 0, 79)) +>SuperClass : Symbol(SuperClass, Decl(mixinIntersectionIsValidbaseType.ts, 20, 60)) +>K : Symbol(K, Decl(mixinIntersectionIsValidbaseType.ts, 20, 16)) + + let SomeHowOkay = class A extends SuperClass { +>SomeHowOkay : Symbol(SomeHowOkay, Decl(mixinIntersectionIsValidbaseType.ts, 21, 7)) +>A : Symbol(A, Decl(mixinIntersectionIsValidbaseType.ts, 21, 21)) +>SuperClass : Symbol(SuperClass, Decl(mixinIntersectionIsValidbaseType.ts, 20, 60)) + + }; + + let SomeHowNotOkay = class A extends Serializable(SuperClass) { +>SomeHowNotOkay : Symbol(SomeHowNotOkay, Decl(mixinIntersectionIsValidbaseType.ts, 24, 7)) +>A : Symbol(A, Decl(mixinIntersectionIsValidbaseType.ts, 24, 24)) +>Serializable : Symbol(Serializable, Decl(mixinIntersectionIsValidbaseType.ts, 9, 12)) +>SuperClass : Symbol(SuperClass, Decl(mixinIntersectionIsValidbaseType.ts, 20, 60)) + + }; +}; diff --git a/tests/baselines/reference/mixinIntersectionIsValidbaseType.types b/tests/baselines/reference/mixinIntersectionIsValidbaseType.types new file mode 100644 index 0000000000000..c828188dcd68b --- /dev/null +++ b/tests/baselines/reference/mixinIntersectionIsValidbaseType.types @@ -0,0 +1,67 @@ +=== tests/cases/compiler/mixinIntersectionIsValidbaseType.ts === +export type Constructor = new (...args: any[]) => T; +>Constructor : Constructor +>args : any[] + +export interface Initable { + init(...args: any[]): void; +>init : (...args: any[]) => void +>args : any[] +} + +/** + * Plain mixin where the superclass must be Initable + */ +export const Serializable = & Initable>( +>Serializable : & Initable>(SuperClass: K) => { new (...args: any[]): SerializableLocal; prototype: Serializable.SerializableLocal; init(...args: any[]): void; } & K +> & Initable>( SuperClass: K) => { const LocalMixin = (InnerSuperClass: K) => { return class SerializableLocal extends InnerSuperClass { } }; let ResultClass = LocalMixin(SuperClass); return ResultClass;} : & Initable>(SuperClass: K) => { new (...args: any[]): SerializableLocal; prototype: Serializable.SerializableLocal; init(...args: any[]): void; } & K + + SuperClass: K +>SuperClass : K + +) => { + const LocalMixin = (InnerSuperClass: K) => { +>LocalMixin : (InnerSuperClass: K) => { new (...args: any[]): SerializableLocal; prototype: Serializable.SerializableLocal; init(...args: any[]): void; } & K +>(InnerSuperClass: K) => { return class SerializableLocal extends InnerSuperClass { } } : (InnerSuperClass: K) => { new (...args: any[]): SerializableLocal; prototype: Serializable.SerializableLocal; init(...args: any[]): void; } & K +>InnerSuperClass : K + + return class SerializableLocal extends InnerSuperClass { +>class SerializableLocal extends InnerSuperClass { } : { new (...args: any[]): SerializableLocal; prototype: Serializable.SerializableLocal; init(...args: any[]): void; } & K +>SerializableLocal : { new (...args: any[]): SerializableLocal; prototype: Serializable.SerializableLocal; init(...args: any[]): void; } & K +>InnerSuperClass : Initable + } + }; + let ResultClass = LocalMixin(SuperClass); +>ResultClass : { new (...args: any[]): SerializableLocal; prototype: Serializable.SerializableLocal; init(...args: any[]): void; } & K +>LocalMixin(SuperClass) : { new (...args: any[]): SerializableLocal; prototype: Serializable.SerializableLocal; init(...args: any[]): void; } & K +>LocalMixin : (InnerSuperClass: K) => { new (...args: any[]): SerializableLocal; prototype: Serializable.SerializableLocal; init(...args: any[]): void; } & K +>SuperClass : K + + return ResultClass; +>ResultClass : { new (...args: any[]): SerializableLocal; prototype: Serializable.SerializableLocal; init(...args: any[]): void; } & K + +}; + +const AMixin = & Initable>(SuperClass: K) => { +>AMixin : & Initable>(SuperClass: K) => void +> & Initable>(SuperClass: K) => { let SomeHowOkay = class A extends SuperClass { }; let SomeHowNotOkay = class A extends Serializable(SuperClass) { };} : & Initable>(SuperClass: K) => void +>SuperClass : K + + let SomeHowOkay = class A extends SuperClass { +>SomeHowOkay : { new (...args: any[]): A; prototype: AMixin.A; init(...args: any[]): void; } & K +>class A extends SuperClass { } : { new (...args: any[]): A; prototype: AMixin.A; init(...args: any[]): void; } & K +>A : { new (...args: any[]): A; prototype: AMixin.A; init(...args: any[]): void; } & K +>SuperClass : Initable + + }; + + let SomeHowNotOkay = class A extends Serializable(SuperClass) { +>SomeHowNotOkay : { new (...args: any[]): A; prototype: AMixin.A; init: (...args: any[]) => void; } & K +>class A extends Serializable(SuperClass) { } : { new (...args: any[]): A; prototype: AMixin.A; init: (...args: any[]) => void; } & K +>A : { new (...args: any[]): A; prototype: AMixin.A; init: (...args: any[]) => void; } & K +>Serializable(SuperClass) : Serializable.SerializableLocal & Initable +>Serializable : & Initable>(SuperClass: K) => { new (...args: any[]): SerializableLocal; prototype: Serializable.SerializableLocal; init(...args: any[]): void; } & K +>SuperClass : K + + }; +}; diff --git a/tests/cases/compiler/mixinIntersectionIsValidbaseType.ts b/tests/cases/compiler/mixinIntersectionIsValidbaseType.ts new file mode 100644 index 0000000000000..4f446a9db8d08 --- /dev/null +++ b/tests/cases/compiler/mixinIntersectionIsValidbaseType.ts @@ -0,0 +1,27 @@ +export type Constructor = new (...args: any[]) => T; + +export interface Initable { + init(...args: any[]): void; +} + +/** + * Plain mixin where the superclass must be Initable + */ +export const Serializable = & Initable>( + SuperClass: K +) => { + const LocalMixin = (InnerSuperClass: K) => { + return class SerializableLocal extends InnerSuperClass { + } + }; + let ResultClass = LocalMixin(SuperClass); + return ResultClass; +}; + +const AMixin = & Initable>(SuperClass: K) => { + let SomeHowOkay = class A extends SuperClass { + }; + + let SomeHowNotOkay = class A extends Serializable(SuperClass) { + }; +}; \ No newline at end of file diff --git a/tests/cases/user/TypeScript-React-Starter/TypeScript-React-Starter b/tests/cases/user/TypeScript-React-Starter/TypeScript-React-Starter index 19c71f2c6a2b8..bfc20b2f17c02 160000 --- a/tests/cases/user/TypeScript-React-Starter/TypeScript-React-Starter +++ b/tests/cases/user/TypeScript-React-Starter/TypeScript-React-Starter @@ -1 +1 @@ -Subproject commit 19c71f2c6a2b874b1b2bb28a8526b19185b8eece +Subproject commit bfc20b2f17c0206105e2cdd42cd35d79dd03a884 diff --git a/tests/cases/user/create-react-app/create-react-app b/tests/cases/user/create-react-app/create-react-app index 6560858398ddc..437b83f0337a5 160000 --- a/tests/cases/user/create-react-app/create-react-app +++ b/tests/cases/user/create-react-app/create-react-app @@ -1 +1 @@ -Subproject commit 6560858398ddc8d1c5b8d7f51929fcb3d9c3055c +Subproject commit 437b83f0337a5d57ce7dd976d2c3b44cb2037e45 diff --git a/tests/cases/user/prettier/prettier b/tests/cases/user/prettier/prettier index 2f40dba3177c6..2314640485001 160000 --- a/tests/cases/user/prettier/prettier +++ b/tests/cases/user/prettier/prettier @@ -1 +1 @@ -Subproject commit 2f40dba3177c6edd3ceb88b26cdf4718e892a3e5 +Subproject commit 23146404850011972f695fb6bc2b8113c3cffbfc diff --git a/tests/cases/user/puppeteer/puppeteer b/tests/cases/user/puppeteer/puppeteer index cba0f98a2ac7e..b6b29502eb6a7 160000 --- a/tests/cases/user/puppeteer/puppeteer +++ b/tests/cases/user/puppeteer/puppeteer @@ -1 +1 @@ -Subproject commit cba0f98a2ac7edd3c2bffd0ac53185877403da6b +Subproject commit b6b29502eb6a75fe3869806f0e7b27195fe51b0d diff --git a/tests/cases/user/webpack/webpack b/tests/cases/user/webpack/webpack index b16ca509d12fa..743ae6da9a6fc 160000 --- a/tests/cases/user/webpack/webpack +++ b/tests/cases/user/webpack/webpack @@ -1 +1 @@ -Subproject commit b16ca509d12faf36573b65fffcbae50c5b3e7ee3 +Subproject commit 743ae6da9a6fc3b459a7ab3bb250fb07d14f9c5d From 2bd78f1d8a21aaebe63b3be25bc58f4bbabd490a Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 18 Sep 2019 16:27:03 -0700 Subject: [PATCH 2/4] Fix nit --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 314b43dfb54c8..9a50311983e27 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6434,7 +6434,7 @@ namespace ts { // object types. function isValidBaseType(type: Type): type is BaseType { return !!(type.flags & (TypeFlags.Object | TypeFlags.NonPrimitive | TypeFlags.Any)) && !isGenericMappedType(type) || - !!(type.flags & TypeFlags.Intersection) && every(filter((type).types, t => !isConstructorType(t)), isValidBaseType); + !!(type.flags & TypeFlags.Intersection) && every((type).types, t => isConstructorType(t) || isValidBaseType(t)); } function resolveBaseTypesOfInterface(type: InterfaceType): void { From 9edb28c710b6d722608e791be79fd69d0c616765 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 18 Sep 2019 17:40:55 -0700 Subject: [PATCH 3/4] Reduce confusion between isConstructorType and isValidBaseType --- src/compiler/checker.ts | 12 +++++++++--- src/compiler/types.ts | 2 +- .../reference/api/tsserverlibrary.d.ts | 2 +- tests/baselines/reference/api/typescript.d.ts | 2 +- .../baseConstraintOfDecorator.errors.txt | 17 ++++------------- .../reference/baseConstraintOfDecorator.symbols | 1 + .../reference/baseConstraintOfDecorator.types | 8 ++++---- 7 files changed, 21 insertions(+), 23 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9a50311983e27..f4ec9760f4704 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6264,12 +6264,12 @@ namespace ts { } function isConstructorType(type: Type): boolean { - if (isValidBaseType(type) && getSignaturesOfType(type, SignatureKind.Construct).length > 0) { + if (getSignaturesOfType(type, SignatureKind.Construct).length > 0) { return true; } if (type.flags & TypeFlags.TypeVariable) { const constraint = getBaseConstraintOfType(type); - return !!constraint && isValidBaseType(constraint) && isMixinConstructorType(constraint); + return !!constraint && isMixinConstructorType(constraint); } return false; } @@ -6433,8 +6433,14 @@ namespace ts { // A valid base type is `any`, any non-generic object type or intersection of constructor types and non-generic // object types. function isValidBaseType(type: Type): type is BaseType { + if (type.flags & TypeFlags.TypeParameter) { + const constraint = getBaseConstraintOfType(type); + if (constraint) { + return isValidBaseType(constraint); + } + } return !!(type.flags & (TypeFlags.Object | TypeFlags.NonPrimitive | TypeFlags.Any)) && !isGenericMappedType(type) || - !!(type.flags & TypeFlags.Intersection) && every((type).types, t => isConstructorType(t) || isValidBaseType(t)); + !!(type.flags & TypeFlags.Intersection) && every((type).types, isValidBaseType); } function resolveBaseTypesOfInterface(type: InterfaceType): void { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 1828d056405c6..e20518ed1191b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4202,7 +4202,7 @@ namespace ts { } // Object type or intersection of object types - export type BaseType = ObjectType | IntersectionType; + export type BaseType = ObjectType | IntersectionType | TypeVariable; // Also `any` and `object` export interface InterfaceTypeWithDeclaredMembers extends InterfaceType { declaredProperties: Symbol[]; // Declared members diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 58d513b2e0a4b..9ad34c6578148 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2340,7 +2340,7 @@ declare namespace ts { localTypeParameters: TypeParameter[] | undefined; thisType: TypeParameter | undefined; } - export type BaseType = ObjectType | IntersectionType; + export type BaseType = ObjectType | IntersectionType | TypeVariable; export interface InterfaceTypeWithDeclaredMembers extends InterfaceType { declaredProperties: Symbol[]; declaredCallSignatures: Signature[]; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index bc71eaffa373f..417a369fdfca0 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2340,7 +2340,7 @@ declare namespace ts { localTypeParameters: TypeParameter[] | undefined; thisType: TypeParameter | undefined; } - export type BaseType = ObjectType | IntersectionType; + export type BaseType = ObjectType | IntersectionType | TypeVariable; export interface InterfaceTypeWithDeclaredMembers extends InterfaceType { declaredProperties: Symbol[]; declaredCallSignatures: Signature[]; diff --git a/tests/baselines/reference/baseConstraintOfDecorator.errors.txt b/tests/baselines/reference/baseConstraintOfDecorator.errors.txt index 8317e5c55c660..8aad9f6feadb5 100644 --- a/tests/baselines/reference/baseConstraintOfDecorator.errors.txt +++ b/tests/baselines/reference/baseConstraintOfDecorator.errors.txt @@ -1,11 +1,10 @@ tests/cases/compiler/baseConstraintOfDecorator.ts(2,5): error TS2322: Type 'typeof decoratorFunc' is not assignable to type 'TFunction'. 'typeof decoratorFunc' is assignable to the constraint of type 'TFunction', but 'TFunction' could be instantiated with a different subtype of constraint '{}'. tests/cases/compiler/baseConstraintOfDecorator.ts(2,40): error TS2507: Type 'TFunction' is not a constructor function type. -tests/cases/compiler/baseConstraintOfDecorator.ts(12,5): error TS2322: Type 'typeof decoratorFunc' is not assignable to type 'TFunction'. -tests/cases/compiler/baseConstraintOfDecorator.ts(12,40): error TS2507: Type 'TFunction' is not a constructor function type. +tests/cases/compiler/baseConstraintOfDecorator.ts(12,18): error TS2545: A mixin class must have a constructor with a single rest parameter of type 'any[]'. -==== tests/cases/compiler/baseConstraintOfDecorator.ts (4 errors) ==== +==== tests/cases/compiler/baseConstraintOfDecorator.ts (3 errors) ==== export function classExtender(superClass: TFunction, _instanceModifier: (instance: any, args: any[]) => void): TFunction { return class decoratorFunc extends superClass { ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -29,20 +28,12 @@ tests/cases/compiler/baseConstraintOfDecorator.ts(12,40): error TS2507: Type 'TF class MyClass { private x; } export function classExtender2 MyClass>(superClass: TFunction, _instanceModifier: (instance: any, args: any[]) => void): TFunction { return class decoratorFunc extends superClass { - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - ~~~~~~~~~~ -!!! error TS2507: Type 'TFunction' is not a constructor function type. -!!! related TS2735 tests/cases/compiler/baseConstraintOfDecorator.ts:11:32: Did you mean for 'TFunction' to be constrained to type 'new (...args: any[]) => MyClass'? + ~~~~~~~~~~~~~ +!!! error TS2545: A mixin class must have a constructor with a single rest parameter of type 'any[]'. constructor(...args: any[]) { - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ super(...args); - ~~~~~~~~~~~~~~~~~~~~~~~~~~~ _instanceModifier(this, args); - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ } - ~~~~~~~~~ }; - ~~~~~~ -!!! error TS2322: Type 'typeof decoratorFunc' is not assignable to type 'TFunction'. } \ No newline at end of file diff --git a/tests/baselines/reference/baseConstraintOfDecorator.symbols b/tests/baselines/reference/baseConstraintOfDecorator.symbols index 9048d7ed568e8..0a1dad239afc2 100644 --- a/tests/baselines/reference/baseConstraintOfDecorator.symbols +++ b/tests/baselines/reference/baseConstraintOfDecorator.symbols @@ -51,6 +51,7 @@ export function classExtender2 MyCl >args : Symbol(args, Decl(baseConstraintOfDecorator.ts, 12, 20)) super(...args); +>super : Symbol(TFunction, Decl(baseConstraintOfDecorator.ts, 10, 31)) >args : Symbol(args, Decl(baseConstraintOfDecorator.ts, 12, 20)) _instanceModifier(this, args); diff --git a/tests/baselines/reference/baseConstraintOfDecorator.types b/tests/baselines/reference/baseConstraintOfDecorator.types index a5a277c5d2a68..78c77ee78a2ab 100644 --- a/tests/baselines/reference/baseConstraintOfDecorator.types +++ b/tests/baselines/reference/baseConstraintOfDecorator.types @@ -42,16 +42,16 @@ export function classExtender2 MyCl >args : any[] return class decoratorFunc extends superClass { ->class decoratorFunc extends superClass { constructor(...args: any[]) { super(...args); _instanceModifier(this, args); } } : typeof decoratorFunc ->decoratorFunc : typeof decoratorFunc ->superClass : TFunction +>class decoratorFunc extends superClass { constructor(...args: any[]) { super(...args); _instanceModifier(this, args); } } : { new (...args: any[]): decoratorFunc; prototype: classExtender2.decoratorFunc; } & TFunction +>decoratorFunc : { new (...args: any[]): decoratorFunc; prototype: classExtender2.decoratorFunc; } & TFunction +>superClass : MyClass constructor(...args: any[]) { >args : any[] super(...args); >super(...args) : void ->super : any +>super : TFunction >...args : any >args : any[] From fd6472c8d32a068066ad1f9f659a1bb9edab8903 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 18 Sep 2019 17:43:59 -0700 Subject: [PATCH 4/4] Update comment --- src/compiler/checker.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f4ec9760f4704..c2cd43ec2a2cc 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6430,8 +6430,7 @@ namespace ts { return true; } - // A valid base type is `any`, any non-generic object type or intersection of constructor types and non-generic - // object types. + // A valid base type is `any`, an object type or intersection of object types. function isValidBaseType(type: Type): type is BaseType { if (type.flags & TypeFlags.TypeParameter) { const constraint = getBaseConstraintOfType(type); @@ -6439,6 +6438,8 @@ namespace ts { return isValidBaseType(constraint); } } + // TODO: Given that we allow type parmeters here now, is this `!isGenericMappedType(type)` check really needed? + // There's no reason a `T` should be allowed while a `Readonly` should not. return !!(type.flags & (TypeFlags.Object | TypeFlags.NonPrimitive | TypeFlags.Any)) && !isGenericMappedType(type) || !!(type.flags & TypeFlags.Intersection) && every((type).types, isValidBaseType); }