TypeScript 推断 T inner<T> for Outer<T> 的通用类型

TypeScript infer generic type of T in Inner<T> for Outer<T>

我有一个 Inner<T> 接口,它包装了一个通用类型的值:

interface Inner<T> {
    v: T;
}

该接口有不同的实现:

class SomeInner<T> implements Inner<T> {
    constructor(public v: T) {}
    some = true;
}

class SomeOtherInner<T> implements Inner<T> {
    constructor(public v: T) {}
    someOther = true;
}

有一个 Outer class 包装了 Inner<T> 的一个实例。我也想不时访问Inner。因此我保留了 Inner 的实际类型:

class Outer<T, TInner extends Inner<T>> {
    constructor(public inner: TInner) { }
    getV(): T {
        return this.inner.v;
    }
}

不幸的是,TS 无法推断出 Outer 中 T 的类型——尽管它知道 TInner 的类型及其泛型:

const someInnerInst = new SomeInner("foo" as string);
const outerInstOfSomeInner = new Outer(someInnerInst);

const vFromOuter = outerInstOfSomeInner.getV(); // Type: unknown // <- SHOULD BE string
const vFromInner = outerInstOfSomeInner.inner.v; // Type: string

有没有办法帮助 TypeScript 推断 Outer 中的 T?

你可以试试full example in the playground.

Typescript 不会从其他类型参数推断 T。基本上,由于 T 没有出现在构造函数的参数列表中,因此不会对其进行任何推断。您可以通过多种方式解决此问题。

最简单的方法之一是在构造函数中使用交集为打字稿提供 T 的推理站点:

class Outer<TInner extends Inner<T>, T> {
    constructor(public inner: TInner & Inner<T>) {  }
    getV(): T {
        return this.inner.v;
    }
}

Playground Link

您还可以使用某种形式的相对类型从 TInner 中提取 T,例如索引访问类型:

class Outer<TInner extends Inner<any>> {
    constructor(public inner: TInner) { }
    getV(): TInner['v'] {
        return this.inner.v;
    }
}

Playground Link

或条件类型:

type InnerTypeParam<T extends Inner<any>> = T extends Inner<infer U> ? U : never

class Outer<TInner extends Inner<any>> {
    constructor(public inner: TInner) { }
    getV(): InnerTypeParam<TInner> {
        return this.inner.v;
    }
}

Playground Link