So technically, I have already figured out an answer to this question, but am looking for a cleaner approach. Similarly, I haven't seen any SO questions or documentation specifically laying out how to do this.
First, an example to then build upon: I know that if I have a typed array, I can infer the type of each value in this way:
// just with types
type Tuple = [item1: number, item2: string];
type Item1Type = Tuple[0]; // type is number
type Item2Type = Tuple[1]; // type is string
// or with `typeof`
const myTuple: Tuple = [1, 'one'];
type InferredItem1Type = typeof myTuple[0]; // type is number
type InferredItem2Type = typeof myTuple[1]; // type is string
If I have a generic like this though and all I have access to is an instance of this type:
// the generic type
type GenericObjectWithList<T> = {
list: T[];
};
// the instance i have access to
const object: GenericObjectWithList<string> = {
list: ['one', 'two', 'three'],
};
I want to infer the type of the inner list.
// infer the type of the list
type InferredListType = typeof object['list'][0]; // type is string
As you can see, I've already figured out that I can infer this type similar to the approach in my first example, but I'm wondering, is there a cleaner way to infer this type? I would expect there to be a way to infer the single value type of a list without needing to do [0]
. If there isn't a another way to do this, would someone mind explaining why this approach is the only way we'd want this to be the way to infer an array value's type?
CodePudding user response:
- List item
Arrays are indexed by a number
, which means you can drill into an array with number
in order to get the value types from that array.
ArrayType[number]
is typically how you would do this. Indexing a non tuple array by zero is pretty much the same thing, but it hints to the programmer that the indexed value would be different for different numbers, which isn't right.
type StrArr = string[]
type ArrVal = StrArr[number] // string
Or from your code:
type InferredListType = typeof object['list'][number]; // type is string
This works with tuples, too.
type MyTuple = [123, 'asd']
type TupleVal = MyTuple[number] // 123 | 'asd'
But, of course, you lost the information of the position of each type in that tuple. If you need that, that's where you would use [0]
, [1]
, etc, instead.
CodePudding user response:
TL;DR typeof object['list'][number]
You can use the type of the index instead of a literal value when indexing types. This works for any indexable object.
So, if you have
const object = {
list: ['one', 'two', 'three'],
};
you can infer the type of that list
property like this:
type InferredListType = typeof object['list'][number]; // note that `number` instead of `0`
Better yet, you can use keyof
to get that index type:
type InferredListElementType = object['list'][keyof object['list']];
However, as you can see, this can be quite verbose for anything more than a single layer of nesting.
But don't forget keyof
: it's a powerful tool.