简体   繁体   中英

Typed function for dispatching functions through a Record

const dispatch = {
  'A': (arg: { a0: number }) => { },
  'B': (arg: { b0: string, b1: number }) => { }
}

function Dispatcher<???>(key: ???, arg: ???) {
  dispatch[key](arg); 
}
function Dispatcher2<K extends keyof typeof dispatch>
(key: K, arg: Parameters<typeof dispatch[K]>[0]) {
    dispatch[key](arg);// passing arg fails type checking
}

Playground

There are several ways to allow only the correct combinations of key and arg into Dispatcher . However, I can never get TypeScript to understand that it's ok to pass the arg into the function call.

Is there a way to do this?

This problem can be put in the category of "correlated union" problems. When indexing dispatch with key , the compiler computes a union of functions which is only callable with an intersection of all parameters. The relationship between dispatch[key] and arg is essentially lost.

But there is a specific way to rewrite the types as described in PR#47109 to make the compiler understand the correlation.

We have to change the typing of dispatch so that it uses a mapped type . The type DispatchRecord acts as a lookup type for the parameter types.

type DispatchRecord = { 
  A: { a0: number }
  B: { b0: string, b1: number }
}

const dispatch: { 
  [K in keyof DispatchRecord]: (arg: DispatchRecord[K]) => void 
} = {
  'A': (a) => { },
  'B': (b) => { }
}

The function itself stays generic over K . To type key and arg , we use a rest parameter. The type of the rest parameter is mapped type which computes a tuple consisting of two elements, one for key and one for arg . This mapped type is then indexed by K .

function Dispatcher<K extends keyof DispatchRecord>(...[key, arg]: {
  [P in K]: [P, DispatchRecord[P]]
}[K]) {
    dispatch[key](arg)
}

Playground

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM