2

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?

J. Hesters
  • 13,117
  • 31
  • 133
  • 249

3 Answers3

3

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

Link
  • 1,198
  • 4
  • 14
  • Thank you Link! It is not an array of `References` though. It is either a tuple (triple) with the 3 values, or an empty array. If I cast it like this: `[] as Reference`, I get the error `Conversion of type '[]' to type 'Reference' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. Source has 0 element(s) but target requires 3.`. Any idea how to fix this? – J. Hesters Dec 08 '20 at 18:49
  • So `const reference = ([] as unknown) as Reference;` worked. I don't know though, if that's the best way to handle this. – J. Hesters Dec 08 '20 at 18:53
  • I've updated my answer, can you please try the solution I suggested. And please try it without using `as Reference` if it doesn't throw you an error for it. – Link Dec 08 '20 at 19:04
  • Thank you Link. The types you suggest unfortunately aren't correct for my instance. (It is not an array of three references. The reference type is a tuple of three values or an empty array. – J. Hesters Dec 08 '20 at 19:37
  • Yes, in my answer above, I am creating a tuple of 3 values of type Reference. And, in order to assign the value of that tuple to an empty array, you need to do this: `const reference : [Reference, Reference, Reference] = ([] as unknown) as [Reference, Reference, Reference]` – Link Dec 08 '20 at 19:53
  • I have updated my answer to include this. Also, here is the TypeScript documentation for tuples just for reference : https://www.typescriptlang.org/docs/handbook/basic-types.html – Link Dec 08 '20 at 19:56
0

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];

t.animal
  • 3,012
  • 1
  • 24
  • 25
0

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,
];
Evert
  • 93,428
  • 18
  • 118
  • 189