网站搜索

typescript - 基于输入类型的函数返回类型映射


问题描述

我有一个叫做 createProduct 的函数,它接受一个 Spec 作为输入,并生成一个 Product 作为输出。

Spec 使用判别联合(type: 'a' | 'b')。类型 ProductA 和 ProductB 不兼容,因为它们在字段定义上没有重叠。

有没有办法对 createProduct 进行类型定义,以确保当使用 SpecA 时,它会生成 ProductA,而当使用 SpecB 时,它会生成 ProductB?基本上,我在寻找某种类型映射。伪代码:createProduct<Spec=SpecA|SpecB>(spec: Spec): <SpecA => ProductA | SpecB => ProductB>

当然,如果我传递一个类型为 Spec 的 spec 变量,这是行不通的。但我认为,如果我传递一个字面量作为参数,例如 { type: 'a' },应该是可以实现的。

代码

type Spec = SpecA | SpecB;
interface SpecA {
  type: 'a';
}
interface SpecB {
  type: 'b';
}

type Product = ProductA | ProductB;
interface ProductA {
  properyA: 'unique property of A';
}
interface ProductB {
  properyB: 'unique property of B';
}

export function createProduct(spec: Spec): Product {
  if (spec.type === 'a') return { properyA: 'unique property of A' };
  else if (spec.type === 'b') return { properyB: 'unique property of B' };
  else return spec; // spec: never
}

export function consumeProductCreator(): void {
  const product = createProduct({ type: 'a' });
  // The type of `product` is just `Product` but I want it to be inferred as `ProductA`
  // by adjusting the signature of `createProduct` function.
}

答案#1

如果你添加两个重载,它应该可以工作:

export function createProduct(spec: SpecA): ProductA;
export function createProduct(spec: SpecB): ProductB;
export function createProduct(spec: Spec): Product {
  if (spec.type === 'a') return { properyA: 'unique property of A' };
  else if (spec.type === 'b') return { properyB: 'unique property of B' };
  else return spec; // spec: never
}

现在是:

export function consumeProductCreator(): void {
  const product = createProduct({ type: 'a' });
  //    ^? const product: ProductA
}

有关更多详细信息,请参阅关于定义函数重载的文档(实际上只是签名重载,因为你无法像在其他语言如 Java 中那样重载实现——但在这种情况下你不需要这样做):

  • https://www.typescriptlang.org/docs/handbook/2/functions.html#function-overloads