TypeScript列挙型をどのようにマップしますか?たとえば、文字列でこれを行うことができます:
let arr = [ 'Hello', 'Goodbye' ];
arr.map(v => {
if (v === 'Hello') {
return ':)';
} else if (v === 'Goodbye') {
return ':(';
}
); // [ ':)', ':(' ]
もちろん、これは列挙型では機能しません。
enum MyEnum { Hello, Goodbye };
MyEnum.map(v => {
if (v === MyEnum.Hello) {
return ':)';
} else if (v === MyEnum.Goodbye) {
return ':(';
}
}); // does not work
理想的には、これを一般化された方法で実行して、所有している列挙型を単純に取得し、マップ関数型情報を保持しながらに渡すことができます。使用法は次のようになります。
map(MyEnum, v => {
if (v === MyEnum.Hello) {
return ':)';
} else if (v === MyEnum.Goodbye) {
return ':(';
}
}); // [ ':)', ':(' ]
私はこれを行う関数を取得するためにいじくり回してきましたが、ジェネリックを適切に取得するのに問題が続いています。
これを解決する関数は非常に単純です。
// you can't use "enum" as a type, so use this.
type EnumType = { [s: number]: string };
function mapEnum (enumerable: EnumType, fn: Function): any[] {
// get all the members of the enum
let enumMembers: any[] = Object.keys(enumerable).map(key => enumerable[key]);
// we are only interested in the numeric identifiers as these represent the values
let enumValues: number[] = enumMembers.filter(v => typeof v === "number");
// now map through the enum values
return enumValues.map(m => fn(m));
}
ご覧のとおり、最初に列挙型のすべてのキーを取得する必要があります(MyEnum.Hello
は実際には実行時に1
です)。次に、それらをマップし、関数を渡します。
使い方も簡単です(例と同じですが、名前を変更しました)。
enum MyEnum { Hello, Goodbye };
let results = mapEnum(MyEnum, v => {
if (v === MyEnum.Hello) {
return ':)';
} else if (v === MyEnum.Goodbye) {
return ':(';
}
});
console.log(results); // [ ':)', ':(' ]
列挙型を数値のみにフィルタリングする必要があるのは、列挙型のコンパイル方法が原因です。
あなたの列挙型は実際にこれにコンパイルされます:
var MyEnum;
(function (MyEnum) {
MyEnum[MyEnum["Hello"] = 0] = "Hello";
MyEnum[MyEnum["Goodbye"] = 1] = "Goodbye";
})(MyEnum || (MyEnum = {}));
;
ただし、"Hello"
や"Goodbye"
は実行時に使用できないため、これらには関心がありません。
また、関数の直前に面白いtype
ステートメントがあります。これは、パラメーターをsomeParameter: enum
として入力できないため、number -> string
マップとして明示的に指定する必要があるためです。
ts-enum-util
( npm 、 github )を使用すると、簡単でタイプセーフ(ジェネリックを使用)で、数値の逆ルックアップエントリをスキップします。
import { $enum } from "ts-enum-util";
enum MyEnum { Hello, Goodbye };
$enum(MyEnum).map(v => {
if (v === MyEnum.Hello) {
return ':)';
} else if (v === MyEnum.Goodbye) {
return ':(';
}
}); // produces [':(', ':)']
注:ts-enum-util
は常に、ソートされた列挙型キーの順序に基づいて反復し、すべての環境で一貫した順序を保証します。 Object.keys()には保証された順序がないため、クロスプラットフォームで保証された方法で列挙型を「定義された順序で」反復することは不可能です。 (更新:ts-enum-utilの新しいバージョンは、列挙型が定義された元の順序を保持するようになりました)
文字列列挙型を使用している場合は、それをts-string-visitor
( npm 、 github )と組み合わせて、より一般的なタイプセーフコンパイラチェックを実行し、すべてを処理することを保証します。マップ関数の可能な列挙値:(更新:ts-enum-utilの新しいバージョンには、ts-string-visitorの機能が含まれ、数値列挙型でも機能するようになりました!)
import { $enum } from "ts-enum-util";
import { mapString } from "ts-string-visitor";
enum MyEnum { Hello = "HELLO", Goodbye = "GOODBYE" };
$enum(MyEnum).map(v => {
// compiler error if you forget to handle a value, or if you
// refactor the enum to have different values, etc.
return mapString(v).with({
[MyEnum.Hello]: ':)',
[MyEnum.Goodby]: ':('
});
}); // produces [':(', ':)']
私はそれを一般的とは呼びませんが、私はこれを何度も使用し、他の人にとっても便利になるでしょう:
type TMyEnum = ':)'|':(';
class MyEnum {
static Hello: TMyEnum = ':)';
static Goodbye: TMyEnum = ':(';
}
console.log(MyEnum.Hello); // :)
console.log(MyEnum.Goodbye); // :(
これで、マッピング関数は必要なく、期待どおりに機能しますが、列挙型ごとに同様のクラスを個別に作成する必要があります(とにかくそうするので問題にはなりません)。私が今考えられる唯一の欠点は、そのプロパティを反復できないことです。しかし、今までは問題ではなかったので、私はそれを必要としませんでした。また、必要に応じて静的配列をクラスに追加できます。