首页 > 解决方案 > Typescript: typing many similar export values

问题描述

I have a module, that contains handlers for my API calls. It looks something like this:

// ./handlers.ts
export function handler1({ api }) { /* ... */ }
export function handler2({ api, config }) { /* ... */ }
export function handler3({ api }) { /* ... */ }
// ./index.ts
import * as handlers from './handlers'

app.post('/', (req, res, next) => {
  const handler = determineHandlerName()
  if(handler in handlers) {
    handlers[handler](api)
  }
})

Is there an elegant way to tell typescript the handlers object looks like this?

interface Handlers {
  [key: string]: (Api) => void
}

标签: typescripttypes

解决方案


从 TypeScript 3.6+ 开始,类型的handler[keyOfHandlers]推断如下:

import * as handlers from './handlers'

declare const handlerName: keyof typeof handlers;

const handler = handlers[handlerName];

/*
handler: (({ api }: { api: any; }) => void)
         | (({ api, config }: { api: any; config: any; }) => void)
         | (({ api }: { api: any; }) => void)
*/

因此,推断的handlers对象类型在技术上是正确的。

问题出在这个地方:

if (handler in handlers) {
  // Here, `handler` still has type `string`,
  // which is the opposite of what we want.

  // We could do this, however:
  const handlerName = handler as keyof typeof handlers;
  handlers[handlerName]({ api });
}

正如我们所见,问题在于 TypeScript 不handler保护if (handler in handlers).

另一个解决方案是创建一个自定义类型保护:

const isKeyOf = <T>(key: any, obj: T): key is keyof T => key in obj;

// then:
if (isKeyOf(handler, handlers)) {
  handlers[handler](api); // works fine
}

推荐阅读