使用可选的first和last元素定义元组列表 [英] Defining a list of tuples with optional first and last elements

查看:89
本文介绍了使用可选的first和last元素定义元组列表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在TypeScript中对如下数据进行建模:

I'm trying to model some data in TypeScript that looks like this:

// Sometimes we lookup the min/max dynamically, so we have keywords for them.
const intervalA: NumericInterval = [['$min', 3], [3, 5], [5, '$max']];

// Other times, we know the min/max and provide them inline
const intervalB: NumericInterval = [[0, 3], [3, 5], [5, 7]];

我尝试将NumericInterval定义为:

type MinInterval = ['$min', number];
type MaxInterval = [number, '$max'];
type Interval = [number, number];
type NumericInterval = [MinInterval?, ...Interval[], MaxInterval?];

但是,TypeScript是不喜欢它的,因为A rest element must be last in a tuple.

However, TypeScript is doesn't like it because A rest element must be last in a tuple.

有没有更好的方式来表达这种模式?

Is there a better way to express this schema?

推荐答案

据我所知,无法直接表示此类型.它是具体类型Array<["$min" | number, number | "$max"]>的子类型,它太宽了,并且允许[[2, "$max"],[4, "$max"],["$min", 6]]之类的东西.

There's no way to represent this type directly as far as I know. It is a subtype of the concrete type Array<["$min" | number, number | "$max"]>, which is too wide and allows things like [[2, "$max"],[4, "$max"],["$min", 6]].

可以使用通用已映射

You can use generic, mapped, and conditional types to represent the desired shape as a constraint on array types, but it's fairly ugly/tedious/complex and you'd have to make anything which produces or accepts the type be generic. I might as well show one way to do it, without much explanation (I can always edit in a more thorough explanation if you actually care about it or want to use this solution):

// get the tail of a tuple: Tail<[1,2,3]> is [2,3]
type Tail<L extends any[]> = ((...x: L) => any) extends
    ((h: any, ...t: infer T) => any) ? T : never;

// verify that T is a valid NumericInterval
type VerifyNumericInterval<T> = T extends Array<any> ?
    { [K in keyof T]: [
        K extends '0' ? "$min" | number : number,
        K extends keyof Tail<T> ? number : number | "$max"
    ] } : Array<["$min" | number, number | "$max"]>

// helper function to ensure parameter is a valid NumericInterval
const asNumericInteral = <T extends any[] | [any]>(
    numericInterval: T & VerifyNumericInterval<T>
): T => numericInterval;

让我们对其进行测试:

asNumericInteral([]); // okay, zero length tuple
asNumericInteral([[1, 2]]); // okay
asNumericInteral([["$min", 2]]); // okay
asNumericInteral([[1, "$max"]]); // okay
asNumericInteral([["$min", "$max"]]); // okay, not sure if you want it to be
asNumericInteral([["$max", 2]]); // error!
//                 ~~~~~~ <-- string not assignable to never
asNumericInteral([[1, 2], [3, "$max"]]); // okay
asNumericInteral([["$min", 2], [3, "$max"]]); // okay
asNumericInteral([["$min", 2], [3, "$max"], [5, 6]]); // error!
//                                 ~~~~~~ <-- string not assignable to number

这一切都符合我对该类型的期望.顺便说一下,这仅适用于期望NumericInterval类型的函数的调用者.在具有泛型类型T & VerifyNumericInterval<T>的值的内部实现中,您可能必须自己处理边缘情况.编译器几乎不可能推理出未解决的泛型类型,足以引起注意,例如:

This all behaves as I'd expect for that type. By the way, this will only be of use for callers of functions that expect NumericInterval types. Inside implementations that have a value of generic type T & VerifyNumericInterval<T> you will likely have to deal with edge cases yourself. There's little chance that the compiler would be able to reason about an unresolved generic type well enough to notice, say, the following:

function hmm<T>(numInt: T & VerifyNumericInterval<T>) {
    for (let i = 0; i < numInt.length; i++) { // okay, numInt is known to be aray
        const interval = numInt[i]; // interval is type [number | "$min", "$max" | number]
        if (i !== 0) { // can't be "$min", right?
            interval[0].toFixed(); // error?! 
            // but it has to be a number, why doesn't the compiler know it?!
        }

        // manually check
        if ((i !== 0) && (typeof interval[0] === "number")) {
            interval[0].toFixed(); // okay now
        } 
    }
}

在该函数中,您知道除i === 0之外,numInt[i]是一对,其中第一个元素肯定是number.但是编译器无法解决问题,因此您必须通过额外的检查(或使用类型断言)来帮助它.

In that function you know that except for i === 0, numInt[i] is a pair where the first element is definitely a number. But the compiler can't figure it out, so you have to walk it through extra checks (or use type assertions) to help it.

好的,希望能有所帮助.祝你好运!

All right, hope that helps. Good luck!

这篇关于使用可选的first和last元素定义元组列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆