Lewis Peel
Lewis Peel

Reputation: 1399

Enum inside class (TypeScript definition file)

I've searched around but can't seem to find an answer for this, hopefully you can help.

How can I add an enum to Image? This is what I would like ideally but I get an error.

declare module 'Lib' {
  export module Graphics {
    export class Image {
      enum State {}

      static STATE_IDLE: State;
      static STATE_LOADING: State;
      static STATE_READY: State;
      static STATE_ERROR: State;
      constructor();
    }
  }
}

If I move State into the Graphics module it works but now State belongs to Graphics, which is incorrect. It needs to be part of Image.

Upvotes: 116

Views: 132083

Answers (8)

Chen Peleg
Chen Peleg

Reputation: 2034

Another interesting option that works including ide support (WebStorm) for refactoring etc:

  export class Image {
    static get State () {
        enum StateEnum {
            STATE_IDLE,
            STATE_LOADING
        }
        return StateEnum;
    }
    constructor();
}

A bit hacky but the advantage is that whenever you change the enum members, your ide can refactor it all over your code.

Upvotes: 0

NSjonas
NSjonas

Reputation: 12032

I think the following is an improvement on KoenT's solution:

export class Image
{
    constructor ()
    {
        this.state = Image.State.Idle;
    }

    state: Image.State;
}

export namespace Image
{
    export enum State
    {
        Idle,
        Loading,
        Ready,
        Error
    }
}

The advantage being that you can leverage named imports:

import {Image} from './image';
let img = new Image()
img.state = Image.State.Error

Here's a code sandbox showing this in action.

Upvotes: 98

Maz T
Maz T

Reputation: 1244

Here's my solution.

program.ts:

enum Status {
    Deleting,
    Editing,
    Existing,
    New
}

export class Program {
    static readonly Status = Status;
    readonly Status = Program.Status;

    title: string;

    status: Status;

    constructor(init?: Partial<Program>) {
        Object.assign(this, init);
    }
}

Usage:

let program = new Program({ title: `some title` });

program.status = Program.Status.New;

or

program.status = program.Status.New;

Added benefit for Angular 2+ users: this can be used in templates

<div *ngIf="program.status === program.Status.New">
  Only display if status of the program is New
</div>

Upvotes: 65

Alex
Alex

Reputation: 14493

I think that this stuff with module augmentation is a very hacky and non-intuitive way* of doing things, so consider this:

export module Graphics
{
    enum State
    {
        STATE_IDLE,
        STATE_LOADING,
        STATE_READY,
        STATE_ERROR
    }

    export class Image
    {
        constructor() { }
        public static readonly State = State;
    }
}

//...

let imgState = Graphics.Image.State.STATE_ERROR;

That is, just declare the enum in the scope of the class that you want to add it to without exporting it, then expose it through a member of the class.

* Which in regards of structuring and organization of code is BAD, even if it technically works.

Update

declare module Lib
{
    enum State
    {
        STATE_IDLE,
        STATE_LOADING,
        STATE_READY,
        STATE_ERROR
    }

    class ImageClass
    {
        constructor();
        public Prop: any;
    }

    export interface Graphics
    {
        Image: typeof State & ImageClass & (new () => typeof State & ImageClass);
    }
}

declare var Graphics: Lib.Graphics;

Then you get typing like:

var someEnum = Graphics.Image.STATE_ERROR;
var image = new Graphics.Image();
var anotherEnum = image.STATE_IDLE;

Upvotes: 3

KoenT
KoenT

Reputation: 491

I also bumped into this problem recently. This is what I am currently using as a solution:

// File: Image.ts

class Image
{
    constructor()
    {
        this.state = Image.State.Idle;
    }

    state: Image.State;
}

module Image
{
    export enum State
    {
        Idle,
        Loading,
        Ready,
        Error
    }
}

export = Image;

Then in the place where I'm using the class and its enum:

import Image = require("Image");

let state = Image.State.Idle;
let image = new Image();
state = image.state;

This seems to work fine (even though I don't consider it as the expected way to do this kind of thing).

Hopefully there will be a way in TypeScript to do it this way:

class Image
{
    enum State
    {
        Idle,
        Loading,
        Ready,
        Error
    }

    constructor()
    {
        this.state = State.Idle;
    }

    state: State;
}

export = Image;

Upvotes: 39

Lewis Peel
Lewis Peel

Reputation: 1399

I think I may have found a solution...whether it's valid TypeScript I don't know but it works and doesn't cause any compile errors. It's a combination of the above answers.

declare module 'Lib' {

  module Graphics {

    module Image {
      enum State { }
      var STATE_IDLE: State;
      var STATE_LOADING: State;
      var STATE_READY: State;
      var STATE_ERROR: State;
    }

    class Image {
      constructor();
    }

  }

}

Can anyone spot any potential issues with this that I haven't noticed?

Upvotes: 3

David Sherret
David Sherret

Reputation: 106560

You could create a module and class with the same name. It might also help to rename your enum so that you don't have to say State twice:

declare module 'Lib' {
    export module Graphics {
        export class Image {
            constructor();
        }

        export module Image {
            export enum State {
                Idle,
                Loading,
                Ready,
                Error
            }
        }
    }
}

Upvotes: 0

Fenton
Fenton

Reputation: 250782

I'm not sure what you intend to do, but I would have expected that you would want an enum to represent the possible state values, and then a state member on the image to indicate the current state of the image.

declare module 'Lib' {
    export module Graphics {

        enum State {
            STATE_IDLE,
            STATE_LOADING,
            STATE_READY,
            STATE_ERROR
        }

        export class Image {
            public state: State;

            constructor();
        }

    }
}

It sounds like you want to declare a class that has enum-like members, rather than declare an enum within a class. i.e:

declare module 'Lib' {

    export module Graphics {

        export class Image {
            static STATE_IDLE: number;
            static STATE_LOADING: number;
            static STATE_READY: number;
            static STATE_ERROR: number;

            constructor();
        }
    }
}

Upvotes: 1

Related Questions