9

Given this slightly artificial example:

['List', 'Of', 'Names']
        .map((name, index) => [name, index % 2])
        .map(([name, num]) => );

why is name and num in the last line of type string | number obviously inferred as an Array of strings and numbers and do any of you know if there is a way to use type inference so that name is a string and num is a number respectively?

2 Answers 2

20

You can use a const assertion:

['List', 'Of', 'Names']
    .map((name, index) => [name, index % 2] as const) // insert `as const` here
    .map(([name, num]) => { }); // name: string, num: number

Take a look at the playground sample.

Sign up to request clarification or add additional context in comments.

3 Comments

Very elegant! This is especially useful when you want to extend useState result in React but keep similar typing, eg adding a side-effect the the function that change the state. The result becomes readonly though.
This should be the answer, given that it is the current way to solve this problem albeit the prior answer does have historical information.
A downside of this solution is that it produces a readonly tuple. If that's not an issue - works great!
13

For arrays literals type inference does not infer tuples, it infers arrays, so

var foo = ["", 0]; // foo is Array<string | number> not [string, number]

I have not found documentation on this but the pull request adding support for tuples never uses inference when declaring them, I'm guessing this is deliberate.

In your case you can specify the type parameter:

['List', 'Of', 'Names']
        .map<[string, number]>((name, index) => [name, index % 2])
        .map(([name, num]) => name + "");

2.9 and below solution

Or create a tuple helper function if this is a common issue for you:

function tuple<T1, T2, T3, T4, T5>(data: [T1, T2, T3, T4, T5]) : typeof data
function tuple<T1, T2, T3, T4>(data: [T1, T2, T3, T4]) : typeof data
function tuple<T1, T2, T3>(data: [T1, T2, T3]) : typeof data
function tuple<T1, T2>(data: [T1, T2]) : typeof data
function tuple(data: Array<any>){
    return data;
}

['List', 'Of', 'Names']
        .map((name, index) => tuple([name, index % 2]))
        .map(([name, num]) => name + "");

3.0 Solution

Since I posted the original answer typescript has improved it's inference with the ability to infer tuple types for rest parameters. See PR for details. With this feature we can write a shorter version of the tuple function :

function tuple<T extends any[]> (...data: T){
    return data;
}

['List', 'Of', 'Names']
        .map((name, index) => tuple(name, index % 2))
        .map(([name, num]) => name + "");

1 Comment

Explicit typing is what I wanted to avoid. I could also use an object and use the object destructuring .map(({name, num}) => ...) but also not very concise. Thx for the idea with the tuple func!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.