TypeScript中的泛型对象数组

v1l68za4  于 2023-02-17  发布在  TypeScript
关注(0)|答案(3)|浏览(153)
type Foo<T = unknown> = {
  bar: T
  bar2: (v: T) => T
}

const f1 : Foo<number> = {
  bar: 42,
  bar2: (v: number) => 5 + v
}

const f2 : Foo<string> = {
  bar: "hello",
  bar2: (v: string) => v
}

// a function that takes an array of any Foo
function process: (Foo|string)[]) {
 ...
}

process([f1, f2]) // ERROR

它给出以下错误:

Type 'Foo<number>' is not assignable to type 'string | Foo<unknown>'.
  Type 'Foo<number>' is not assignable to type 'Foo<unknown>'.
    Types of property 'bar2' are incompatible.
      Type '(v: number) => number' is not assignable to type '(v: unknown) => unknown'.
        Types of parameters 'v' and 'v' are incompatible.
          Type 'unknown' is not assignable to type 'number'

typescript Playground链接
我不清楚如何达到我的目标:有一个函数,它接受任何T的任何形式的Foo数组。

cbjzeqam

cbjzeqam1#

既然你想让函数参数保持泛型,你需要让函数本身成为泛型(更多信息here
它大概是这样的

// a function that takes an array of any Foo
function extractValues<T>(ar: Foo<T>[]) {
    // ...
}

extractValues([f1]) // this will now work

如果您希望数组接受Foo的混合类型,可以如下键入

function extractValues(ar: (Foo<number> | Foo<string>)[]) {
    // ...
}

extractValues([f1, f2]) // you can now pass a heterogeneous array

如果您必须这样做,它可以接受任何类型的foo,但是使用any违背了typescript的目的,因此它被认为是一种代码气味

function extractValues(ar: Foo<any>[]) {
    // ...
}

extractValues([f1, f2]) // you can now pass a heterogeneous array
bvn4nwqk

bvn4nwqk2#

一种专门针对[Foo<T0>, Foo<T1>, ..., Foo<TN>]输入的 * arrays * 实现这种行为的方法是,将函数generic设置为每个元素的类型参数的元组类型T,也就是说,T将是[T0, T1, ..., TN],并且将函数的输入设置为Map元组类型:

function extractValues<T extends any[]>(
  ar: [...{ [I in keyof T]: Foo<T[I]> | string }]
) {
}

这里,ar参数有一个可变元组类型,它将T元组的每个元素Map到对应的Foo类型(或者我猜是string,如您的示例所示)。
让我们看看它的实际应用:

extractValues([f1, f2, "okay"]) // okay
// function extractValues<[number, string, unknown]>

那就行了编译器推断T是元组类型[number, string, unknown],因此ar应该是[Foo<number> | string, Foo<string> | string, Foo<unknown> | string]类型,它就是[Foo<number> | string, Foo<string> | string, Foo<unknown> | string]类型。注意numberstring是从Map的类型推断的。第三元组元素不能很好地推断并且退回到unknown,但这没关系,因为它是string
如果我们传递了一个错误的输入呢?

extractValues([{ bar: 1, bar2: (v: string) => v.toUpperCase() }, f1, f2]); // error
// --------------------> ~~~~
// function extractValues<[string | number, number, string]>

这里T被推断为[string | number, number, string]。虽然f1f2是正确的,但是作为第一个元素传入的值不是正确的,因为bar2不是(v: number | string) => number | string类型
这也行得通它表明编译器确实以Array<Foo<any>>所没有的方式约束输入。
Playground代码链接

wd2eg0qa

wd2eg0qa3#

我是第一次输入脚本,但这段代码没有抛出任何错误:

type Foo<T = any> = {
  bar: T
  bar2: (v: T) => T
}

const f1 : Foo<any> = {
  bar: 42,
  bar2: (v: number) => 5 + v
}

const f2 : Foo<any> = {
  bar: "hello",
  bar2: (v: string) => v
}

与使用“任何”,它可以调整到什么输入它获得(基本上是一个欺骗类型),但是的,希望这有帮助!

相关问题