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

Например, если у нас есть следующий код,

type Events = {
    SOME_EVENT: number
    OTHER_EVENT: string
    ANOTHER_EVENT: undefined
}

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 ) {}

который работает (ссылка на игровую площадку), как сделать так, чтобы вызовы emit не требовали второго аргумента для события ANOTHER_EVENT?

Например, я могу добавить следующую строку, и она работает:

emitter.emit('OTHER_EVENT', 'foo')

(ссылка на игровую площадку)

Но если я хочу вызвать emit с 'ANOTHER_EVENT', я бы хотел сделать это без второго аргумента:

emitter.emit('ANOTHER_EVENT') // ERROR, expected 2 arguments, but got 1.

который выдает ошибку, потому что ожидает второй аргумент. (ссылка на игровую площадку< /а>)

Чтобы заставить его работать, я должен написать:

emitter.emit('ANOTHER_EVENT', undefined)

(p ссылка на макет)

Как мы можем сделать второй аргумент необязательным только для случая, когда мы вызываем emit с 'ANOTHER_EVENT'?


person trusktr    schedule 01.12.2018    source источник


Ответы (2)


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

type Events = {
    SOME_EVENT: number
    OTHER_EVENT: string
    ANOTHER_EVENT: undefined
}

type EventArgs<EventTypes, K extends keyof EventTypes> = EventTypes[K] extends undefined ?[]:[EventTypes[K]]
interface EventEmitter<EventTypes> {
    on<K extends keyof EventTypes>(s: K, listener: (...v: EventArgs<EventTypes,K>) => void);
    emit<K extends keyof EventTypes>(s: K, ...v: EventArgs<EventTypes,K>);
}

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) {}

emitter.emit('OTHER_EVENT', 'foo')

emitter.emit('ANOTHER_EVENT')
person Titian Cernicova-Dragomir    schedule 01.12.2018
comment
Святая мольба, это потрясающе! Спасибо Тициан! - person trusktr; 01.12.2018

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

type Events = {
    SOME_EVENT: number
    OTHER_EVENT: string
    ANOTHER_EVENT?: undefined
}

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

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) {}

emitter.emit('OTHER_EVENT', 'foo')

emitter.emit('ANOTHER_EVENT')

Я не думаю, что это возможно сделать необязательным на основе ввода первого аргумента. В этом случае вам просто нужны два разных метода.

person Arno van Oordt    schedule 01.12.2018
comment
Привет Арно, видимо это возможно! См. ответ Тициана. - person trusktr; 01.12.2018