网站搜索

在 TypeScript 中通过动态键-函数映射确保类型安全


我正在使用 TypeScript,并且在动态将键映射到特定处理函数时遇到了确保类型安全的挑战。下面是说明我问题的 TypeScript 代码片段:

interface Foo {
  foo: string
}

interface Bar {
  bar: string
}

const fooProcessor = (foo: Foo) => {
  return foo
}

const barProcessor = (bar: Bar) => {
  return bar
}

const processorMap = {
  foo: fooProcessor,
  bar: barProcessor,
}

const fooGenerator = () => ({ foo: '123' } as Foo)
const barGenerator = () => ({ bar: '123' } as Bar)

const generatorMap = {
  foo: fooGenerator,
  bar: barGenerator,
}

const topLevelProcessor = (type: 'foo' | 'bar') => {
  const object = generatorMap[type]()

  return processorMap[type](object)
}

挑战出现在 topLevelProcessor 函数中,TypeScript 无法推断对象和函数 processorMap[type] 的正确类型。这导致类型不匹配错误。我正在尝试避免使用 as any,以保持强类型安全。

答案#1

在字符串字面量和类型之间建立绑定的最直接方法是创建辅助接口(下文的KnownTypeBinding):

interface Foo {
  foo: string;
}

interface Bar {
  bar: string;
}

interface KnownTypeBinding {
  foo: Foo;
  bar: Bar;
}

type KnownTypeName = keyof KnownTypeBinding;
type KnownType = KnownTypeBinding[KnownTypeName];

type TypeProcessor<T extends KnownType> = (x: T) => T;
type TypeGenerator<T extends KnownType> = () => T;

type ProcessorMap = {
  [K in KnownTypeName]: TypeProcessor<KnownTypeBinding[K]>;
}

const processorMap: ProcessorMap = {
  foo: (foo: Foo) => { return foo; },
  bar: (bar: Bar) => { return bar; }
}

type GeneratorMap = {
  [K in KnownTypeName]: TypeGenerator<KnownTypeBinding[K]>;
}

const generatorMap: GeneratorMap = {
  foo: () => { return { foo: 'foo' }; },
  bar: () => { return { bar: 'bar' }; }
}

type TopLevelProcessor = <K extends KnownTypeName>(type: K) => KnownTypeBinding[K];

const topLevelProcessor: TopLevelProcessor = <K extends KnownTypeName>(type: K) => {
  const object = generatorMap[type]()
  return processorMap[type](object);
}