TypeScript: Typing array / tuple with values or empty

Viewed 1319

I'm trying to type a custom React hook:

import { useRef } from 'react';

type Reference = [
  Promise<unknown>,
  (value?: unknown) => void,
  (reason?: unknown) => void,
];

const usePromise = () => {
  const reference: Reference = [];
  const container = useRef(reference);

  reference[0] = new Promise((resolve, reject) => {
    reference[1] = resolve;
    reference[2] = reject;
  });

  // [promise, resolve, reject]
  return container.current;
};

export default usePromise;

TypeScript complains about reference saying:

Type '[]' is not assignable to type 'Reference'.
  Source has 0 element(s) but target requires 3.

How can I allow TypeScript to accept the empty array / tuple initialization, too? And also maybe there is a way to give usePromise the type of the value, too, so that it doesn't say unknown?

3 Answers

Edit 2:

If you want to initialize the tuple to an empty array, you can do this:

const reference : [Reference, Reference, Reference] = ([] as unknown) as [Reference, Reference, Reference];

Edit: edited as per OP's comment

You need to do something like this if you want to use a tuple: Please read the inline comments below.

type Reference = {
    name:  string;
    age: number;
}

//In your case, it should be something like this beause your tuple may have upto 3 values of type Reference.

//initialization
//null! - the '!' mark after null is the non-null assertion operator, which will let you assign null value to a type Reference.
const reference2: [Reference, Reference, Reference] = [null!, null!, null!];
//assignment
reference2[0] = {name: 'John', age:10};

Original Answer: That is because you have declared a const variable reference which is of type Reference (object instance of a class, type or interface called Reference) and but initializing it with an empty array [ ].

If you want reference to have an array of Reference objects, then you should do this instead:

const reference: Reference[] = []; 

If you have strict type checking enabled in your tsconfig, you should do it like this:

const reference: Reference[] = [] as Reference[];

You can read about the as keyword here: Stack Overflow question about the 'as' keyword

The type Reference is a tuple with three entries. So when you assign to a variable of the type Reference, typescript checks if 1) the assigned value is a tuple 2) if the assigned tuple has the desired length 3) if all types of the entries in the tuple match the corresponding types in the Reference type.

You assign an empty list, which violates these conditions. If you don't have strict null checks enabled, you can simply assign const reference: Reference = [null, null, null];

Instead for forcing typescript to ignore your problem, I feel it's better to describe reference as what it really is:

let reference: Reference|null = null;

Or:

type Reference = [
  Promise<unknown> | null,
  ((value?: unknown) => void) | null,
  ((reason?: unknown) => void) | null,
];
Related