Skip to content

Commit

Permalink
Allow local class declarations to be returned as mixins (#22807)
Browse files Browse the repository at this point in the history
  • Loading branch information
weswigham authored Mar 22, 2018
1 parent 874dd25 commit a7b066f
Show file tree
Hide file tree
Showing 5 changed files with 294 additions and 1 deletion.
4 changes: 3 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
109 changes: 109 additions & 0 deletions tests/baselines/reference/declarationEmitLocalClassDeclarationMixin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
//// [declarationEmitLocalClassDeclarationMixin.ts]
interface Constructor<C> { new (...args: any[]): C; }

function mixin<B extends Constructor<{}>>(Base: B) {
class PrivateMixed extends Base {
bar = 2;
}
return PrivateMixed;
}

export class Unmixed {
foo = 1;
}

export const Mixed = mixin(Unmixed);

function Filter<C extends Constructor<{}>>(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;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
=== tests/cases/compiler/declarationEmitLocalClassDeclarationMixin.ts ===
interface Constructor<C> { 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<B extends Constructor<{}>>(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<C extends Constructor<{}>>(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;
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
=== tests/cases/compiler/declarationEmitLocalClassDeclarationMixin.ts ===
interface Constructor<C> { new (...args: any[]): C; }
>Constructor : Constructor<C>
>C : C
>args : any[]
>C : C

function mixin<B extends Constructor<{}>>(Base: B) {
>mixin : <B extends Constructor<{}>>(Base: B) => { new (...args: any[]): PrivateMixed; prototype: mixin<any>.PrivateMixed; } & B
>B : B
>Constructor : Constructor<C>
>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<any>.PrivateMixed; } & B
}

export class Unmixed {
>Unmixed : Unmixed

foo = 1;
>foo : number
>1 : 1
}

export const Mixed = mixin(Unmixed);
>Mixed : { new (...args: any[]): mixin<typeof Unmixed>.PrivateMixed; prototype: mixin<any>.PrivateMixed; } & typeof Unmixed
>mixin(Unmixed) : { new (...args: any[]): mixin<typeof Unmixed>.PrivateMixed; prototype: mixin<any>.PrivateMixed; } & typeof Unmixed
>mixin : <B extends Constructor<{}>>(Base: B) => { new (...args: any[]): PrivateMixed; prototype: mixin<any>.PrivateMixed; } & B
>Unmixed : typeof Unmixed

function Filter<C extends Constructor<{}>>(ctor: C) {
>Filter : <C extends Constructor<{}>>(ctor: C) => { new (...args: any[]): FilterMixin; prototype: Filter<any>.FilterMixin; } & C
>C : C
>Constructor : Constructor<C>
>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<any>.FilterMixin; } & C
}

export class FilteredThing extends Filter(Unmixed) {
>FilteredThing : FilteredThing
>Filter(Unmixed) : Filter<typeof Unmixed>.FilterMixin & Unmixed
>Filter : <C extends Constructor<{}>>(ctor: C) => { new (...args: any[]): FilterMixin; prototype: Filter<any>.FilterMixin; } & C
>Unmixed : typeof Unmixed

match(path: string) {
>match : (path: string) => boolean
>path : string

return false;
>false : false
}
}

30 changes: 30 additions & 0 deletions tests/cases/compiler/declarationEmitLocalClassDeclarationMixin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// @declaration: true
interface Constructor<C> { new (...args: any[]): C; }

function mixin<B extends Constructor<{}>>(Base: B) {
class PrivateMixed extends Base {
bar = 2;
}
return PrivateMixed;
}

export class Unmixed {
foo = 1;
}

export const Mixed = mixin(Unmixed);

function Filter<C extends Constructor<{}>>(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;
}
}

0 comments on commit a7b066f

Please sign in to comment.