Closed Zarel closed 6 years ago
I think the general case you're seeing is a design limitation of our analyses not seeing far enough where these values get used. But see #4988 for more relevant discussion on the idea of slice()
returning this
.
I think I understand the problem. When inferring type, tuples should degrade to arrays, so in situations like this:
let a = [1, 2];
a
is inferred to be number[]
rather than [number, number]
, so that later, a.push(3);
isn't an error.
I think a simple workaround might be to prefer tuples to arrays of multiple type.
So [1, 2]
would be inferred as number[]
rather than [number, number]
, but [1, '']
would be inferred as [number, string]
rather than (number | string)[]
.
It might be different from others, but in my experience, multi-type tuples are much more common than multi-type arrays, and I would rather specify the latter explicitly than the former.
I guess the biggest problem with that approach might be backwards compatibility, though...
but
[1, '']
would be inferred as[number, string]
rather than(number | string)[]
.
I think there is no way for the compiler to know that you what to do a tuple instead of an array. Either case, you could check the stricter tuple PR, that probably will be available in next release. #17765. Still, I don't think the compiler should mark a tuple a initialized array.
In hindsight, I think mutable array literals should be pretty rare. If I write a non-empty array literal, I usually want it to be either immutable or a tuple.
The times I want to initialize a non-empty array and keep it mutable are rare enough that I'd probably rather have TypeScript infer it's a tuple by default and explicitly type them if I want them mutable, than have TypeScript infer it's mutable by default and explicitly type them if I want them to be a tuple.
Perhaps a compiler option to infer all non-empty array literals as tuples by default?
@Zarel mutable array literals are rare but we are constrained by backward compatibility for those rare cases (see #17765 for exactly such a case that we ran into with firebase). There may be other reasons to make array literals immutable, but tuple usage is also quite rare [1], so we would need other, stronger motivations.
[1] As far as I can tell from sources like definitely typed and our user-code tests. Javascript's object literal support is close enough to tuples that it seems like most people don't bother.
I know, which is why I'm asking for it to be behind a compiler flag.
A flag isn't a great choice because the choice of tuple vs not is really a global decision - some arrays should be tuples, some aren't. In any case this problem isn't worth doubling the configuration space over.
TypeScript Version: 2.7.0-dev.20171226
Code
The above is an example, but it's not the only example of where TypeScript has trouble with tuples.
Others include:
The above one is harder, but it'd be great if
array.slice()
andarray.slice(0)
could be used to clone tuples.https://github.com/Zarel/Pokemon-Showdown/blob/2b5654e307d7d65ee16c8bf55961b352c97c96e9/users.js#L1438
The above linked example disappeared when I tried to construct a simple equivalent testcase:
But if you care to clone that repo, checkout that commit, and delete the
// @ts-ignore
, you'll get a similar error message as the others, where the tuple degrades to an array.Expected behavior:
No errors
Actual behavior: