【问题标题】:Why can't TypeScript infer a generic type when it is in a nested object?为什么 TypeScript 在嵌套对象中不能推断出泛型类型?
【发布时间】:2018-09-13 22:57:51
【问题描述】:

我有一个 TypeScript 无法推断出其泛型的类型。

interface Foo<A> {
    [name: string] : {
        foo : A
    }
}

function makeFoo<A>(foo: Foo<A>) : Foo<A>{
    return foo
}

// Works fine when manually specifying the types
const manuallyTyped : Foo<string | number> = {
    a: {
        foo: '1'
    },
    b: {
        foo: 3
    }
}

// ERROR, Can't infer type as Foo<string | number>
makeFoo({
    a: {
        foo: '1'
    },
    b: {
        foo: 3
    }
})

最初,我使用下面的类型,但我想自己制作对象的值。当索引签名平坦时,推理工作得很好。

interface FlatFoo<B> {
    [name: string] : B
}

function makeFlatFoo<B>(bar: FlatFoo<B>): FlatFoo<B>{
    return bar
}

// Correctly has type FlatFoo<string | number>
const inferred = makeBar({
    a: 'a',
    b: 2
})

有没有人解释和/或建议让它发挥作用?

【问题讨论】:

    标签: typescript


    【解决方案1】:

    这与this questionthis question 中的问题类似。当 TypeScript 对同一个类型参数进行多个协变推断时(在第一个示例中,numberstring 用于 A),它会尝试选择其中一个是其他类型参数的超类型;它不推断联合,除非在推断是相同原始类型的文字类型的特殊情况下。如果 TypeScript 在其他情况下似乎推断出联合类型,那是因为其他一些语言特性在起作用。在makeFlatFoo 的情况下,该功能是对象文字类型的隐式索引签名生成,它采用属性ab 的类型的联合,即string | numberstring | numberB 匹配,您会得到string | numberB单个 推断,并且一切正常。但是,在makeFoo 中,隐式索引签名的返回类型是Foo&lt;string&gt; | Foo&lt;number&gt;。当这与 Foo&lt;A&gt; 匹配时,联合被打破,你会得到 两个不同的推论 stringnumber 对于A

    虽然基于您的答案的以下示例编译没有错误:

    function makeFoo<A, F extends Foo<A>>(foo: F) : F{
        return foo
    }
    
    const result = makeFoo({
        a: {
            foo: '1'
        },
        b: {
            foo: 3
        }
    });
    

    您会看到A{}result 的类型是{ a: { foo: string; }; b: { foo: number; }; },因此您还没有成功地将对象转换为Foo&lt;T&gt; 类型。相反,您可以使用类型参数FA 来捕获隐式索引签名的返回类型,然后使用分布式条件类型来提取foo 属性的实际类型,如this answer

    interface FlatFoo<FA> { 
        [name: string]: FA;
    }
    type FooPropTypes<FA> = FA extends { foo: infer A } ? A : never;
    function makeFoo<FA extends {foo: unknown}>(foo: FlatFoo<FA>) : Foo<FooPropTypes<FA>> {
        return <any>foo
    }
    

    【讨论】:

    • 你有什么可以推荐的资源吗?我发现自己想知道如何影响类型推断,通常是针对联合类型,经常如此。我喜欢描述不同情况下类型推断的优先级以及如何影响它的东西。
    • 你的网络搜索和我的一样好。我怀疑是否存在任何此类文件;遗憾的是,这并不是 TypeScript 团队的优先事项。你可以file an issue 但我怀疑会发生什么。我确实知道一些关于类型推断和我不知道的事情,我很高兴阅读源代码来学习,所以请随时继续在 Stack Overflow 上提出具体问题。
    【解决方案2】:

    看来您可以通过更新 makeFoo 以将整个输入捕获为泛型来解决此问题。

    function makeFoo<A, F extends Foo<A>>(foo: F) : F{
        return foo
    }
    

    这并不完全符合我的预期,但结构类型很有效。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-01-15
      • 2020-05-19
      • 1970-01-01
      • 1970-01-01
      • 2020-02-15
      • 2017-08-04
      • 1970-01-01
      相关资源
      最近更新 更多