【问题标题】:TypeScript infer generic type of T in Inner<T> for Outer<T>TypeScript 在 Inner<T> 中为 Outer<T> 推断 T 的泛型类型
【发布时间】:2022-01-07 09:39:30
【问题描述】:

我有一个 Inner&lt;T&gt; 接口,它包装了一个泛型类型的值:

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 类包装了Inner&lt;T&gt; 的一个实例。我也想不时访问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 oop generics


    【解决方案1】:

    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

    【讨论】:

    • 呃,我有点太快了。 Outer&lt;SomeInner&lt;string&gt;, string&gt; 类型确实是多余的。有没有办法摆脱多余的字符串?
    • @htho 您可以使用其他版本。我更新了答案
    • 再次感谢。通过将参数类型更改为&lt;TInner extends Inner&lt;any&gt;, T = InnerTypeParam&lt;TInner&gt;&gt;,我可以再次使用getV(): T {...}。此声明对可选类型参数也有效:const checkOuter: Outer&lt;SomeInner&lt;string&gt;&gt; = outerInstOfSomeInner;(游乐场链接不适合 cmets。)随时将其添加到您的答案中。
    猜你喜欢
    • 2020-08-22
    • 1970-01-01
    • 2020-01-29
    • 1970-01-01
    • 1970-01-01
    • 2021-06-23
    • 2014-12-27
    • 2011-08-13
    • 1970-01-01
    相关资源
    最近更新 更多