【问题标题】:Generic type of extended interface not inferred未推断出扩展接口的通用类型
【发布时间】:2019-08-07 23:14:47
【问题描述】:

在下面的例子中Typescript可以从bar中传递给它的参数推断foo方法中T的类型,但是它并不能推断R的类型,感觉像应该 - 假设它知道 T 的类型以及 T extends I<R>

interface I<T> {
}

class A implements I<string> {

}

function foo<T extends I<R>, R>(bar: T): R {
    return;
}

foo(new A());

还有其他方法吗?

【问题讨论】:

    标签: typescript generics types type-inference


    【解决方案1】:

    第一个问题是你的接口是空的,打字稿使用结构类型,所以如果你的通用接口不使用它的类型参数,那么它是否有它并不重要。例如这有效:

    interface I<T> { }
    declare let foo: I<string> 
    declare let bar: I<number>
    // Same structure ({}), assignment works
    foo = bar
    bar = foo 
    

    如果我们添加一个字段,Typescript 仍然不会推断出R 类型参数,它只是不会尝试从T 中提取它。您最好的选择是使用条件类型并在需要的地方提取泛型类型参数:

    interface I<T> {
        value :T 
    }
    
    class A implements I<string> {
        value! :string 
    }
    
    type ExtractFromI<T extends I<unknown>> = T extends I<infer U> ? U : never;
    function foo<T extends I<unknown>>(bar: T):  ExtractFromI<T>{
        return bar.value as ExtractFromI<T>; // Generic type with unresolved type parameters, we need a type assertion to convince the compiler this is ok 
    }
    var r = foo(new A()); // string
    

    【讨论】:

    • 这太酷了,谢谢,后续问题,是否有可能使这个ExtractFrom 映射类型通用?这样它就可以从任何界面中提取而不是专门从我那里提取?
    • @IBOED2 很​​遗憾,不,您需要为每个要从中提取参数的泛型类型量身定制的专用条件类型。
    【解决方案2】:

    我想补充一下@Titan 接受的答案。您还可以在界面上添加一个隐藏符号来欺骗结构类型。

    我在我的项目中使用了以下内容,编译器能够推断出我的 Action 的 Result 类型。你可以在Typescript Playground试试。

    const F = Symbol();
    
    interface Action<R = unknown> {
        [F]?: R; // this trick the compiler
    }
    
    type ActionResult<T extends Action> = T extends Action<infer R> ? R : never;
    
    interface SetName extends Action<SetNameResult> {
        type: 'set_name';
        name: string;
    }
    
    interface SetNameResult {
        type: 'set_name_result';
        newName: string;
    }
    
    function process<A extends Action>(action: A): ActionResult<A> {
        return {} as any; // TODO process action and return a valid result
    }
    
    const r = process({ type: 'set_name', name: 'the name' } as SetName);
    r.type; // 'set_name_result'
    r.newName; // string
    

    【讨论】:

      【解决方案3】:

      您可能还只想从实现中提取它。

      例如。

      export class CatWatcher extends Watcher<Cat>
      {
          watch(): Cat { ... }  // overridden from base
      }
      

      我想从CatWatcher 获得Cat。正如其他答案所说,似乎不可能通过推理从基类中提取它。

      但你知道它会有一个watch 函数 - 所以从那里提取它。

      export type ExtractWatcherType<T> = T extends { watch: infer W } ? W : never;
      

      【讨论】:

        猜你喜欢
        • 2021-11-19
        • 2014-05-20
        • 2021-02-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多