54

I'm interested in defining an existing framework (openlayers.d.ts) but cannot figure out how to express the fact that OpenLayers.Layer is both a class and a namespace for OpenLayers.Layer.Markers. I believe this makes Markers a nested class of Layer.

usage:

l = new OpenLayers.Layer(...); // this is a base class, never do this
m = new OpenLayers.Layer.Markers(...);

How would you declare both the Layer and Markers class in typescript?

basarat
  • 261,912
  • 58
  • 460
  • 511
Corey Alix
  • 2,694
  • 2
  • 27
  • 38
  • This appears to be a duplicate of the unanswered question at http://stackoverflow.com/questions/12799433/typescript-module-and-class-simultaneously – Corey Alix Nov 21 '12 at 14:17
  • 2
    Not talking about how you would want to model this from a *declaration* perspective but TypeScript does now have nested class support : http://stackoverflow.com/a/32494175/390330 – basarat Sep 10 '15 at 06:00

6 Answers6

55

This seems like it has been fixed in versions 0.9.1.1 and later. You just have to create a module with the same name as the class where you want to nest types, and put your nested types in it.

More concretely, this is how you do it:

declare module a
{
    class b
    {
    }

    module b
    {
        class c
        {
        }
    }
}

var myB = new a.b();
var myC = new a.b.c();

This works as well when nesting types in typescript code with the export keyword:

export module a
{
    export class b
    {
    }

    export module b
    {
        export enum c
        {
            C1 = 1,
            C2 = 2,
            C3 = 3,
        }
    }
}

As mentioned by the user @recursive in the comments below, the order of declaration is important. So the class definition must be located before the module with the nested types.

sboisse
  • 4,860
  • 3
  • 37
  • 48
  • 7
    I couldn't get this to work, but then I realized that the order is important. This only seems to work if you declare the class first, and then the module. It does not work the other way around. – recursive Oct 16 '14 at 18:33
  • How do you do this with an Angular project using Typescript? Modules use the decorator: @NgModule – Kevin LeStarge Sep 08 '17 at 18:20
  • Anyway to achieve this without pyramid of declarations? – jayarjo Mar 20 '18 at 14:07
  • @jayarjo not that I am aware of. Pyramids of declarations would be the only way to do it. – sboisse Mar 27 '18 at 14:39
15

You can use namespace/class merging to get an equivalent effect to nested classes. This example is adapted from Chapter 1 of Pro TypeScript.

You perform merging with a class and a namespace, or with a function and a namespace.

In all cases, the namespace must appear after the class or function for the merge to work.

Adapted example:

class Outer {

}

namespace Outer {
    export class Mid {

    }

    export module Mid {
        export class Inner {

        }
    }
}

var a = new Outer();
var b = new Outer.Mid();
var x = new Outer.Mid.Inner();
Fenton
  • 241,084
  • 71
  • 387
  • 401
  • how to call method from Mid class to Outer class? – cracker Jun 02 '16 at 07:20
  • Should the whole thing reside in single file? Or can we have implementations separate from declarations? – jayarjo Mar 20 '18 at 14:08
  • @jayarjo all namespaces in the same common root contribute to a single name. So you can have several files all adding to the same namespace if you wish. – Fenton Mar 21 '18 at 08:25
8

As of 2016, I guess this is easier:

class A {
    static B = class { }
}

var a = new A();
var b = new A.B();
Marcelo Glasberg
  • 29,013
  • 23
  • 109
  • 133
  • 2
    I get this compile error `Initializers are not allowed in ambient contexts` with atom latest ts plugin – nkint Apr 20 '16 at 14:29
  • How can you refer to the `this` of `A` within a method of `B` without passing it as an argument explicitly? Or is that impossible since `B` is static? My current solution is to just have a `bFactory` method on `A` where I can access `A` members and pass them to the `B` constructor explicitly, but I was hoping there was a slicker way to do it. – Kyle Pittman Jul 13 '16 at 13:22
  • @Monkpit: `this` refers to an `a` object, and not to the `A` class. You can instantiate many `a`s, and many `b`s, each with its own `this`. So how could a particular `b` know which `a` you would like it to access? – Marcelo Glasberg Jul 16 '16 at 19:24
  • @MarcG As class `A` is not static, any instance of an `a` object contains the definition of a static class `B`, no? Otherwise, why would `B` be defined in the scope of `A` at all? I'm probably totally missing the point here... – Kyle Pittman Jul 18 '16 at 15:19
  • @Monkpit: any `a` instance contains the DEFINITION of the `B` class, and as such can instantiate `b`s. But you may instantiate `b`s without instantiating `a`s, as you can see in my example `var b = new A.B();`. – Marcelo Glasberg Jul 22 '16 at 01:55
  • vscode with latest everything also gives `Initializers are not allowed in ambient contexts` with this, pointing at the word `class` as the problem. – Micah Zoltu Jul 28 '17 at 19:33
  • Ah, apparently this syntax isn't valid in a d.ts file. :/ I was really hoping to get better syntax for writing definition files for node modules that export a class that has nested classes dangling off of it. I guess this isn't the solution for that. – Micah Zoltu Jul 28 '17 at 19:35
6

Currently there is no support for nested classes.

http://typescript.codeplex.com/workitem/460

user1793714
  • 329
  • 2
  • 3
6

This is a bit late but others may find it useful. This is how I did it to fool TS to accomplish the task.

declare module OpenLayers {
  interface Layer { 
   ....
  }

  interface Markers {
    ....
  }

  var Layer: {
    new(name:string, options?:any):Layer;
    prototype:Layer;
    Markers: { 
      new(params?:any):Markers;
      prototype: Markers;
    }
  }
}

var markers = new OpenLayers.Layer.Markers();
Leng
  • 1,979
  • 1
  • 13
  • 7
3

Use TypeScript's class expressions for this:

var OpenLayers = class {
    static Layer = class {
        static Markers = class {}
    }
}
Vad
  • 4,052
  • 3
  • 29
  • 34
  • Does it also works if the top level object is `export`ed? I am facing following error: `Public static property 'myprop' of exported class has or using private name '(Anonymous class)'`. Any suggestion? – Sayan Pal Sep 22 '16 at 10:15
  • @SayanPal don't know what you are trying to do – Vad Sep 22 '16 at 14:09