简体   繁体   中英

Why does typescript infer unknown with a recursive type?

Why is U being inferred as unknown in foo ?

Could it not look at UnwrappedArray and known that U must be a string or yet another UnwrappedArray ?

Is there any hints that I can give to the compiler to help it out?

type Unwrap<T> = T extends string ? T : T extends Array<infer U> ? UnwrappedArray<U> : never;
interface UnwrappedArray<T> extends Array<Unwrap<T>> {}

let a: string = "hello";
let b: Unwrap<string> = a;
a = b;

let c: Array<string> = [a];
let d: Unwrap<Array<string>> = c;
c = d;

function foo<A>(unwrapped: Unwrap<A>): A {
    // This fails.
    return unwrapped;
}
Type 'Unwrap<A>' is not assignable to type 'A'.
  'A' could be instantiated with an arbitrary type which could be unrelated to 'Unwrap<A>'.
    Type 'A | (A extends (infer U)[] ? UnwrappedArray<U> : never)' is not assignable to type 'A'.
      'A' could be instantiated with an arbitrary type which could be unrelated to 'A | (A extends (infer U)[] ? UnwrappedArray<U> : never)'.
        Type 'A extends (infer U)[] ? UnwrappedArray<U> : never' is not assignable to type 'A'.
          'A' could be instantiated with an arbitrary type which could be unrelated to 'A extends (infer U)[] ? UnwrappedArray<U> : never'.
            Type 'UnwrappedArray<unknown>' is not assignable to type 'A'.
              'A' could be instantiated with an arbitrary type which could be unrelated to 'UnwrappedArray<unknown>'.(2322)

Playground

I give a counter example, let's just think when A is ['1'] type, so when we use type Unwrap<A> to transform it, due to interface UnwrappedArray<T> extends Array<Unwrap<T>> {} , finally, UnwrappedArray<U> would be equal to Array<'1'> which is not initial type ['1'] . This is demonstrated by:

let e: Array<"hello"> = ["hello"];
let f: Unwrap<Array<string>> = e;
// Counter example.
e = f;

To solve this problem, you can try the following change:

type Unwrap<T> = T extends string
  ? T
  : T extends Array<infer U>
    // U extends Unwrap<U>, the following line can be simplified like that, thx for the suggestion 
    ? U extends Unwrap<U>
      ? T
      : never
    : never;

let a: string = "hello";
let b: Unwrap<string> = a;
a = b;

let c: Array<string> = [a];
let d: Unwrap<Array<string>> = c;
c = d;

function foo<A>(unwrapped: Unwrap<A>): A {
    return unwrapped;
}

Playground

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM