Подтвердить что ты не робот

TypeScript: Добавить функции в Enum

Можно ли добавить функции в тип Enum в TypeScript?

например:

enum Mode {
    landscape,
    portrait,

    // the dream...
    toString() { console.log(this); } 
}

Или:

class ModeExtension {
    public toString = () => console.log(this);
}

enum Mode extends ModeExtension {
    landscape,
    portrait,
}

Конечно, функция toString() содержала бы что-то вроде switch. Но прецедент бы шел по строкам:

class Device {
    constructor(public mode:Mode) {
        console.log(this.mode.toString());
    }
}

Я понимаю, почему расширение enum может быть странным, просто интересно, возможно ли это.

4b9b3361

Ответ 1

Во время выполнения var mode = Mode.portrait; переменная mode является числом. Вам нужно будет переопределить number.toString, чтобы получить нужное поведение, и это повлияет на все числа (и все перечисления), что определенно не то, что вы хотите.

Итак, это не совсем то, что вам нужно, но это позволяет вам инкапсулировать поведение "Режим в строку", используя статический метод либо в отдельном классе, либо с помощью объединения enum/module:

class ModeUtil {
    public static toString(mode: Mode) {
        return Mode[mode];
    }
}

Вы можете использовать его следующим образом:

var mode = Mode.portrait;
var x = ModeUtil.toString(mode);
console.log(x);

или

module Mode {
    export function toString(mode: Mode) {
        return Mode[mode];
    }

    export function parse(mode: string) {
        return Mode[mode];
    }
}

Вы можете использовать это:

var mode = Mode.portrait;
var x = Mode.toString(mode);
console.log(x);

И я включил там бонус parse, который вы можете использовать следующим образом:

var y = Mode.parse('portrait');
console.log(y);

Подробнее о слиянии объявлений (и, в частности, о слиянии модулей с классами, функциями и перечислениями) здесь.

Ответ 2

Вы можете получить строковое значение неконстантного перечисления с помощью квадратных скобок:

class Device {
    constructor(public mode:Mode) {
        console.log(Mode[this.mode]);
    }
}

Вы также можете поместить некоторые enum-специфичные функции использования в перечисление, но это так же, как и статические члены класса:

enum Mode {
    landscape,
    portrait
}

namespace Mode {
    export function doSomething(mode:Mode) {
        // your code here
    }
}

Ответ 3

Преобразуйте enum в шаблон перечисления. Я считаю, что это более эффективная практика в целом для многих языков, поскольку в противном случае вы ограничиваете параметры инкапсуляции для своего типа. Тенденция к switch по значению перечисления, когда действительно любые данные или функциональные возможности, зависящие от конкретного значения перечисления, должны просто входить в каждый экземпляр перечисления. Я добавил еще несколько примеров для демонстрации.

Это может не сработать, если вы особенно зависите от базовых значений перечисления. В этом случае вам нужно будет добавить элемент для старых значений и преобразовать места, в которых он нуждается, чтобы использовать новое свойство.

class Mode {
   public static landscape = new Mode(1920, 1080);
   public static portrait = new Mode(1080, 1920);

   public get Width(): number { return this.mWidth; }
   public get Height(): number { return this.mHeight; }

   // private constructor if possible in a future version of TS
   constructor(
      private mWidth: number,
      private mHeight: number
   ) {
   }

   public GetAspectRatio() {
      return this.mWidth / this.mHeight;
   }
}