一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第9天,点击查看活动详情。
本文的翻译于<<Effective TypeScript>>, 特别感谢!! ps: 本文会用简洁, 易懂的语言描述原书的所有要点. 如果能看懂这文章,将节省许多阅读时间. 如果看不懂,务必给我留言, 我回去修改.
技巧16: 尽量选择 Arrays, Tuples, 和 ArrayLike, 而不是 数字索引
js这门语言是出了名的奇怪. 最臭名昭著的例子:
> "0" == 0
true
但是使用=== 或者使用!== 可以避免上述情况.
js的 object 也很奇怪.我们之前讨论过(见 技巧10) js的 object 的奇怪的地方, 这里我们讨论另一处奇怪的地方.
什么是object? js中, object 就是 key/value 的集合, key通常是 string(es2015后可以是symbol), value 可以是any.
这一点其实是比其他语言更加严格的. 像python, java会有 'hashable' 的object, js没有. 如果在js中使用复杂类型作为索引, 就会被自动转换成 string:
> x = {}
{}
> x[[1, 2, 3]] = 2
2
> x
{ '1,2,3': 1 }
numbers 也不能被用作key. 否则js也会将number转化成string:
> { 1: 2, 3: 4}
{ '1': 2, '3': 4 }
那什么是 arrays, 当然也是objects:
> typeof []
'object'
当然你也可以用数字索引:
> x = [1, 2, 3]
[ 1, 2, 3 ]
> x[0]
1
这些数字索引会被转换成string吗? 是的! 你可以这样尝试:
> x['1']
2
你还可以尝试用Object.keys获取所有的keys:
> Object.keys(x)
[ '0', '1', '2' ]
ts试图区分这些 number key 和 string key , 来让这一切显得更合理. 你可以在lib.es5.d.ts中找到:
interface Array<T> {
// ...
[n: number]: T;
}
有一类ts虚构的错误, 因为在js中string是一定能作为索引的. 但是显示这类错误对编程是有帮助的:
const xs = [1, 2, 3];
const x0 = xs[0]; // OK
const x1 = xs['1'];
// ~~~ Element implicitly has an 'any' type
// because index expression is not of type 'number'
function get<T>(array: T[], k: string): T {
return array[k];
// ~ Element implicitly has an 'any' type
// because index expression is not of type 'number'
}
因为在js运行时, 所有ts代码会被擦除, 所以object.keys 返回依旧是string:
const keys = Object.keys(xs); // Type is string[]
for (const key in xs) {
key; // Type is string
const x = xs[key]; // Type is number
}
最后一步挺让人奇怪的. 因为string不能指定给number.你最好把它考虑成js对实用性的一种让步.但是上述对数组遍历的方式不够好, 如果你不在意 index, 你可以:
for (const x of xs) {
x; // Type is number
}
如果你非常在意index, 你可以使用foreach函数:
xs.forEach((x, i) => {
i; // Type is number
x; // Type is number
});
如果你需要提前跳出循环:
for (let i = 0; i < xs.length; i++) {
const x = xs[i];
if (x < 0) break;
}
所以我们尽量要使用string索引, 尽量不使用number索引.如果需要使用number索引, 那你可以使用Array或者Tuple代替.
如果你的函数想接受Array类型的变量, 但是又不需要要使用Array附带的函数, 比如:push, concat. 那你可以使用ArrayLike:
function checkedAccess<T>(xs: ArrayLike<T>, i: number): T {
if (i < xs.length) {
return xs[i];
}
throw new Error(`Attempt to access ${i} which is past end of array.`)
}
ArrayLike 类型仅仅只有 length 和 number的 index .但是在囧中, index依旧是string类型:
const tupleLike: ArrayLike<string> = {
'0': 'A',
'1': 'B',
length: 2,
}; // OK