我只是在学习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
答案 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>
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
}
请注意如何支持数组接口,但其元素类型是所有元组成员类型的并集。这使得元组比其他语言更宽松;它们(显然)不是纯元组,而是半限制阵列。这也意味着类型检查器必须考虑数组方法(例如pop
,push
等)。因此:
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;
}
});