Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TS 4.1] Array.isArray incorrectly infers recursive type #41609

Closed
victorgarciaesgi opened this issue Nov 20, 2020 · 1 comment
Closed

[TS 4.1] Array.isArray incorrectly infers recursive type #41609

victorgarciaesgi opened this issue Nov 20, 2020 · 1 comment

Comments

@victorgarciaesgi
Copy link

TypeScript Version: 4.1.2

Code

I have this recursive type that I use for form generation.

export type FormStructure<TForm extends ObjectLiteral> = {
  [TKey in keyof TForm]?: TForm[TKey] extends DefaultFieldStruture
    ? TForm[TKey]
    : TForm[TKey] extends Array<any>
    ? FormStructure<UnArray<TForm[TKey]>>[]
    : FormStructure<TForm[TKey]>;
};

And inside a recursive function, I traverse this big nested object

declare const fields: FormStructure<TForm>

return (Object.entries(fields) as [
    string,
    DefaultFieldStruture | FormStructure<TForm>
  ][]).reduce<TReturn>((acc, [key, field]) => {
    if (field instanceof DefaultFieldStruture) {
      //
    } else if (Array.isArray(field) && Array.isArray(datas[key])) {
      acc[key] = field
        .map((element, index) => {
//       ^^^^
      // This expression is not callable.
     //  Each member of the union type '(<U>(callbackfn: (value: any, index: number, array: any[]) => U, thisArg?: any) => U[]) | 
      //(<U>(callbackfn: (value: any, index: number, array: readonly any[]) => U, thisArg?: any) => U[])' has signatures, but none 
     // of those signatures are compatible with each other.
          return mapAndCompareThroughData(element, datas[key]?.[index], predicate);
        })
        .filter((result) => !!result);
    } 
    }
    return acc;
  }, {} as TReturn);

Expected behavior:

Before passing into Array.isArray, my field var is correctly infered as

var field: FormStructure<TForm>

After passing thought Array.isArray, it is now

var field: FormStructure<TForm> & (FormStructure<TForm> extends readonly any[] ? unknown extends readonly any[] & FormStructure<TForm> ? never : readonly any[] : any[])

I've tried puting a -readonly to my type but without success

export type FormStructure<TForm extends ObjectLiteral> = {
  -readonly [TKey in keyof TForm]?: TForm[TKey] extends DefaultFieldStruture
    ? TForm[TKey]
    : TForm[TKey] extends Array<any>
    ? FormStructure<UnArray<TForm[TKey]>>[]
    : FormStructure<TForm[TKey]>;
};

Is there a way to make a type guard for this? Why Array.isArray became so strange to analyse?

Playground Link: https://www.typescriptlang.org/play?ts=4.1.0-beta#

The playground exemple doesn't works because TS 4.1-beta don't includes isArray new types

@victorgarciaesgi
Copy link
Author

Nevermind, found the issue, I replaced

(Object.entries(fields) as [
    string,
    DefaultFieldStruture | FormStructure<TForm>
  ][])

by

(Object.entries(fields) as [
    string,
   DefaultFieldStruture  | FormStructure<T> | (FormStructure<T> | DefaultFieldStruture)[]
  ][])

And added extra check in the map interator

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant