Skip to content

Type inference lost after spreading array with ArrayLikeΒ #58286

@KarelVerschraegen

Description

@KarelVerschraegen

πŸ”Ž Search Terms

array inference, type inference, ArrayLike, lodash compact, _.compact

πŸ•— Version & Regression Information

  • This changed between versions 4.9.5 and 5.0.4

⏯ Playground Link

https://www.typescriptlang.org/play?ts=5.0.4#code/C4TwDgpgBAsiAq4IB55QgD2BAdgEwGcoBXHAaxwHsB3HAPigF4oBvAKCigFsQBpCEAC4o8ANwduIAGKkAxsACWlHMIAUAQwBOAcwCMw+AEomDAG6UFecQF9xbWcoLAo23BE3rsTKKsPCAgpoeIMhwiJDITpoKONp0DIwM7JyaEMDEmjhQgcEAdABmmpRcqixQADa42sAAFsIArFDWhrlc6mChCEiRwNGx8aqqAPoANFAxeJjGiawSKWkZWcmcKzz8QlAABgAkLBOY1psjc6vScorKalp600knK1Cp6ZniD5zWxw+2Es02dg44JxQLTBAAyCjIEAAkjh8u5cLIIDIcPIlFlmKh0FhcIQSOQqLQ6KoJCCAkF1CBwZDMQAfKA4YjlcpQOmkSb5GIQPAsqD5dTlAjQOkAImFPIADAw6QymTy2RAOTguWw-CIANoAXRMs3mzyymr+bHsjmcIIpVOhsPcqRR0GYZspEMtcJtiORqOUqjVEjKawEwmFugATABmYVjHjui4qHymfnECC3VjWJqfKC9BNQABkWdYknWAYALPUAGzhyRRtFqOPlBNJlgpj4SPkC6A5vN+jbC4tliNnFHR6vxxPahupiS5SeuJUebC+NMAegX6aQ4ytQQgiKglHyFfOaPGRHKlCcEgzbdzvr4-qg3dL5cj+8useH9cbaZbguzl-zN7vvb3AcqxfWsRxmMcm04K8C1vHsH37D0Y1UGs61Hd82A1Qx-hNYFyRAGEXQRJEn3RKBMUwbB8CIUgKBoehiU4Ulsjw2l6UZZlWXwBVOW5OlPyFW8xTpSUeRlDi8XZHiVQMTVRwkJ5FigA02G+Y1AVNPCCPhW1vAdLTXWIoDPW9KDfy7YMwz7Stn2Q180PHThz2-Dtry7OCrJIodQLfBzeX5L922gv93MAxCvNQ8D0M4SdcmndxPAgecJCXFdIDXF1Ui3HdQujQ8oEhMBgDPTRM0Csyi3vDyjKQlCwKSKK-NbZygrcyqcuA2zvPsyCXJg-94Osmq7Mi8dMPEIA

πŸ’» Code

type MyType<T extends unknown> = {
  myKey: T;
  myFunction: (arg1: T) => void;
};

const generate = (): Array<MyType<string>> => {
  return Array.from({ length: 5 }).map<MyType<string>>((_, index) => {
    return {
      myKey: `${index}`,
      myFunction: (arg1) => {
        return;
      },
    };
  });
};

const arrayLikeInferenceFunction = <T extends unknown>(
  arr: ArrayLike<T | null | undefined | false | "" | 0> | null | undefined
): T[] => {
  return [];
};

const arrayLikeInference = arrayLikeInferenceFunction([
  { myKey: "123", myFunction: (value) => {} }, // value is correctly inferred as string
  true && { myKey: "456", myFunction: (value) => {} },
  false && { myKey: "456", myFunction: (value) => {} },
  ...generate(),
  // type inference of myFunction is lost
  true && { myKey: "456", myFunction: (value) => {} }, // value is inferred as any
  false && { myKey: "456", myFunction: (value) => {} },
  { myKey: "456", myFunction: (value) => {} },
]);

const arrayInferenceFunction = <T extends unknown>(
  arr: Array<T | null | undefined | false | "" | 0> | null | undefined
): T[] => {
  return [];
};

const arrayInference = arrayInferenceFunction([
  { myKey: "123", myFunction: (value) => {} }, // value is correctly inferred as string
  true && { myKey: "456", myFunction: (value) => {} },
  false && { myKey: "456", myFunction: (value) => {} },
  ...generate(),
  // type inference of myFunction is kept
  true && { myKey: "456", myFunction: (value) => {} }, // value is correctly inferred as string
  false && { myKey: "456", myFunction: (value) => {} },
  { myKey: "456", myFunction: (value) => {} },
]);

πŸ™ Actual behavior

The typings of the arguments of myFunction are lost after the array spread when using ArrayLike. When using Array, the type inference is kept.

πŸ™‚ Expected behavior

The arguments of myFunction should be inferred as string

Additional information about the issue

I have opened a similar issue in the DefinitelyTyped repository for the lodash typings and they suggested opening an issue in this repository. DefinitelyTyped/DefinitelyTyped#69410

The issue originated from using lodash.compact() with code similar to the code below:

import _ from "lodash";

type MyType<T extends unknown> = {
  myKey: T;
  myFunction: (arg1: T) => void;
};

const generate = (): Array<MyType<string>> => {
  return Array.from({ length: 5 }).map<MyType<string>>((_, index) => {
    return {
      myKey: `${index}`,
      myFunction: (arg1) => {
        return;
      },
    };
  });
};

const lodashInferred = _.compact([
  { myKey: "123", myFunction: (value) => {} }, // infers value as string
  true && { myKey: "456", myFunction: (value) => {} },
  false && { myKey: "456", myFunction: (value) => {} },
  ...generate(),
  { myKey: "123", myFunction: (value) => {} },  // infers value as any
  true && { myKey: "456", myFunction: (value) => {} },
  false && { myKey: "456", myFunction: (value) => {} },
]);

Metadata

Metadata

Assignees

No one assigned

    Labels

    Help WantedYou can do thisPossible ImprovementThe current behavior isn't wrong, but it's possible to see that it might be better in some cases

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions