0

I have a code similar to this:

const x = 'a';
const y = 1;

const arr1 = [x, y];                   // -> (string | number)[]
const arr2: [string, number] = [x, y]; // -> [string, number]

I cant understand why TypeScript's type inference is getting (string | number)[] instead of [string, number].

Do I have to specifically tell TypeScript arr2: [string, number] or maybe there is easier way?

Above example is simple, but problem is more annoying with this:

function useFormField() {
  const [field, setField] = useState('');
  const [fieldError, setFieldError] = useState(false);
  const fieldRef = useRef<HTMLInputElement>(null);
  return [field, setField, fieldError, setFieldError, fieldRef];
}

Type inference: (string | boolean | React.RefObject<HTMLInputElement> | React.Dispatch<React.SetStateAction<string>> | React.Dispatch<React.SetStateAction<boolean>>)[]

So to make it what I need I have to define return type manually:

function useFormField(): [string, React.Dispatch<React.SetStateAction<string>>, boolean, React.Dispatch<React.SetStateAction<boolean>>, React.RefObject<HTMLInputElement>]{
  // ...
}
1
  • 1
    Why not just use an interface instead of organizing structured data in an array? Commented Apr 5, 2019 at 8:41

1 Answer 1

1

Typescript will not generally infer tuples for array literals. There are several ways you can get tuples inferred without explicit types (the versions with explicit types also work fine but you might want to avoid the duplication).

Option 1: Use a generic function

Typescript will infer tuples if we have a type parameter for a rest argument type

function tuple<T extends any[]>(...a: T) {
    return a
}
let r = tuple(1,2,3) // [number, number, number]

Another option, if we want to preserve the array literal syntax is to use a constraint that is both an array and a single element tuple (any | any[]), the single element tuple will hint to the compiler that we want a tuple but the array will allow the tuple to be of any size:

function tuple<T extends [any] | any[]>(a: T) {
    return a
}
let r = tuple([1,2,3]) // [number, number, number]

Option 2: Use as const (for TS 3.4 and higher)

3.4 Add what is called a const assertion. This has several side effects one of which is to infer tuples. The other is to infer a read-only tuple and to infer literal types for all literals, this might make this solution not as general propuse as the function but you can mix and match as needed:

let r = [1,2,3] as const // [1,2,3]
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.