Как мы можем сопоставить массив строк с картой типов TypeScript ключей для типов? - PullRequest
0 голосов
/ 30 ноября 2018

У меня есть следующий рабочий код без ошибок типа:

type Events = { SOME_EVENT: number; OTHER_EVENT: string }

interface EventEmitter<EventTypes> {
  on<K extends keyof EventTypes>(s: K, listener: (v: EventTypes[K]) => void);
}

declare const emitter: EventEmitter<Events>;

emitter.on('SOME_EVENT', (payload) => testNumber(payload));
emitter.on('OTHER_EVENT', (payload) => testString(payload));

function testNumber( value: number ) {}
function testString( value: string ) {}

( пример на TS Playground )

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

emitter.on(EventNames.SOME_EVENT, (payload) => testNumber(payload));
emitter.on(EventNames.OTHER_EVENT, (payload) => testString(payload));

Я пытаюсь сделать вещи СУХИМЫМИ, поэтому мне интересно, есть ли способ сделать эту работу без повторения всех имен событий в новом типе.

В простом JavaScript я могу легко сделать следующее:

const EventNames = [
    'SOME_EVENT',
    'OTHER_EVENT',
]

const Events = {}

for (const eventName of Events) {
    Events[eventName] = eventName
}

// then use it:

emitter.on(Events.SOME_EVENT, (payload) => testNumber(payload));
emitter.on(Events.OTHER_EVENT, (payload) => testString(payload));

Таким образом, в простом JS я могу создавать объекты, похожие на enum, без необходимости прибегать к чему-то чуть менее СУХОМУ, например,

const Events = {
    SOME_EVENT: 'SOME_EVENT', // repeats the names twice
    OTHER_EVENT: 'OTHER_EVENT',
}

emitter.on(Events.SOME_EVENT, (payload) => testNumber(payload));
emitter.on(Events.OTHER_EVENT, (payload) => testString(payload));

Мне интересно, как я могу сделать вещи максимально сухими в TypeScript,только когда-либо определяя имя каждого события только один раз, но также имеет тип, связанный с полезными нагрузками события.

Я могу сделать это следующим способом менее СУХОЙ, повторяя имена событий три раза каждое:

type Events = { SOME_EVENT: number; OTHER_EVENT: string }

const EventNames: {[k in keyof Events]: k} = {
  SOME_EVENT: 'SOME_EVENT', OTHER_EVENT: 'OTHER_EVENT'
}

interface EventEmitter<EventTypes> {
  on<K extends keyof EventTypes>(s: K, listener: (v: EventTypes[K]) => void);
}

declare const emitter: EventEmitter<Events>;

emitter.on(EventNames.SOME_EVENT, (payload) => testNumber(payload));
emitter.on(EventNames.OTHER_EVENT, (payload) => testString(payload));

function testNumber( value: number ) {}
function testString( value: string ) {}

( Playgroи ссылка )

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

Если это невозможно сделать только с одним экземпляром каждого имени события, каков наилучший способ?

1 Ответ

0 голосов
/ 30 ноября 2018

Я нашел способ сохранять вещи СУХИМЫМИ, чтобы каждое имя события определялось только один раз, используя фиктивный класс, который содержит информацию о типе И генерирует вывод JS, по которому мы можем выполнить итерации, чтобы создать тип enum с правильной типизацией:

class EventTypes {
    constructor(
        // define a map of event names to event payload types here.
        public SOME_EVENT: number,
        public OTHER_EVENT: string
    ) {}
}

const EventNames = {} as { [k in keyof EventTypes]: k }

for (const key in new (EventTypes as any)) {
    EventNames[key] = key
}

console.log(EventNames)

interface EventEmitter<EventTypes> {
  on<K extends keyof EventTypes>(s: K, listener: (v: EventTypes[K]) => void);
}

declare const emitter: EventEmitter<EventTypes>;

emitter.on(EventNames.SOME_EVENT, (payload) => testNumber(payload));
emitter.on(EventNames.OTHER_EVENT, (payload) => testString(payload));

function testNumber( value: number ) { console.assert(typeof value === 'number') }
function testString( value: string ) { console.assert(typeof value === 'string') }

игровая площадка

...