Typescript递归泛型与条件类型的斗争

时间:2019-06-18 06:15:05

标签: typescript generics

以下代码编译:

type Ugh = {
  boo: {[s: string]: Ugh },
  baz: ({ [P in keyof Ugh["boo"]]: Ugh["boo"][P] extends Ugh ? Ugh["boo"][P] : string })
};

const q: Ugh = { boo: {}, baz: {}};
const v: Ugh = {boo: { why: { boo: {}, baz: {}}}, baz: { why: { boo: {}, baz: {}} }};

以下内容不

type Ugh = {
  boo: {[s: string]: string | Ugh },
  baz: ({ [P in keyof Ugh["boo"]]: Ugh["boo"][P] extends Ugh ? Ugh["boo"][P] : string })
};

const q: Ugh = { boo: {}, baz: {}};
const v: Ugh = {boo: { why: { boo: {}, baz: {}}}, baz: { why: { boo: {}, baz: {}} }};

唯一的区别是boo的类型。为什么第二个不编译?

1 个答案:

答案 0 :(得分:1)

  

为什么第二个不编译?

类型string | Ugh不会扩展类型Ugh,因此在第二个示例中,Ugh["boo"][P] extends Ugh将始终为false,结果是baz始终为string类型。

在代码注释(and in the playground)中:

type Ugh = {
    // The string index type of Ugh...
    boo: {
        [s: string]: string
    },
    baz: (
        {
            // means that P will always be an Ugh...
            // which does extend Ugh...
            [P in keyof Ugh["boo"]]: Ugh["boo"][P] extends Ugh
            // and so this will always resolve to an Ugh.
            ? Ugh["boo"][P]
            : string
        }
    )
};

type t1 = Ugh["boo"][string] extends Ugh ? true : false; // true

type UghToo = {
    // The string index type of string | UghToo...
    boo: { [s: string]: string | UghToo },
    baz: ({
        // means that P will always be a string | UghToo...
        // which does not extend UghToo...
        [P in keyof UghToo["boo"]]: UghToo["boo"][P] extends UghToo
        ? UghToo["boo"][P]
        // and so this will always resolve to a string.
        : string
    })
};

type t2 = UghToo["boo"][string] extends UghToo ? true : false; // false

通常,两种类型的联合(几乎没有例外)不会扩展这两种类型中的任何一种。

type t3 = Date extends Date ? true : false; // true
type t4 = string | Date extends Date ? true : false; // false
type t5 = string | Date extends string ? true : false; // false