3

I have a function

function foo() {
    return ["", 1, () => ""];
}

types are: string, number and (() => string)

however after I destructure those values

const [str, num, func] = foo();

compiler treats each variable as type: string | number | (() => string)

I want to pass these variables in functions, but compiler complains that types don't match and my IDE goes nuts with red text underlying. What can I do?

3 Answers 3

9

Specify the return type of the function

function foo(): [string, number, () => string] {
    return ["", 1, () => ""];
}

This will strictly type the function to return an array with:

  • value of index 0 to be type string
  • value of index 1 to be type number
  • value of index 2 to be type () => string / function that returns string
function foo(): [string, number, () => string] {
    return [0, 1, () => ""]; // Will shows error because index 0 is number, not string
}
Sign up to request clarification or add additional context in comments.

4 Comments

That was simple. Thanks :)
I'm guessing this is because TS infers the return type to by an array of any? or unknown? How does TS infer types in this situation?
When you are returning an array from a function, Typescript will automatically set the return type as all of the possible type inside the returned array, that's why @MatasLiu get string | number | (() => string) for each of the returned array value type like explained on @Curtis Fenner's answer, and not any or unknown. If you want to strictly type it like "index 0 is string, index 1 is number, index 2 is function", you need to declare the return type like I did on my answer.
Your alternative is not safe and as is a dangerous operation, I would suggest, it would be good if you remove alternative ;)
6

Another way to achieve this is:

function foo() {
  return ['', 1, () => ''] as const
}

const [str, num, func] = foo()

Main difference with as const and without it is that:

  • as const tells ts that it is immutable value, hence ts infers concrete type which in this case is tuple of 3, and as you know, tuple can have different types
  • without as const, ts infers it as Array and array takes single type parameter, but in this case array is holding string, number and func values. Hence ts infers it as (string | number | () -> string)[]

1 Comment

This is definitely more elegant!
5

By default, TypeScript infers the type of an array literal not as a tuple, but as an array of a union of all of the element types. So in const x = [a, b, c]; isn't inferred to have type [A, B, C] but instead (A | B | C)[].

If you want TypeScript to infer a tuple type, you have to explicitly indicate that:

const x: [A, B, C] = [a, b, c]; // this typechecks

An alternative is to use the as const syntax:

const x = [a, b, c] as const; // infers as `readonly [A, B, C]`

This isn't the only place where TypeScript infers a less-specific type than seems possible. For example, string literals by default infer as string instead of their literal type: let x = "word"; // string vs let x = "word" as const; // "word" vs const x = "word"; // "word"

Comments

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.