如何推断通用属性类型?

Posted

技术标签:

【中文标题】如何推断通用属性类型?【英文标题】:How to Infer Generic Property Types? 【发布时间】:2020-04-27 11:34:07 【问题描述】:

我不知道如何根据其所在对象的泛型类型推断泛型属性的类型。在下面的例子中,我怎么能说Something.aProp需要匹配Something的U.obj.prop的类型呢?

interface Prop 
  a: number;

interface FancyProp extends Prop 
  b: number;


interface Obj<T extends Prop> 
  prop: T;


interface FancyObj extends Obj<FancyProp> 

interface Parent<T extends Obj<any>>  // <-- the <any> here seems wrong too
  obj: T;


interface FancyParent extends Parent<FancyObj> 
  fancy: number;


class Something<U extends Parent<any>> 
  aProp: typeof U.obj.prop;

Something&lt;Parent&gt;.aProp 应该是Prop 类型,而Something&lt;FancyParent&gt;.aProp 应该是FancyProp 类型?

【问题讨论】:

你想要lookup types, aka indexed access types,比如U['obj']['prop'] 你能解释一下the &lt;any&gt; here seems wrong too吗?您的具体问题是什么? 好吧,如果你愿意,你可以写Obj&lt;Prop&gt;,这会更安全一点,但是根据你实际的非示例接口,你可能需要求助于any,除非你想要Parent 具有多个通用参数,如 Parent&lt;P extends Prop, T extends Obj&lt;P&gt;&gt; @jcalz 感谢 cmets!通过添加普通(非花哨)接口的类型约束版本,我似乎能够使用查找类型,并且类中只有一个类型参数:class Something&lt;T extends Parent&lt;any&gt;&gt; aProp: T['obj']['prop']; interface PlainObj extends Obj&lt;Prop&gt; interface PlainParent extends Parent&lt;PlainObj&gt; new Something&lt;PlainParent&gt;().aProp.a; new Something&lt;FancyParent&gt;().aProp.b; 【参考方案1】:

对于您的主要问题,在给定对象类型T 和键类型K 的情况下查找属性值类型的方法是使用lookup types, a.k.a., indexed access types,通过括号语法T[K]。因此,如果您想查找U 类型对象的"obj"-keyed 属性的"prop"-keyed 属性的类型,您可以将该类型写为U["obj"]["prop"]

请注意,点语法不适用于类型,即使键类型是字符串文字。如果U.obj.prop 是类型系统中U["obj"]["prop"] 的同义词,那就太好了,但是unfortunately that syntax would collide with namespaces,因为可能有一个名为U 的命名空间,一个名为obj 的子命名空间,以及一个名为@ 的导出类型987654338@,然后U.obj.prop 将引用该类型。


对于您关于any 的cmets,当Y&lt;T&gt; 的类型参数T 有一个generic constraint 时,使用X extends Y&lt;any&gt; 并不是真的错误,但它可能有点没有你能得到的类型安全。如果Y&lt;T&gt; 类型以covariant 的方式与T 相关,那么您可以使用通用约束而不是any

这意味着,例如,Parent&lt;T extends Obj&lt;any&gt;&gt; 可以替换为 Parent&lt;T extends Obj&lt;Prop&gt;&gt;U extends Parent&lt;any&gt; 可以替换为 U extends Parent&lt;Obj&lt;Prop&gt;&gt;


这些更改为您提供如下代码:

interface Parent<T extends Obj<Prop>> 
    obj: T;


class Something<U extends Parent<Obj<Prop>>> 
    aProp: U['obj']['prop'];
    constructor(u: U) 
        this.aProp = u.obj.prop;
    

我还向Something 添加了一个构造函数,因为class properties should be initialized 并且我想表明当uU 时,可以为aProp 分配来自u.obj.pop 的值。

这应该可以按您的预期工作:

interface PlainObj extends Obj<Prop>  
interface PlainParent extends Parent<PlainObj>  
new Something<PlainParent>( obj:  prop:  a: 1   ).aProp.a; // number

interface FancyObj extends Obj<FancyProp>  
interface FancyParent extends Parent<FancyObj> 
    fancy: number;

new Something<FancyParent>( obj:  prop:  a: 1, b: 2  , fancy: 3 ).aProp.b; // number

好的,希望对您有所帮助;祝你好运!

Playground link to code

【讨论】:

以上是关于如何推断通用属性类型?的主要内容,如果未能解决你的问题,请参考以下文章

使用联合类型进行类型推断 - 不存在最佳通用类型

打字稿不推断通用对象的值

通用扩展方法:无法从用法推断类型参数

TypeScript 类型推断 - 函数的通用对象

推断函子返回类型的通用方法?

打字稿:在对象内推断通用对象的类型