打字稿:数字[]和[数字,数字]之间有什么区别?

时间:2016-09-01 14:19:02

标签: typescript

我只是在学习Typescript并尝试理解类型和接口的最佳实践。我正在玩一个使用GPS坐标的例子,想知道一种方法是否比另一种更好。

let gps1 : number[];
let gps2 : [number, number]
interface Coordinate {
   0: number,
   1: number
}
let gps3 : Coordinate;

我想更大的问题是输入一个固定大小和数组的数组是否有价值。类型。因为Typescript不允许我在运行时轻松测试某个值是否属于某种类型,所以正确吗? (即从JSON字符串中反序列化)

gps3 = [1,3]
let check = gps3 instanceof Coordinate;  // doesn't work

6 个答案:

答案 0 :(得分:3)

在您的示例中,gps1是一个数字数组,gps2是两个数字的a tuple

  

元组类型允许您表示一个固定类型的数组   元素的数量是已知的,但不必相同。

gps3 instanceof Coordinate不起作用的原因是Coordinate是一个接口而不是实际的运行时类型。
键入接口的编译器用户检查你的代码,但它没有被翻译成javascript。

您可以为此创建type guard

interface Coordinate {
   0: number,
   1: number
}

function isCoordinate(obj: any): obj is Coordinate {
    return obj instanceof Array && obj.length === 2 && typeof obj[0] === "number" && typeof obj[1] === "number";
}

let a;

if (isCoordinate(a)) {
    console.log(a[0]);
}

答案 1 :(得分:1)

首先,您检查的原因是,接口仅存在于TypeScript中。在它作为JavaScript执行的那一刻,不再有任何接口Coordinate。如果你想使用这样的东西,你需要创建一个类并用new来调用它。

class Coordinate extends Array<number> {}

let gps3 = new Coordinate();

gps3.push(1);
gps3.push(3);
let check = gps3 instanceof Coordinate;

我个人总是使用Array<number>

  1. 此表示法也用于其他语言
  2. 当使用其他泛型时,例如Observables,你也必须使用它
  3. 它更清晰,你不会对number[] [number][number, number]产生这种混淆,我认为它基本相同。 好的,因为其他答案正确显示它不一样。

答案 2 :(得分:1)

似乎元组[number, number]并不限制值的数量。它就像number[],但至少有两个值:

let gps2 : [number, number];
gps2 = [1, 2, 3]; // ok
gps2 = [1, 2, 'abc']; // Error: Type [number, number, string] is not assignable to type [number, number]
gps2 = [1]; // Error: Type [number] is not assignable to type [number, number]

对于两个第一个值,带有数字键的界面与[number, number]类似,但它不能确保下一个值的类型:

interface Coordinate {
    0: number,
    1: number
}
let gps3 : Coordinate;
gps3 = [1, 2]; // ok
gps3 = [1, 'abc']; // Error: Type [number, string] is not assignable to type 'Coordinate'
gps3 = [1, 2, 'abc']; // ok

答案 3 :(得分:1)

Typescript不允许对接口进行运行时类型检查。相反,您可以通过将 x <- unique( melt(d.test, id.vars = c("sequence", "foo"), measure.vars = c("real"), variable.name = "model", value.name = "output" )) d.test$real<-NULL names(x) <- names(d.test) rbind(d.test, x) 定义为类来使用第3种方法。

Coordinate

此外,typescript旨在强制静态类型,否则动态键入javascript。但是你的方法2并不一定将数组的大小限制为2个元素,class Coordinate { longitude: number; latitude: number; constructor(long, lat) { this.longitude = long; this.latitude = lat; } } let gps3 = new Coordinate(1,2); if(gps3 instanceof Coordinate) // true 仍然是一个有效的参数(尽管它的值是gps2[10]类型)。

为了便于阅读,我建议在我的回答中使用上述方法。

答案 4 :(得分:1)

除了@ Paleo关于数组和元组之间差异的答案,Unicode escapes元组,数组和索引接口之间的关系已经形式化,因此我们现在可以安全地准确地说出元组在其他特性方面是什么。 (我认为这没有改变任何东西,但在编译器将元组作为特殊情况处理之前)。

元组[T0, T1, T2]现在完全等同于:

interface Tuple<T0, T1, T2> extends Array<T0 | T1 | T2> {
    0: T0,
    1: T1,
    2: T2
}

请注意如何支持数组接口,但其元素类型是所有元组成员类型的并集。这使得元组比其他语言更宽松;它们(显然)不是纯元组,而是半限制阵列。这也意味着类型检查器必须考虑数组方法(例如poppush等)。因此:

const a: [number, number] = [1, 2, "3"];

这是类型错误,因为pop的{​​{1}}方法返回[1, 2, "3"],而number|string应该只返回a.pop

在界面中包含number可能会导致in a forthcoming version of TS出现问题,并且有一个other future work建议进行重大更改以删除它,从而使元组更加严格,即固定长度没有Array等。

答案 5 :(得分:0)

也许我有点晚了,但最佳做法可能是使用ECMA6 Proxy

    latLngLiteral = new Proxy({},{
        set: function(obj, prop, val) {
            //only these two properties can be set
            if(['lng','lat'].indexOf(prop) == -1) {
                throw new ReferenceError('Key must be "lat" or "lng"!');
            }

            //the dec format only accepts numbers
            if(isNaN(val)) {
                throw new TypeError('Value must be numeric');
            }

            //latitude is in range between 0 and 90
            if(prop == 'lat'  && !(-90 < val && val < 90)) {
                throw new RangeError('Position is out of range!');
            }
            //longitude is in range between 0 and 180
            else if(prop == 'lng' && !(-180 < val && val < 180)) {
                throw new RangeError('Position is out of range!');
            }

            obj[prop] = val;

            return true;
        }
    });