Repository navigation
Should the literal number value types of const-asserted arrays' indices be inferred? #34589
Description
Activity
Partial workaround for
indexOf,const days = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'] as const; /** "Mo" | "Tu" */ type FirstTwoDays = (typeof days)[1 | 2] /** 7 */ type DaysCount = typeof days.length; type PopFront<ArrT extends readonly any[]> = ((...arr: ArrT) => void) extends ((_: any, ...tail: infer TailT) => void)? TailT : never ; type PushFront<TailT extends readonly any[], HeadT> = ((head : HeadT, ...arr: TailT) => void) extends ((...arr: infer ArrT) => void)? ArrT : never ; type IndexOf< ArrT extends readonly any[], ValueT extends ArrT[number], IndexTHack extends readonly any[] = [], ResultT extends number = never > = { 0: number, 1: ResultT, 2: IndexOf< PopFront<ArrT>, ValueT, PushFront<IndexTHack, any>, ResultT|(ValueT extends ArrT[0] ? IndexTHack["length"] : never) > }[ number extends ArrT["length"] ? 0 : ArrT["length"] extends 0 ? 1 : 2 ] ; declare function indexOf<ArrT extends readonly any[], ValueT extends ArrT[number]>( arr: ArrT, value : ValueT ): IndexOf<ArrT, ValueT>; //2 const tu = indexOf(days, 'Tu' as const) //2|3 const tuWe = indexOf(days, 'We' as 'Tu' | 'We') //0|5 const suFr = indexOf(days, 'Su' as 'Su'|'Fr')
For "number keys",
const days = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'] as const; /** "Mo" | "Tu" */ type FirstTwoDays = (typeof days)[1 | 2] /** 7 */ type DaysCount = typeof days.length; type PopFront<ArrT extends readonly any[]> = ((...arr: ArrT) => void) extends ((_: any, ...tail: infer TailT) => void)? TailT : never ; type PushFront<TailT extends readonly any[], HeadT> = ((head : HeadT, ...arr: TailT) => void) extends ((...arr: infer ArrT) => void)? ArrT : never ; type NumberKeysOf< ArrT extends readonly any[], IndexTHack extends readonly any[] = [], ResultT extends number=never > = { 0: number, 1: ResultT, 2: NumberKeysOf< PopFront<ArrT>, PushFront<IndexTHack, any>, ResultT|IndexTHack["length"] > }[ number extends ArrT["length"] ? 0 : ArrT["length"] extends 0 ? 1 : 2 ] ; //type k = 0 | 1 | 2 | 3 | 4 | 5 | 6 type k = NumberKeysOf<typeof days>;
Damn. That was an astoundingly clever usage of spread function params as a vehicle for recursively building an array! Tried it out myself and it indeed works. Then I was reminded of a talk (i think) anders hejlsberg had where he mentioned TypeScript having some internal recursion limit, and I was able to confirm: for me,
NumberKeysOfbreaks down for an arrayas constat around the length of 42 (intelligently defaulting tonumberbecauseType instantiation is excessively deep and possibly infinite)I guess even if it had worked, it would've been a masterly albeit roundabout way of achieving what I thought would be a lightweight inference task. As you can see I am not a TypeScript expert lol
There are ways to work around the depth limit but those are a bit out of scope =x
I agree it would be nice to get TS to just give us the number literal keys of a tuple without hackery.
A nicer way to type
indexOf()would be nice, tooThe workarounds above are... Not great
- addedAwaiting More FeedbackThis means we'd like to hear from more people who would be helped by this featureThis means we'd like to hear from more people who would be helped by this featureSuggestionAn idea for TypeScriptAn idea for TypeScript
on Oct 30, 2019 RyanCavanaugh commented
on Oct 30, 2019 MemberMore actionsThis one is kind of problematic and would need a bunch of special handling, or new type system primitives we currently don't have.
Fundamentally,
keyofis an operation that returns string literal types, because that's how JS properties work. ButforEachgives numeric values to theindexargument, and obviously only includes the array indices themselves.getting the literal number types for
as constarrays is a little easier with template literal types
Search Terms
array tuple const assertion literal index number
Suggestion, Use Cases, and Examples
I would like to infer the index type of a
const-asserted array to be that of its position in the array, rather than justnumber(i.e. I want the literal number type). However, I can't seem to create a way to extract this into atype, even though I know TypeScript is holding onto this information somewhere.For example, this code follows my intuition:
Yet here,
indexis of typenumber. Although this is understandable, given the signature ofArray['forEach'], it begs the question - how could I assert, as theas constdoes, thatindexshould be of type0 | 1 | 2 | 3 | 4 | 5 | 6?Likewise, how could I get this to "work"?
The closest I can get to achieving my desired
typeis withbut it's not perfect:
Checklist
My suggestion meets these guidelines: