输入泛型推断类型数组

问题描述 投票:0回答:2

我正在尝试创建对象数组的类型。该对象的第一个和第二个键需要匹配。例如:

[{ 
  key1: "hi",
  key2: "world"
},{
  key1: 1,
  key2: 2
},{
  key1: true,
  key2: false
}]

这是我想出来的,但它并不完全有效。我有一个通用类型来定义数组中的对象。当调用它生成数组类型时,会引发错误。

type ArrayItem<T> = {
  key1: T,
  key2: T
}

// This raises an error Generic Type ArrayItem requires 1 type argument
type Array = ArrayItem<T>[]

输入这样的嵌套对象的最佳方法是什么(支持类型推断)?

typescript generics nested type-inference inference
2个回答
5
投票

如果

T
中没有
ArrayItem<T>
的可能类型的有限列表,则 TypeScript 中没有与
Array<ArrayItem<T>>
相对应的具体类型。 要将此类事物表示为非泛型类型,需要诸如存在类型之类的东西,而 TypeScript 并不直接支持这种类型。

(如果您do有一个有限列表,例如

ArrayItem<string> | ArrayItem<number> | ArrayItem<boolean>
,那么您可以像其他答案一样使用并集。)

在 TypeScript 中最接近这一点的是作为泛型类型,而在推理和编译器警告方面你能做的最好的事情就是将其表示为类似泛型约束的东西。

实现此目的的一种方法是编写一个通用辅助函数

asMyArray()
接受一个元组,编译器将检查元组的每个元素以确保它满足约束。 一个障碍是,如果您允许像
{key1: "hi", key2: 2}
这样的东西作为 string | number,那么
T
确实
满足约束。 为了防止编译器愉快地接受所有类型对,我将尝试使其仅从
T
推断
key1
(请参阅 microsoft/TypeScript#14829 以了解防止从特定推断站点推断的方法),并且然后检查
key2
是否匹配:

type NoInfer<T> = [T][T extends any ? 0 : 1]

const asMyArray = <T extends readonly any[]>(
    x: [...({ [K in keyof T]: { key1: T[K], key2: NoInfer<T[K]> } })]) =>
    x;

泛型类型参数

T
是一个元组,对应于传入数组中每个元素的
key1
值。 传入的数组
x
映射元组类型
& {}
位会降低
key2
的推理优先级。
[... ]
位只是提示编译器推断一个元组而不是一个数组(它无法区分不同的元素),让我们测试一下:

const myArray = asMyArray([{
    key1: "hi",
    key2: "world"
}, {
    key1: 1,
    key2: 2
}, {
    key1: true,
    key2: false
}])
// const asMyArray: <[string, number, boolean]>(...)

您可以看到

T
被推断为
[string, number, boolean]
。 此方法成功,而以下以相同方式推断
T
的方法失败:

const badArray = asMyArray([{
    key1: "hi", key2: 123 // error!
    // -------> ~~~~
    // number not assignable to string
}, {
    key1: 1, key2: "world" // error!
    // ----> ~~~~
    // string not assignable to number
}, {
    key1: true, key2: false
}]);

看起来像你想要的。

Playground 代码链接


1
投票

处理数组可能会很混乱,即使是通用类型也是如此。这在很大程度上取决于数组初始化后将如何使用您的项目。根据您的代码片段,我将开始为数组中的每种“类型”条目创建接口,以便为每个不同的属性集获得强类型。

export interface FooItemType {
  key1: string,
  key2: string,
}


export interface BarItemType {
  key1: boolean,
  key2: boolean,
}

然后,您可以创建一个新类型来映射您之前定义的接口。

export type ItemType = BarItemType | FooItemType;

之后,您可以将其声明为简单的

ItemType
数组。

export myArr: ItemType[] = [{ 
  key1: "hi",
  key2: "world"
},{
  key1: 1,
  key2: 2
},{
  key1: true,
  key2: false
}]

虽然这种方法是强类型的,但在从数组中获取

myArray[i]
项后,可能会导致一些手动转换。有时,我们在考虑“类型”在应用程序中的用法之前就设计了“类型”,因此必须在设计数据结构时将其作为一个整体来设计。

© www.soinside.com 2019 - 2024. All rights reserved.