如何在TypeScript中使一个泛型类型紧密依赖于另一个泛型类型?

时间:2020-05-06 15:45:32

标签: typescript generics

我正在寻找一种使代码更具类型安全性的方法,并希望在将某些类型传递给泛型函数时定义它们之间的紧密关系。

例如,对于给定的一组类型:

interface FooParam {}
interface FooReturn {}

interface BarParam {}
interface BarReturn {}

和以下功能:

function action<T, R>(foo: T): R

我想将FooParamFooReturn紧密绑定,并将BarParamBarReturn绑定,因此,仅当传递了一对关联类型时,编译器才允许调用分别为TR,并返回错误。

action<FooParam, FooReturn>(...) // bound types, OK
action<BarParam, BarReturn>(...) // bound types, OK

action<FooParam, BarReturn>(...) // types are not bound, ERROR
action<FooParam, string>(...) // types are not bound, ERROR

实际上,我已经通过定义两个基本接口来实现上述目的,这些接口随后将用作对通用类型的约束:

interface Param {}
interface Return<T extends Param>{
    _typeGuard?: keyof T
}

interface FooParam extends Param {}
interface FooReturn extends Return<FooParam> {}

interface BarParam extends Param {}
interface BarReturn extends Return<BarParam> {}

function action<T extends Param, R extends Return<T>>(foo: T): R

但是,这似乎更像是一种解决方法,而不是一个干净的解决方案,尤其是考虑到_typeGuard?: keyof T字段,该字段仅存在,因此T实际上很重要,并且被检查而不被忽略。另一个缺点是,每种类型都必须扩展我的自定义接口之一,有时候这是不可能的。

是否有更好,更通用的方法来获得所描述的功能?

3 个答案:

答案 0 :(得分:3)

您可以使用通用类型和推断。如果您的要求允许这样做,那么比从基本接口扩展每种类型都容易。此解决方案将所有链接放在一个位置,因此有其自身的缺点,但是如果您不能更改原始类型,对我来说似乎可以:

type Return<T> =
  T extends BarParam ? BarReturn :
  T extends FooParam ? FooReturn :
  never;

您可以在操作Playground中看到它

答案 1 :(得分:1)

听起来比泛型更像是code

{
   "query": {
      "bool": {
         "must": {
            "term": {
               "event_type": "my_event_type"
            }
         },
         "should": [
            {
               "terms": {
                  "event_type": [
                     "my_event_type1",
                     "my_event_type2",
                     "my_event_type3"
                  ]
               }
            }
         ]
      }
   }
}

在这里,我不建议在泛型中使用条件类型。

答案 2 :(得分:0)

我将基于链接类型将耦合的接口制成泛型:

interface Foo {}
interface Bar {}

interface Param<T> {
}

interface Return<T> {
}

interface FooParam extends Param<Foo> {
}

interface FooReturn extends Return<Foo> {
}

interface BarParam extends Param<Bar> {
}

interface BarReturn extends Return<Bar> {
}

function action<T>(foo: Param<T>): Return<T> {
  ...
}