Is there a way to nest classes in TypeScript. E.g. I'd like to use them like:
var foo = new Foo();
var bar = new Foo.Bar();
Is there a way to nest classes in TypeScript. E.g. I'd like to use them like:
var foo = new Foo();
var bar = new Foo.Bar();
In modern TypeScript we have class expressions which you can use to create a nested class. For example you can do the following :
class Foo {
static Bar = class {
}
}
// works!
var foo = new Foo();
var bar = new Foo.Bar();
Here is a more complex use case using class expressions.
It allows the inner class to access the private
members of the outer class.
class classX {
private y: number = 0;
public getY(): number { return this.y; }
public utilities = new class {
constructor(public superThis: classX) {
}
public testSetOuterPrivate(target: number) {
this.superThis.y = target;
}
}(this);
}
const x1: classX = new classX();
alert(x1.getY());
x1.utilities.testSetOuterPrivate(4);
alert(x1.getY());
I couldn't get this to work with exported classes without receiving a compile error, instead I used namespaces:
namespace MyNamespace {
export class Foo { }
}
namespace MyNamespace.Foo {
export class Bar { }
}
If you're in the context of a type declaration file, you can do this by mixing classes and namespaces:
// foo.d.ts
declare class Foo {
constructor();
fooMethod(): any;
}
declare namespace Foo {
class Bar {
constructor();
barMethod(): any;
}
}
// ...elsewhere
const foo = new Foo();
const bar = new Foo.Bar();
This answer is about a seamless implementation of nested classes.
Foo.Bar
(static)Defining the static nested class Foo.Bar
can be done in the following two ways.
Bar
inside the class Foo
. The type is declared in a declare namespace
declaration.Bar
inside a Foo
namespace with the export
keyword.Bar
inside class Foo
All of the classes are implemented in one block, namely in that of the Foo
class declaration.
class Foo {
static Bar = class { }
}
declare namespace Foo {
type Bar = typeof Foo.Bar.prototype
}
let bar: Foo.Bar = new Foo.Bar()
Bar
inside namespace Foo
For static classes, the following implementation might be more elegant to some. The downside is that this method does not work with non-static nested classes.
class Foo { }
namespace Foo {
export class Bar { }
}
let bar: Foo.Bar = new Foo.Bar()
Foo.prototype.Bar
(non-static)To create a seamless non-static nested class, one may use prototype
to indicate that the nested class is not static.
class Foo {
Bar = class { }
}
declare namespace Foo.prototype {
type Bar = typeof Foo.prototype.Bar.prototype
}
let foo: Foo = new Foo()
let bar: Foo.prototype.Bar = new foo.Bar()
Note: the call new Foo.prototype.Bar()
doesn't work, although it is valid Typescript even without type declaration.
I Hope this can be helpful
Able to:
Use Case
export interface Constructor<T> {
new(...args: any[]): T;
}
export interface Testable {
test(): void;
}
export function LogClassName<T>() {
return function (target: Constructor<T>) {
console.log(target.name);
}
}
class OuterClass {
private _prop1: string;
constructor(prop1: string) {
this._prop1 = prop1;
}
private method1(): string {
return 'private outer method 1';
}
public InnerClass = (
() => {
const $outer = this;
@LogClassName()
class InnerClass implements Testable {
private readonly _$outer: typeof $outer;
constructor(public innerProp1: string) {
this._$outer = $outer;
}
public test(): void {
console.log('test()');
}
public outerPrivateProp1(): string {
return this._$outer._prop1;
}
public outerPrivateMethod1(): string {
return this._$outer.method1();
}
}
return InnerClass;
}
)();
}
const outer = new OuterClass('outer prop 1')
const inner = new outer.InnerClass('inner prop 1');
console.log(inner instanceof outer.InnerClass); // true
console.log(inner.innerProp1); // inner prop 1
console.log(inner.outerPrivateProp1()); // outer prop 1
console.log(inner.outerPrivateMethod1()); // private outer method 1