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

jsa*_*mol 7 generics typescript

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

例如,对于给定的类型集:

interface FooParam {}
interface FooReturn {}

interface BarParam {}
interface BarReturn {}
Run Code Online (Sandbox Code Playgroud)

以及以下功能:

function action<T, R>(foo: T): R
Run Code Online (Sandbox Code Playgroud)

我想紧密结合FooParamFooReturnBarParamBarReturn的,所以编译器允许只有在对相关类型的作为通过调用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
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

然而,这似乎更像是一种变通方法而不是一个干净的解决方案,特别是考虑到该_typeGuard?: keyof T领域,它仅存在于T实际重要并且被检查而不是被忽略的领域。另一个缺点是每种类型都必须扩展我的自定义接口之一,这有时是不可能的。

有没有更好、更通用的方法来获得所描述的功能?

leo*_*ory 6

您可以使用泛型类型和推理。如果您的要求允许这样做,它可能比从基本接口扩展每种类型更容易。此解决方案将您的所有链接放在一个地方,因此它有其自身的缺点,但如果您无法更改原始类型,则对我来说似乎没问题:

type Return<T> =
  T extends BarParam ? BarReturn :
  T extends FooParam ? FooReturn :
  never;
Run Code Online (Sandbox Code Playgroud)

你可以在Playground 中看到它