可以在TypeScript中表达类型“相同类型的值对的数组”吗?

Mar*_*ijn 7 typescript

各个对可能具有不同的类型,但每个对应具有两个相同类型的值。如in [["foo", "bar"], [1, 2]]有效,但[["foo", 2]]无效。因此,[any, any][]过于宽泛。

有点像我想实例化type X<T> = [T, T]每个元素的类型,每个元素都不同T。(X<any>[]再次太宽泛)。

这可能吗?

(这是一个简化的问题,其中的元素是通用接口的实例,通常将其写为文字,我真的很喜欢TypeScript的帮助来捕获单个对象中的类型不匹配。)

jca*_*alz 6

这种事情通常需要表示为受约束的泛型类型,而不是具体类型。因此,任何想要处理这种类型的东西都将需要处理泛型。通常,这意味着您需要帮助函数来验证某些值是否与类型匹配。这是我要执行的操作:

type SwapPair<T> = T extends readonly [any, any] ? readonly [T[1], T[0]] : never;

type AsArrayOfPairs<T> = ReadonlyArray<readonly [any, any]> &
    { [K in keyof T]: SwapPair<T[K]> }

const asArrayOfPairs = <T extends AsArrayOfPairs<T>>(pairArray: T) => pairArray;
Run Code Online (Sandbox Code Playgroud)

该类型SwapPair<T>采用像这样的成对类型[A, B]并将其转换为[B, A](该readonly位只是使其变得更通用;您可以根据需要删除它们。在以上所有内容中,您可以使readonly事情变得可变,并且可以正常工作,并且可能更容易看清是什么)发生)

AsArrayOfPairs<T>采用一个候选类型T,并交换所有成对的属性。这将产生一个新类型,该类型应等于T它是有效的对数组。否则会有所不同。

例如,AsArrayOfPairs<[[number, number]]>将产生readonly (readonly [any, any])[] & [readonly [number, number]]。既然[[number, number]]可以分配给它,那就成功了。但是,AsArrayOfPairs<[[string, number]]>生产readonly (readonly [any, any])[] & [readonly [number, string]],到[[string, number]]没有分配。因为[string, number][number, string]不兼容。

并且辅助函数asArrayOfPairs将验证一个值而不扩大它。让我们看看它的工作原理:

const goodVal = asArrayOfPairs([[1, 2], ["a", "b"], [true, false]]); // okay
// const goodVal: ([number, number] | [string, string] | [boolean, boolean])[]
Run Code Online (Sandbox Code Playgroud)

这可以正常编译,并且将的类型goodVal推断为Array<[number, number] | [string, string] | [boolean, boolean]>。然后这个:

const badVal = asArrayOfPairs([[1, 2], ["a", "b"], [true, false], ["", 1]]); // error!
// error! (string | number)[] is not assignable  ---------------> ~~~~~~~
Run Code Online (Sandbox Code Playgroud)

给出错误,抱怨["", 1]输入。当它尝试并无法推断该条目时,编译器将其扩展到(string | number)[],然后最终放弃说它似乎不是有效的配对类型。这不是我选择的错误消息,但是它显示在正确的位置,这很好。


还有其他方法可以解决此问题,但是我尝试过的大多数较简单的方法均无效。例如,您可以像这样进行映射的元组推断:

const simplerButTooWide = <T extends readonly any[]>(t: [] | { [K in keyof T]: [T[K], T[K]] }) => t;
simplerButTooWide([[1, ""], [true, undefined]]); // no error here!
// [[string | number, string | number], [boolean | undefined, boolean | undefined]]
Run Code Online (Sandbox Code Playgroud)

但是编译器乐于查看type的值[1, ""]并将其推断为一对type [string | number, string | number]。因此,如果推断足够宽,则几乎任何东西都将是“有效”对。防止这种扩展需要一些技巧,例如上面的数组交换,该交换在推断类型之后发生。所以这里有些艺术与科学相对。


无论如何,希望能有所帮助。祝好运!

链接到代码