在TypeScript中重用函数参数

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

Reusing function parameter in TypeScript

问题

这需要一些解释。

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

class Dependency<T> {
    value: T
    ...
}

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

type DependencyArray = Array<Dependency<any>>
type DeriveFunc<T, D extends DependencyArray = DependencyArray> = (...deps: D) => Promise<T>

class Derivative<T, D extends DependencyArray = DependencyArray> {
    value: T
    constructor(
        dependencies: D,
        derive: DeriveFunc<T, D>,
    ) { ... }
}

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

const depA = new Dependency<string>()
const depB = new Dependency<boolean>()

// 好:这个能够编译通过
const new Derivative<string>([depA, depB], async ([a, b]) => `${a}-${b}`)

// 好:这两个编译失败,因为数组的长度不同
// TS2345: Source has 2 element(s) but target allows only 1.
const new Derivative<string>([depA, depB], async ([a]) => `${a}`)
const new Derivative<string>([depA], async ([a, b]) => `${a}`)

// 不好:在这个例子中,`derive`函数不了解依赖项数组中包含的具体类型,因此将它合并到Dependency<string | boolean>[]中
// TS2339: Property 'length' does not exist on type 'boolean | string'.
//     Property 'length' does not exist on type 'false'.
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):

class Dependency&lt;T&gt; {
    value: T
    ...
}

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

type DependencyArray = Array&lt;Dependency&lt;any&gt;&gt;
type DeriveFunc&lt;T, D extends DependencyArray = DependencyArray&gt; = (...deps: D) =&gt; Promise&lt;T&gt;

class Derivative&lt;T, D extends DependencyArray = DependencyArray&gt; {
    value: T
    constructor(
        dependencies: D,
        derive: DeriveFunc&lt;T, D&gt;,
    ) { ... }
}

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

const depA = new Dependency&lt;string&gt;()
const depB = new Dependency&lt;boolean&gt;()

// Good: this one compiles
const new Derivative&lt;string&gt;([depA, depB], async ([a, b]) =&gt; `${a}-${b}`)

// Good: these two fail to compile, since arrays aren&#39;t the same length
// TS2345: Source has 2 element(s) but target allows only 1.
const new Derivative&lt;string&gt;([depA, depB], async ([a]) =&gt; `${a}`)
const new Derivative&lt;string&gt;([depA], async ([a, b]) =&gt; `${a}`)

// 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;[]
// TS2339: Property &#39;length&#39; does not exist on type &#39;boolean | string&#39;.
//     Property &#39;length&#39; does not exist on type &#39;false&#39;.
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)[])。不幸的是,您无法改变这一点。您可以查看一个相关的 问题,它描述了您尝试的操作。

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

class Derivative<T, D extends DependencyArray = DependencyArray> {
    value: T
    constructor(
        derive: DeriveFunc<T, D>,
        ...dependencies: D
    ) { ... }
}

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

英文:

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:

class Derivative&lt;T, D extends DependencyArray = DependencyArray&gt; {
    value: T
    constructor(
        derive: DeriveFunc&lt;T, D&gt;,
        ...dependencies: D
    ) { ... }
}

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:

type DependencyMap = { [key: string]: Dependency&lt;any&gt; }
type DeriveFunc&lt;T, D&gt; = (deps: D) =&gt; Promise&lt;T&gt;

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:

确定