定义强制共享值的元组列表

时间:2019-05-11 00:24:59

标签: typescript ecmascript-6

我想指定一个强制上一个元组中的最后一个值与下一个元组中的第一个值匹配的类型,即:

const contiguousIntervals: ThreeContiguousIntervals = [
  [0, 1],
  [1, 2],
  [2, 3],
];

我已经很接近以下定义:

type IntervalMin<TEnd extends number> = [number, TEnd];

type IntervalMax<TStart extends number> = [TStart, number];

type Interval<TStart extends number, TEnd extends number> = [TStart, TEnd];

type ThreeContiguousIntervals<A extends number, B extends number> = [
  NumericIntervalMin<A>,
  NumericInterval<A, B>,
  NumericIntervalMax<B>
];

这可行,但是我必须传递泛型签名中的值:

// works!
const foo: ThreeContiguousIntervals<2, 3> = [
  [0, 2], [2, 3], [3, 4],
];

// breaks as expected
const bar: ThreeContiguousIntervals<2, 3> = [
  [0, 2], [3, 4], [4, 5],
           ^ throws error: "type '3' is not assignable to type '2'"
];

如何获取TypeScript来推断通用签名?

1 个答案:

答案 0 :(得分:1)

上述问题的答案是使用generic辅助函数,并依赖该函数中的类型参数推断,以及各种技巧,以使推断不会太宽泛(例如,您不会不想将您的bar推断为ThreeContiguousIntervals<2|3, 4>,但是可以)。

但这也类似于your other question,所以我不妨给出一个similar answer to it,其中我们支持任意长度的元组,而不是仅支持长度为三的元组...

您可以使用genericmappedconditional类型来表示所需的形状(数字对的元组,其中每个对的第二个元素与第一个元素的类型相同下对的元素)作为对数组类型的约束。这是一种实现方法:

// prepend a value to a tuple.  Cons<1, [2,3]> is [1,2,3]
type Cons<H, T extends any[]> = ((h: H, ...t: T) => any) extends
    ((...l: infer L) => any) ? L : never;

// verify that T is an array of numeric pairs where the last element of
// each pair is the same as the first element of the next pair
type VerifyContig<T> = T extends Array<any> ?
    { [K in keyof T]: [
        K extends '0' ? number :
        Cons<null, T> extends Record<K, [any, infer N]> ? N : never
        , number
    ] } : never;

// helper function to validate that values match the desired shape
const asContig = <N extends number, T extends [N, N][] | [[N, N]]>(
    contig: T & VerifyContig<T>
): T => contig;

asContig([]); // okay
asContig([[1, 2]]); // okay
asContig([[1, 2], [2, 3]]); // okay
asContig([[1, 2], [3, 4]]); // error!
//                 ~ <-- 3 is not assignable to 2
asContig([[1, 2], [2, 3], [3, 5], [5, 8], [8, 13]]); // okay
asContig([[1, 2], [2, 3], [3, 5], [5, 7], [8, 13]]); // error!
//            8 is not assignable to 7 --> ~

看起来不错。希望能有所帮助;祝你好运!