Open 747 opened 11 months ago
In newer versions of TS you can do this:
let x = [1, 2, 3, 4, 5] satisfies [number, ...number[]];
Just writing this off the top of my head so I may have made a mistake, but if I did that right it should automatically infer a tuple type based on the length of the array literal.
Wow, quite formidable but it works as type definition too.
let tup: [number, ...number[]] & {length: 12} = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];
Thank you for letting me know.
I still like the original idea, as a clearer way to write these types. Though I don't know how common big tuples are; do you have examples? Lately I name each item in the tuple and *
wouldn't let you do so.
Though I don't know how common big tuples are;
Yes, that's the point. I have only needed once so far, and have no idea if it is any common pattern.
do you have examples?
What I can easily think of is:
enum St { White, Black, None }
type GoBoard = [
[St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St],
[St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St],
[St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St],
[St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St],
[St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St],
[St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St],
[St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St],
[St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St],
[St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St],
[St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St],
[St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St],
[St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St],
[St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St],
[St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St],
[St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St],
[St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St],
[St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St],
[St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St],
[St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St, St],
]
would benefit a lot from
type GoBoard = [[St * 19] * 19]
But arguably you only use this type of definition once or so in your program.
Here's the one use case where I had a long-ish tuple: https://github.com/bbrk24/spe-lib/blob/e1453e1f3595ca00394cb098bee90a26546e0358/src/PhonemeStringMatcher.ts#L246-L248
static subscripts = (
['₀', '₁', '₂', '₃', '₄', '₅', '₆', '₇', '₈', '₉']
) satisfies [string, ...string[]];
Some related effort: https://millsp.github.io/ts-toolbelt/modules/list_repeat.html We've wanted to provide more convenient syntax for ts-toolbelt-style features, and this seems like a good first.
Note that Python would look more like [number] * 12
instead of [number * 12]
. But I think Python's style of multiplication would be harder to implement at compile time; we'd need to do something more like List/Repeat
except when the tuple is right there next to the *
. Also note that [number] * 12
isn't as useful unless we had a concatenation operator like [number] * 12 + [string] * 12
, paralleling List/Concat. But we should consider now whether we want *
to only work within tuples ([number * 12]
), as originally proposed, or to operate on tuple types ([number] * 12
). One advantage of the latter would be the ability to write [number, string] * 12
(meaning [number, string, number string, ...]
).
If it isn't too much for parser, restricting []
to only arrays and tuples might be good, so that
[number * 12]
→ [number, number, number, ...]
[(number, string) * 12]
→ [number, string, number, string, ...]
[[number, string] * 12]
→ [[number, string], [number, string], ...]
[number * 12, string * 12]
→ [number, ..., string, ...]
I managed to get this working for the first case of [number * 12]
, but not for the alternating case of [(number, string) * 12]
:
// Adapted from https://stackoverflow.com/a/50641073/6253337
UnionToIntersection<U> ::= if (if U < any then (k: U) =>) < ((k: infer I) =>) then I
NoUnion<Key> ::=
if [Key] < [boolean] then boolean
else if [Key] < [UnionToIntersection<Key>] then Key
SpecificNumber<N < number> ::= unless number < N then NoUnion<N>
Tuple<T, Length < number> ::= T[] & length: SpecificNumber<Length>
This even allows functions to be generic over tuple length:
function zip<T1, T2, Length < number>(
arr1: Tuple<T1, Length>,
arr2: Tuple<T2, Length>
): Tuple<[T1, T2], Length> {
arr1.map((el, i) => [el, arr2[i]]) as Tuple<[T1, T2], Length>
}
Then, zip [1, 2, 3] as tuple, ["a", "b", "c"] as tuple
compiles (Length = 3
), but zip [1, 2, 3, 4] as tuple, ["a", "b", "c"] as tuple
gives a type error at compilation time, pointing at the first tuple.
As far as I have searched, there seems no other way to declare tuple in TS beside:
I wonder if it is beneficial to let us write like:
or go further by