为什么在元组上使用映射类型时需要条件类型?

Why is conditional type needed when using mapped type on tuple?

这个有效:

class Dog {
    speak() {
        return "woof" as const;
    }
}

type ExtractSpeak<T extends [...Dog[]]> = { [P in keyof T]: T[P] extends Dog ? ReturnType<T[P]["speak"]> : never };

// Type of Example is ["woof", "woof"]
type Example = ExtractSpeak<[Dog, Dog]>;

但是,为什么没有条件类型它就不能工作?

// Error: Type '"speak"' cannot be used to index type 'T[P]'. (2536)
type ExtractSpeak<T extends [...Dog[]]> = { [P in keyof T]: ReturnType<T[P]["speak"]> };

什么时候 T[P] 指代具有 speak 方法的类型以外的任何东西 returns "woof"

这是 TypeScript 中的一个错误;看到 microsoft/TypeScript#27995. When you make a mapped type over a tuple/array 出来的类型也会是一个 tuple/array;也就是说,您实际上只是在映射数组的数字索引。但是在映射类型的定义中,编译器并不关注这一点。相反,它认为 K in keyof T(其中 T 是数组类型)可能在 每个可能的键 上迭代 K,如下所示:

type DogArrayKeys = keyof [Dog, Dog];
// number | "0" | "1" | "length" | "toString" | "toLocaleString" | "pop" | 
// "push" | "concat" | "join" | "reverse" | "shift" | "slice" | "sort" | 
// "splice" | "unshift" | "indexOf" | "lastIndexOf" | 
// ... 14 more ... | "includes"

并且因为,例如,[Dog, Dog]["length"] 是类型 2 而不是 Dog,编译器不会让你把 T[K] 当作 Dog.布莱奇

无论如何,这是一个错误,如果修复了就好了。我想你可以去 ms/TS#27995 给它一个 .但实际上它在任何优先级列表中可能都不高(目前处于积压状态)。标准的解决方法是使用像 Extract<T[K], Dog> 这样的条件类型然后继续,这基本上就是上面的内容。

Playground link to code