Skip to content

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

Closed
@victorgarciaesgi

Description

@victorgarciaesgi

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions