【问题标题】:TypeScript type inference from array passed as function parameter从作为函数参数传递的数组的 TypeScript 类型推断
【发布时间】:2020-07-14 01:50:52
【问题描述】:

在玩 TS 3.7.2 时,我发现了一个奇怪的边缘情况,现在我很想知道更多关于函数中的类型推断在这种特定情况下是如何工作的。

interface A {
    functions: { t: number, a: { test: number }; };
}

interface B {
    functions: { t: number, b: { ciao: number }; };
}

function combineStuff<TNamespace extends {}>(stuff: { functions: Partial<TNamespace> }[]): TNamespace {
    // Implementation irrelevant for the question
    return {functions: merge({}, ...stuff.map(s => s.functions))} as any;
}
const a: A = { functions: { t: 1, a: { test: 1 } } };
const b: B = {functions: { t: 1, b: { ciao: 2 } }};
const c = combineStuff([a, b]);
c.a.test;
c.b.ciao;  // Error. Why is that?

const d = combineStuff<A['functions'] & B['functions']>([a, b]);
d.a.test;
d.b.ciao; // Works well

我的预期是 c.b.ciaod.b.ciao 都是类型安全的,但访问 c.b.ciao 会导致错误。

从这个简单的例子看来,泛型类型可以从数组的元素中自动推断出来,这一假设也得到了以下事实的支持:在调用 combineStuff&lt;A['functions'] &amp; B['functions']&gt;([a, b]); 中,TS 编译器验证了 ab 实际上是正确的。

此外,类型推断对于数组a 的第一个元素可以正常工作,我可以安全地访问c.a.test

为什么我需要显式地将泛型类型提供给combineStuff() 以获得正确的结果?为什么对数组第一个元素的推断工作正常?

【问题讨论】:

    标签: typescript typescript-typings typescript-generics


    【解决方案1】:

    问题在于,由于您在数组定义中使用Partial,TS 只会选择其中一种类型(它遇到的第一个类型),然后检查另一种类型是否可分配,因为我们正在谈论Partial 会的。

    解决方案是对整个对象使用类型参数。这将捕获AB 的联合。然后我们可以使用索引类型查询 (T['functions']) 来获得 function 类型的联合。并使用UnionToIntersection shoutout jcalz 将其转换为所需的交集。

    interface A {
        functions: { t: number, a: { test: number }; };
    }
    
    interface B {
        functions: { t: number, b: { ciao: number }; };
    }
    type UnionToIntersection<T> =
      (T extends T ? (p: T) => void : never) extends (p: infer U) => void ? U : never;
    function combineStuff<TNamespace extends Array<{ functions: object }>>(stuff: TNamespace):  UnionToIntersection<TNamespace[number]['functions']>{
        // Implementation irrelevant for the question
        return null! as any;
    }
    const a: A = { functions: { t: 1, a: { test: 1 } } };
    const b: B = { functions: { t: 1, b: { ciao: 2 } } };
    
    const c = combineStuff([b, a]);
    
    c.a.test;
    c.b.ciao; 
    
    

    Playground Link

    【讨论】:

      猜你喜欢
      • 2016-11-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-28
      • 2020-03-11
      • 2010-09-29
      • 2022-01-26
      • 2021-12-22
      相关资源
      最近更新 更多