在TypeScript中重用函数参数

huangapple go评论78阅读模式
英文:

Reusing function parameter in TypeScript

问题

这需要一些解释。

我有一个名为Dependency的类,它持有泛型类型T的值并提供一些辅助方法(在这里并不是特别相关):

  1. class Dependency<T> {
  2. value: T
  3. ...
  4. }

我试图编写另一个类,名为Derivative,它接受依赖项的列表和一个derive函数,用于生成一些其他值:

  1. type DependencyArray = Array<Dependency<any>>
  2. type DeriveFunc<T, D extends DependencyArray = DependencyArray> = (...deps: D) => Promise<T>
  3. class Derivative<T, D extends DependencyArray = DependencyArray> {
  4. value: T
  5. constructor(
  6. dependencies: D,
  7. derive: DeriveFunc<T, D>,
  8. ) { ... }
  9. }

这里有一个关键点 - 我想限制derive函数只接受由Derivative构造函数的第一个参数提供的依赖项。例如:

  1. const depA = new Dependency<string>()
  2. const depB = new Dependency<boolean>()
  3. // 好:这个能够编译通过
  4. const new Derivative<string>([depA, depB], async ([a, b]) => `${a}-${b}`)
  5. // 好:这两个编译失败,因为数组的长度不同
  6. // TS2345: Source has 2 element(s) but target allows only 1.
  7. const new Derivative<string>([depA, depB], async ([a]) => `${a}`)
  8. const new Derivative<string>([depA], async ([a, b]) => `${a}`)
  9. // 不好:在这个例子中,`derive`函数不了解依赖项数组中包含的具体类型,因此将它合并到Dependency<string | boolean>[]中
  10. // TS2339: Property 'length' does not exist on type 'boolean | string'.
  11. // Property 'length' does not exist on type 'false'.
  12. const new Derivative<string>([depA, depB], async ([a, b]) => `${a.length}-${b}`)

是否有一种方法可以精确引用传递给Derivative构造函数的依赖项?

英文:

This will take a bit to explain.

I have a class called Dependency, which holds a value of generic type T and provides some helper methods (not really relevant here):

  1. class Dependency&lt;T&gt; {
  2. value: T
  3. ...
  4. }

I'm trying to write another class, Derivative, that takes a list of dependencies and a derive function to generate some other value:

  1. type DependencyArray = Array&lt;Dependency&lt;any&gt;&gt;
  2. type DeriveFunc&lt;T, D extends DependencyArray = DependencyArray&gt; = (...deps: D) =&gt; Promise&lt;T&gt;
  3. class Derivative&lt;T, D extends DependencyArray = DependencyArray&gt; {
  4. value: T
  5. constructor(
  6. dependencies: D,
  7. derive: DeriveFunc&lt;T, D&gt;,
  8. ) { ... }
  9. }

Here's the catch – I want to restrict derive to only accept dependencies provided by first argument to Derivative constructor.
For example:

  1. const depA = new Dependency&lt;string&gt;()
  2. const depB = new Dependency&lt;boolean&gt;()
  3. // Good: this one compiles
  4. const new Derivative&lt;string&gt;([depA, depB], async ([a, b]) =&gt; `${a}-${b}`)
  5. // Good: these two fail to compile, since arrays aren&#39;t the same length
  6. // TS2345: Source has 2 element(s) but target allows only 1.
  7. const new Derivative&lt;string&gt;([depA, depB], async ([a]) =&gt; `${a}`)
  8. const new Derivative&lt;string&gt;([depA], async ([a, b]) =&gt; `${a}`)
  9. // Bad: in this one, `derive` function doesn&#39;t understand which types are in dependency array, so it merges it to Dependency&lt;string | boolean&gt;[]
  10. // TS2339: Property &#39;length&#39; does not exist on type &#39;boolean | string&#39;.
  11. // Property &#39;length&#39; does not exist on type &#39;false&#39;.
  12. const new Derivative&lt;string&gt;([depA, depB], async ([a, b]) =&gt; `${a.length}-${b}`)

Is there a way to reference exactly the dependencies that were passed to Derivative constructor?

答案1

得分: 1

当前,TypeScript 不支持您尝试的操作,因为数组字面量的类型([1, 5, true, null])不被隐式转化为元组类型([ number, number, boolean, null]),而是转化为数组类型((number | boolean | null)[])。不幸的是,您无法改变这一点。您可以查看一个相关的 问题,它描述了您尝试的操作。

不管怎样,您可以使用扩展语法代替:

  1. class Derivative<T, D extends DependencyArray = DependencyArray> {
  2. value: T
  3. constructor(
  4. derive: DeriveFunc<T, D>,
  5. ...dependencies: D
  6. ) { ... }
  7. }

这样,参数类型将被正确隐式推断。不幸的是,没有其他解决方法,如果您想使用数组,您没有特别的选择。

英文:

Currently, TypeScript doesn't support what you're trying to do, since the types of array literals ([1, 5, true, null]) aren't imlied to tuples ([ number, number, boolean, null]), but instead to an array type ((number | boolean | null)[]). Unfortunately, there's nothing you can do about it. There's an issue you can take a look at, it describes exactly what you're trying to do.

Regardless, you can use the spread syntax instead:

  1. class Derivative&lt;T, D extends DependencyArray = DependencyArray&gt; {
  2. value: T
  3. constructor(
  4. derive: DeriveFunc&lt;T, D&gt;,
  5. ...dependencies: D
  6. ) { ... }
  7. }

In this way, argument types will get implied correctly. Unfortunately, there are no other workarounds, and if you want to use an array, you don't have a particular choice.

答案2

得分: 0

只有翻译部分,不包括代码:

It turns out using objects instead of arrays works just fine.
I had to switch to something like that:

类型 DependencyMap = { [key: string]: Dependency }
类型 DeriveFunc<T, D> = (deps: D) => Promise

const new Derivative({ depA, depB }, async ({ depA, depB }) => ${depA.length}-${depB})

英文:

It turns out using objects instead of arrays works just fine.
I had to switch to something like that:

  1. type DependencyMap = { [key: string]: Dependency&lt;any&gt; }
  2. type DeriveFunc&lt;T, D&gt; = (deps: D) =&gt; Promise&lt;T&gt;
  3. const new Derivative&lt;string&gt;({ depA, depB }, async ({ depA, depB }) =&gt; `${depA.length}-${depB}`)

huangapple
  • 本文由 发表于 2023年2月7日 01:24:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/75364592.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定