TypeScript typing for Array spread as Object

Viewed 186

I'm spreading an Array into an object to extend some properties... the problem I'm encountering is basically as follows:

const arr = [1, 2]
const obj = {...arr};

// TypeScript fails to catch this error
obj.forEach(v => console.log(v))

Playground Link

The resulting obj will contain the keys of the array e.g. '0', '1', and any other enumerable properties of the array (for example RegExp.prototype.exec array would have additional enumerable properties index and input)

TypeScript will still see the obj Object as an Array, and intellisense/auto-complete will think that all of the Array methods such as forEach are still available. Is there any way to make this typesafe?

A real-world use case:

export const matchAll = (re: RegExp, str: string): Array<any> => {
  const matches = [];
  let match: RegExpExecArray | null;
  let lastIndex = 0;
  while ((match = re.exec(str))) {
    matches.push({...match, lastIndex});
    lastIndex = re.lastIndex;
  }
  
  // the implicit type of `matches` items will have a lot of 
  // methods that are not accessible
  return matches;
};
3 Answers

Does precising the type may be a solution in your case ?

Like this : Playground Link

const arr = [1, 2];
const obj : Object = {...arr};

Spreading will only clone properties that don't exist on the prototype. Unfortunately, TypeScript does not see it this way.

I suggest annotating obj for what it really is.

const obj: Record<number, number> = { ...arr };

You can also make the conversion explicit by abstracting it to a function.

function toObject<T>(arr: T[]): Record<number, T> {
    return { ...arr };
}
Related