Search code examples
typescriptfunctionparametersstring-literalsinference

Typescript: how to inference a string literal param type and use it on another param type


Here an example that explain what I'm asking!

// Given an events definition

interface IEventDef {
    event1: () => void,
    event2: (data: T) => void
    // ...
}

How can it declare something like:

public on(
    eventName: keyof IEventsDefinitions, // get the literal string here (when it's used)
    callback: IEventsDefinitions[EventName] // and use it on the next param type ???
) {

}

The point is how to make such a definition so that when we write:

obj.on('event1', (data) => {
    // need data to be correctly infered and that using the string literal 'event1'! Technically that is possible to be done
    // as it is, it doesn't inference
})

// need data to be correctly infered and that using the string literal 'event1'! Technically that is possible to be done

As it is, it doesn't inference!

Know too that a workaround like this works:

public on<EventName extends keyof IEventsDefinitions>(
     eventName: keyof IEventsDefinitions,
     callback: IEventsDefinitions[EventName]
) {

and use it like:

on<"event1">('event1', (data) => {})

data will be infered correctly! But it's ugly! (and technically inferencing from the literal is possible)!

So I'm asking how could we do that in any way !? Thank you in advance !


Solution

  • Your workaround version is mostly correct, the part that you are missing is that the eventName and callback arguments need to reference the same key.

    Instead of eventName: keyof IEventsDefinitions, you need eventName: EventName where the value of the eventName argument matches the generic variable of the function.

    function on<EventName extends keyof IEventsDefinitions>(
        eventName: EventName,
        callback: IEventsDefinitions[EventName]
    ) {
    

    You no longer need to specify the generic when calling the function because now typescript is able to infer the generic from the first argument.

    on('event1', () => {})
    
    on('event2', (data) => {})
    

    Playground Link