TSR
TSR

Reputation: 20564

Typescript look up the type of the second argument from interface using first argument in a function

Is it possible to make Typescript figure out the types of the arguments of this callback based on an interface?

export interface MiddlewareEvent {
    onNewAccount: (accountId: string, timestamp: number) => void,
    onAccountDelete: (accountId:string, timestamp:number)=>void,
}

 const middlewareMap: Map<keyof MiddlewareEvent,((...arg: any) => void )[]> = new Map();

function registerMiddleWare(
    eventId: keyof MiddlewareEvent,
    middleware: ((...arg: any) => void)
) {
        const existing = middlewareMap.get(eventId);
        if (!existing?.length) {
            middlewareMap.set(eventId, [middleware]);
        } else {
            existing.push(middleware);
        }
}

registerMiddleWare(`onNewAccount`, (accountId: number, datetime: string) => {
    console.log(`Wait, Typescript should have prevented this`)
    console.log(`account Id is string not number`)
    console.log(`datetime has been changed to timestamp and is now a number`)
})

The idea is to just pass the property of the interface as a string (or enum ? ) to a function as first parameter and Typescript should figure out of the types of the argument of the callback which is the second parameter by looking that key in the interface.

This won`t require the user to manually pass a generic type argument everytime.

Upvotes: 0

Views: 387

Answers (1)

Alex Wayne
Alex Wayne

Reputation: 187134

Sure, you just need an inferred generic function. Let it infer the eventId being passed in, and then drill into MiddlewareEvent based on that key.

function registerMiddleWare<EventName extends keyof MiddlewareEvent>(
    eventId: EventName,
    middleware: MiddlewareEvent[EventName]
) { /* ... */ }

registerMiddleWare(`onNewAccount`, (accountId: number, datetime: string) => {})
// Argument of type
//   '(accountId: number, datetime: string) => void'
// is not assignable to parameter of type
//   '(accountId: string, timestamp: number) => void'

Playground

Upvotes: 1

Related Questions