How can we make an arg be optional only if the type mapped to it is undefined?

自古美人都是妖i 提交于 2019-12-11 10:46:01

问题


For example, if we have the following code,

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

which works (playground link), how can we make it so that calls to emit do not require the second arg for the ANOTHER_EVENT event?

For example, I can add the following line and it works:

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

(playground link)

But if I want to call emit with 'ANOTHER_EVENT', I'd like to do it without the second arg:

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

which gives an error because it expects the second arg. (playground link)

To make it work, I have to write:

emitter.emit('ANOTHER_EVENT', undefined)

(playground link)

How can we make the second arg not required only for the case where we call emit with 'ANOTHER_EVENT'?


回答1:


You can use tuples in rest arguments to change the number of arguments (and even their optionality) based on the first argument.

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')



回答2:


You can make the second argument optional by using a ?:

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')

I don't think it's possible to have it optional based on the input of the first argument. In that case you just need two different methods.



来源:https://stackoverflow.com/questions/53568797/how-can-we-make-an-arg-be-optional-only-if-the-type-mapped-to-it-is-undefined

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!