typescript - 如何使函数重载通用以强类型化其实现参数?

标签 typescript generics polymorphism overloading

我正在尝试找到一种方法来从函数重载中获得严格的参数区分。显然,我的这个实现的问题是我的通用类型 T可以扩展到任何继承AorB的东西props 所以我在这里得到的错误是完全可以预料的( '{ type: "A"; a: any; }' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'AorB'. )。

我正在寻找的是一种拥有某种 <T implements AorB> 的方法这样当参数 type等于 "A"customProps参数被判别为A Prop 。

我也试图避免使用 any在函数实现参数或as内在返回之内。

type A = {
  a: string
  type: 'A'
}

type B = {
  b: string
  type: 'B'
}

type AorB = A | B

function createAorB(type: A['type'], customProps?: Partial<Omit<A, 'type'>>): A
function createAorB(type: B['type'], customProps?: Partial<Omit<B, 'type'>>): B
function createAorB<T extends AorB>(type: T['type'], customProps: Partial<Omit<T, 'type'>> = {}): T {
  if (type === 'A') {
    return {
      type,
      a: customProps.a || '',
    }
  }

  return {
    type,
    b: customProps.b || '',
  }
}

const newA = createAorB('A')
const newB = createAorB('B')

更新

如果我强制执行AorB作为返回值:

function createAorB<T extends AorB>(type: T['type'], customProps: Partial<Omit<T, 'type'>> = {}): AorB

我收到两个错误 Property 'a' does not exist on type 'Partial<Omit<T, "type">>'.Property 'b' does not exist on type 'Partial<Omit<T, "type">>'.分别在customProps.acustomProps.b行。

最佳答案

参数列表tuple typecreateAOrB()形成 discriminated union ,其中第一个元组元素是判别式。它看起来像这样:

type Args = 
   [type: "A", customProps?: Partial<Omit<A, "type">>] | 
   [type: "B", customProps?: Partial<Omit<B, "type">>]; 

如果您使用 rest parameterdestructuring assignment将元素分配给名为 type 的变量和customProps ,您可以annotate其余参数为Args ,然后 the compiler will use control flow analysis to narrow customProps when you check type :

function createAorB(type: A['type'], customProps?: Partial<Omit<A, 'type'>>): A
function createAorB(type: B['type'], customProps?: Partial<Omit<B, 'type'>>): B
function createAorB(...[type, customProps]: Args) {
  if (type === 'A') {
    return {
      type,
      a: customProps?.a || '', // okay
    }
  }

  return {
    type,
    b: customProps?.b || '', // okay
  }
}

如果 AorB 中碰巧有很多类型不仅仅是A | B ,扩展 Args 可能会很乏味。定义。在这种情况下,您可以让编译器将其计算为 AorB 的函数。 ,像这样:

type Args = AorB extends infer T ? T extends AorB ? (
  [type: T['type'], customProps?: Partial<Omit<T, 'type'>>]
) : never : never;

这使用 conditional type inference复制AorB进入新的generic类型参数T ,然后用它来制作 distributive conditional type这样[type: T['type'], customProps?: Partial<Omit<T, 'type'>>]自动变成union type如果T是联合类型(它分配 T 中联合的操作)。

您可以验证它的计算结果是否与上面手动定义的版本相同。

Playground link to code

关于typescript - 如何使函数重载通用以强类型化其实现参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72747133/

相关文章:

c# - 告诉调用者 `async Task<T>`方法可能返回null

html - Angular 2/4/6 组件方法如何用于另一个组件?

javascript - 通过数组在 Angular 6 中引导多个模块

java - 继承了一些不好的 Java 泛型

java - 什么是原始类型,为什么我们不应该使用它呢?

C++继承/重定义错误

angularjs - Angular TypeScript 状态 Controller

node.js - TypeScript Node.js 该命令未定义

C++ 纯虚void

c# - 如何通过自定义多态类型的集合获得多态行为?