From a7b066fe04e4725ee18798cbb520de4d61044eb4 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 22 Mar 2018 15:02:54 -0700 Subject: [PATCH] Allow local class declarations to be returned as mixins (#22807) --- src/compiler/checker.ts | 4 +- ...clarationEmitLocalClassDeclarationMixin.js | 109 ++++++++++++++++++ ...tionEmitLocalClassDeclarationMixin.symbols | 73 ++++++++++++ ...rationEmitLocalClassDeclarationMixin.types | 79 +++++++++++++ ...clarationEmitLocalClassDeclarationMixin.ts | 30 +++++ 5 files changed, 294 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/declarationEmitLocalClassDeclarationMixin.js create mode 100644 tests/baselines/reference/declarationEmitLocalClassDeclarationMixin.symbols create mode 100644 tests/baselines/reference/declarationEmitLocalClassDeclarationMixin.types create mode 100644 tests/cases/compiler/declarationEmitLocalClassDeclarationMixin.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 929dabca6fbcb..968d7c6826ee6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3185,7 +3185,9 @@ namespace ts { } else if (context.flags & NodeBuilderFlags.WriteClassExpressionAsTypeLiteral && type.symbol.valueDeclaration && - type.symbol.valueDeclaration.kind === SyntaxKind.ClassExpression) { + isClassLike(type.symbol.valueDeclaration) && + !isValueSymbolAccessible(type.symbol, context.enclosingDeclaration) + ) { return createAnonymousTypeNode(type); } else { diff --git a/tests/baselines/reference/declarationEmitLocalClassDeclarationMixin.js b/tests/baselines/reference/declarationEmitLocalClassDeclarationMixin.js new file mode 100644 index 0000000000000..352520ff575e6 --- /dev/null +++ b/tests/baselines/reference/declarationEmitLocalClassDeclarationMixin.js @@ -0,0 +1,109 @@ +//// [declarationEmitLocalClassDeclarationMixin.ts] +interface Constructor { new (...args: any[]): C; } + +function mixin>(Base: B) { + class PrivateMixed extends Base { + bar = 2; + } + return PrivateMixed; +} + +export class Unmixed { + foo = 1; +} + +export const Mixed = mixin(Unmixed); + +function Filter>(ctor: C) { + abstract class FilterMixin extends ctor { + abstract match(path: string): boolean; + // other concrete methods, fields, constructor + thing = 12; + } + return FilterMixin; +} + +export class FilteredThing extends Filter(Unmixed) { + match(path: string) { + return false; + } +} + + +//// [declarationEmitLocalClassDeclarationMixin.js] +"use strict"; +var __extends = (this && this.__extends) || (function () { + var 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 function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +exports.__esModule = true; +function mixin(Base) { + var PrivateMixed = /** @class */ (function (_super) { + __extends(PrivateMixed, _super); + function PrivateMixed() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.bar = 2; + return _this; + } + return PrivateMixed; + }(Base)); + return PrivateMixed; +} +var Unmixed = /** @class */ (function () { + function Unmixed() { + this.foo = 1; + } + return Unmixed; +}()); +exports.Unmixed = Unmixed; +exports.Mixed = mixin(Unmixed); +function Filter(ctor) { + var FilterMixin = /** @class */ (function (_super) { + __extends(FilterMixin, _super); + function FilterMixin() { + var _this = _super !== null && _super.apply(this, arguments) || this; + // other concrete methods, fields, constructor + _this.thing = 12; + return _this; + } + return FilterMixin; + }(ctor)); + return FilterMixin; +} +var FilteredThing = /** @class */ (function (_super) { + __extends(FilteredThing, _super); + function FilteredThing() { + return _super !== null && _super.apply(this, arguments) || this; + } + FilteredThing.prototype.match = function (path) { + return false; + }; + return FilteredThing; +}(Filter(Unmixed))); +exports.FilteredThing = FilteredThing; + + +//// [declarationEmitLocalClassDeclarationMixin.d.ts] +export declare class Unmixed { + foo: number; +} +export declare const Mixed: { + new (...args: any[]): { + bar: number; + }; +} & typeof Unmixed; +declare const FilteredThing_base: { + new (...args: any[]): { + match(path: string): boolean; + thing: number; + }; +} & typeof Unmixed; +export declare class FilteredThing extends FilteredThing_base { + match(path: string): boolean; +} diff --git a/tests/baselines/reference/declarationEmitLocalClassDeclarationMixin.symbols b/tests/baselines/reference/declarationEmitLocalClassDeclarationMixin.symbols new file mode 100644 index 0000000000000..453c2d3e73d52 --- /dev/null +++ b/tests/baselines/reference/declarationEmitLocalClassDeclarationMixin.symbols @@ -0,0 +1,73 @@ +=== tests/cases/compiler/declarationEmitLocalClassDeclarationMixin.ts === +interface Constructor { new (...args: any[]): C; } +>Constructor : Symbol(Constructor, Decl(declarationEmitLocalClassDeclarationMixin.ts, 0, 0)) +>C : Symbol(C, Decl(declarationEmitLocalClassDeclarationMixin.ts, 0, 22)) +>args : Symbol(args, Decl(declarationEmitLocalClassDeclarationMixin.ts, 0, 32)) +>C : Symbol(C, Decl(declarationEmitLocalClassDeclarationMixin.ts, 0, 22)) + +function mixin>(Base: B) { +>mixin : Symbol(mixin, Decl(declarationEmitLocalClassDeclarationMixin.ts, 0, 53)) +>B : Symbol(B, Decl(declarationEmitLocalClassDeclarationMixin.ts, 2, 15)) +>Constructor : Symbol(Constructor, Decl(declarationEmitLocalClassDeclarationMixin.ts, 0, 0)) +>Base : Symbol(Base, Decl(declarationEmitLocalClassDeclarationMixin.ts, 2, 42)) +>B : Symbol(B, Decl(declarationEmitLocalClassDeclarationMixin.ts, 2, 15)) + + class PrivateMixed extends Base { +>PrivateMixed : Symbol(PrivateMixed, Decl(declarationEmitLocalClassDeclarationMixin.ts, 2, 52)) +>Base : Symbol(Base, Decl(declarationEmitLocalClassDeclarationMixin.ts, 2, 42)) + + bar = 2; +>bar : Symbol(PrivateMixed.bar, Decl(declarationEmitLocalClassDeclarationMixin.ts, 3, 37)) + } + return PrivateMixed; +>PrivateMixed : Symbol(PrivateMixed, Decl(declarationEmitLocalClassDeclarationMixin.ts, 2, 52)) +} + +export class Unmixed { +>Unmixed : Symbol(Unmixed, Decl(declarationEmitLocalClassDeclarationMixin.ts, 7, 1)) + + foo = 1; +>foo : Symbol(Unmixed.foo, Decl(declarationEmitLocalClassDeclarationMixin.ts, 9, 22)) +} + +export const Mixed = mixin(Unmixed); +>Mixed : Symbol(Mixed, Decl(declarationEmitLocalClassDeclarationMixin.ts, 13, 12)) +>mixin : Symbol(mixin, Decl(declarationEmitLocalClassDeclarationMixin.ts, 0, 53)) +>Unmixed : Symbol(Unmixed, Decl(declarationEmitLocalClassDeclarationMixin.ts, 7, 1)) + +function Filter>(ctor: C) { +>Filter : Symbol(Filter, Decl(declarationEmitLocalClassDeclarationMixin.ts, 13, 36)) +>C : Symbol(C, Decl(declarationEmitLocalClassDeclarationMixin.ts, 15, 16)) +>Constructor : Symbol(Constructor, Decl(declarationEmitLocalClassDeclarationMixin.ts, 0, 0)) +>ctor : Symbol(ctor, Decl(declarationEmitLocalClassDeclarationMixin.ts, 15, 43)) +>C : Symbol(C, Decl(declarationEmitLocalClassDeclarationMixin.ts, 15, 16)) + + abstract class FilterMixin extends ctor { +>FilterMixin : Symbol(FilterMixin, Decl(declarationEmitLocalClassDeclarationMixin.ts, 15, 53)) +>ctor : Symbol(ctor, Decl(declarationEmitLocalClassDeclarationMixin.ts, 15, 43)) + + abstract match(path: string): boolean; +>match : Symbol(FilterMixin.match, Decl(declarationEmitLocalClassDeclarationMixin.ts, 16, 45)) +>path : Symbol(path, Decl(declarationEmitLocalClassDeclarationMixin.ts, 17, 23)) + + // other concrete methods, fields, constructor + thing = 12; +>thing : Symbol(FilterMixin.thing, Decl(declarationEmitLocalClassDeclarationMixin.ts, 17, 46)) + } + return FilterMixin; +>FilterMixin : Symbol(FilterMixin, Decl(declarationEmitLocalClassDeclarationMixin.ts, 15, 53)) +} + +export class FilteredThing extends Filter(Unmixed) { +>FilteredThing : Symbol(FilteredThing, Decl(declarationEmitLocalClassDeclarationMixin.ts, 22, 1)) +>Filter : Symbol(Filter, Decl(declarationEmitLocalClassDeclarationMixin.ts, 13, 36)) +>Unmixed : Symbol(Unmixed, Decl(declarationEmitLocalClassDeclarationMixin.ts, 7, 1)) + + match(path: string) { +>match : Symbol(FilteredThing.match, Decl(declarationEmitLocalClassDeclarationMixin.ts, 24, 52)) +>path : Symbol(path, Decl(declarationEmitLocalClassDeclarationMixin.ts, 25, 10)) + + return false; + } +} + diff --git a/tests/baselines/reference/declarationEmitLocalClassDeclarationMixin.types b/tests/baselines/reference/declarationEmitLocalClassDeclarationMixin.types new file mode 100644 index 0000000000000..3129e3fe438dd --- /dev/null +++ b/tests/baselines/reference/declarationEmitLocalClassDeclarationMixin.types @@ -0,0 +1,79 @@ +=== tests/cases/compiler/declarationEmitLocalClassDeclarationMixin.ts === +interface Constructor { new (...args: any[]): C; } +>Constructor : Constructor +>C : C +>args : any[] +>C : C + +function mixin>(Base: B) { +>mixin : >(Base: B) => { new (...args: any[]): PrivateMixed; prototype: mixin.PrivateMixed; } & B +>B : B +>Constructor : Constructor +>Base : B +>B : B + + class PrivateMixed extends Base { +>PrivateMixed : PrivateMixed +>Base : {} + + bar = 2; +>bar : number +>2 : 2 + } + return PrivateMixed; +>PrivateMixed : { new (...args: any[]): PrivateMixed; prototype: mixin.PrivateMixed; } & B +} + +export class Unmixed { +>Unmixed : Unmixed + + foo = 1; +>foo : number +>1 : 1 +} + +export const Mixed = mixin(Unmixed); +>Mixed : { new (...args: any[]): mixin.PrivateMixed; prototype: mixin.PrivateMixed; } & typeof Unmixed +>mixin(Unmixed) : { new (...args: any[]): mixin.PrivateMixed; prototype: mixin.PrivateMixed; } & typeof Unmixed +>mixin : >(Base: B) => { new (...args: any[]): PrivateMixed; prototype: mixin.PrivateMixed; } & B +>Unmixed : typeof Unmixed + +function Filter>(ctor: C) { +>Filter : >(ctor: C) => { new (...args: any[]): FilterMixin; prototype: Filter.FilterMixin; } & C +>C : C +>Constructor : Constructor +>ctor : C +>C : C + + abstract class FilterMixin extends ctor { +>FilterMixin : FilterMixin +>ctor : {} + + abstract match(path: string): boolean; +>match : (path: string) => boolean +>path : string + + // other concrete methods, fields, constructor + thing = 12; +>thing : number +>12 : 12 + } + return FilterMixin; +>FilterMixin : { new (...args: any[]): FilterMixin; prototype: Filter.FilterMixin; } & C +} + +export class FilteredThing extends Filter(Unmixed) { +>FilteredThing : FilteredThing +>Filter(Unmixed) : Filter.FilterMixin & Unmixed +>Filter : >(ctor: C) => { new (...args: any[]): FilterMixin; prototype: Filter.FilterMixin; } & C +>Unmixed : typeof Unmixed + + match(path: string) { +>match : (path: string) => boolean +>path : string + + return false; +>false : false + } +} + diff --git a/tests/cases/compiler/declarationEmitLocalClassDeclarationMixin.ts b/tests/cases/compiler/declarationEmitLocalClassDeclarationMixin.ts new file mode 100644 index 0000000000000..0f15614ff5b23 --- /dev/null +++ b/tests/cases/compiler/declarationEmitLocalClassDeclarationMixin.ts @@ -0,0 +1,30 @@ +// @declaration: true +interface Constructor { new (...args: any[]): C; } + +function mixin>(Base: B) { + class PrivateMixed extends Base { + bar = 2; + } + return PrivateMixed; +} + +export class Unmixed { + foo = 1; +} + +export const Mixed = mixin(Unmixed); + +function Filter>(ctor: C) { + abstract class FilterMixin extends ctor { + abstract match(path: string): boolean; + // other concrete methods, fields, constructor + thing = 12; + } + return FilterMixin; +} + +export class FilteredThing extends Filter(Unmixed) { + match(path: string) { + return false; + } +}