TypeScript:如何根据另一个泛型推断一个泛型的类型?

TypeScript: How to infer the type of one generic based on another generic?

我有这个代码:

class A<T> {
    a(t: T) {
        console.log(t);
    }
}

class B<T, O extends {b: T}> {
    constructor(public a: A<O>) {
    }

    b(t: T) {
        console.log(t)
    }
}

const a = new A<{b: number}>(); // type: A<{ b: number; }>

const b = new B(a); // type: B<unknown, { b: number; }>

为什么 TypeScript 将 class B 的方法 b 的参数标记为未知?

我想我明白问题出在哪里了。 class B 有两个泛型类型参数 TOB 的构造函数只接受 A<O> 类型的参数,不接受 T 类型的参数。因此,当您创建一个新的 B 时,无法判断 T 应该绑定到什么。尝试 new B<Type1, Type2>(a) 来帮助编译器。

你认为它应该派生那个T = number吗?如果条件是 O equals { b: T }(你不能写),那么那就是真的。但是由于 number extends {} 我们也可以有 T = {}.

在此代码中,g 的类型为 true:

type G<T> = { b: number } extends { b: T } ? true : false;
let g: G<{}>;

我们有类型 TO,其中 O extends {b: T}TO['b']的关系是O['b'] extends T。这意味着 T 可以是 O['b'] 但它也可以是 比 [=17] 更广泛 的任何类型=].

这个推理方向是打字稿无法推断的,因为 T 有无限多种类型 number extends T。正如 @md2perpe 所建议的,您可以使用非常广泛的类型,例如 any{}。您可以有一个包含 number 的联合,例如 string | number,等等

为了能够推断出第二个参数,我们需要 extends 走另一条路。我们需要知道 T 必须 O['b'] 更窄 。我们可以这样写:

class B<T extends O['b'], O extends {b: any}> {

这里我们说 O 是一些带有 b 属性 的对象。我们说 T 是某种类型,它是 Ob 值或其子集。现在打字稿会将 T 推断为 O['b'].

const a = new A<{b: number}>(); // type: A< b: number; }>

const b = new B(a); // type: B<number, { b: number; }>

b.b(5) // takes type: number

请注意,您仍然可以手动将 T 设置为更窄的类型:

const specificB = new B<5, {b: number}>(a); // type: B<5, { b: number; }>

specificB.b(10); // error: Argument of type '10' is not assignable to parameter of type '5'.

Typescript Playground Link