【问题标题】:Is there a way to hint TS which occurrence of generic parameter to use for generic inference?有没有办法提示 TS 哪个泛型参数的出现用于泛型推理?
【发布时间】:2021-07-10 11:36:28
【问题描述】:

有没有办法提示 TS 使用哪个泛型参数出现来进行泛型推理?

type Handler = <T>( // <-- if T is unspecified in method definition and call how can i tell ts to infer it to Ta from Params<Ta> and not Tb of the input?
  func: (params: Params<Ta>) => any,
  input: Tb
) => string;

代码示例

type Params<T = Record<string, unknown>> = {
  p: T;
  // other metadata
}

type Handler = <T>( // <-- if T is unspecified how can i tell ts to infer it from Params<T>?
  func: (params: Params<T>) => any,
  input: T
) => string;

type Config = {
  handler: Handler
}

const c: Config = {
  handler: (params, input) => 'ok' // <-- no possibility to pass/state the generic parameter here
}


type AB = {a:number, b:number}
const myHandler = (params: Params<AB>) => params.p.a + params.p.b;

c.handler(myHandler, { a:1, b:2, c:"unexpected" }) // <-- T is inferred to { a:1, b:2, c:"unexpected" }
c.handler<AB>(myHandler, { a:1, b:2, c:"unexpected" }) // <-- this is desired behaviour, can it be done without stating the T = AB explicitly?

ts playground link

【问题讨论】:

    标签: typescript generics type-inference


    【解决方案1】:

    您正在寻找microsoft/TypeScript#14829 请求的功能,“非推理类型参数用法”:

    通常对于泛型,会有一些位置类型参数应该可以从用法中推断出来,而其他位置类型参数应该只用于强制类型检查。这出现在各种情况下。

    建议提出一些NoInfer&lt;T&gt; 语法,它等同于T,只是编译器不会从该位置推断T。这是您所要求的另一面,但它们都将实现相同的目标:input 属性类型上的 NoInfer&lt;T&gt; 就像 func() 方法的输入上的“PleaseInfer&lt;T&gt;” .


    虽然NoInfer&lt;T&gt; 没有官方 版本,但 GitHub 问题中提到了一些适用于某些用例的实现。 One I sometimes recommend 是:

    type NoInfer<T> = [T][T extends any ? 0 : never];
    

    当检查的类型是未解析的泛型时,这通过利用编译器延迟来评估distributive conditional types 来工作。它无法“看到”NoInfer&lt;T&gt; 将评估为 T,直到 T 是某个特定的解析类型,例如在推断出 T 之后。


    如果我将您的示例代码更改为:

    type Handler = <T>(
      func: (params: Params<T>) => any,
      input: NoInfer<T>
    ) => string;
    

    然后您的示例按预期工作:

    const c: Config = {
      handler: (func, input) => func({ p: input }) // okay,
    }
       
    type AB = { a: number, b: number }
    const myHandler = (params: Params<AB>) => params.p.a + params.p.b;
    
    c.handler(myHandler, { a: 1, b: 2, c: "unexpected" }); // error!
    // ------------------------------> ~~~~~~~~~~~~~~~
    // Object literal may only specify known properties, 
    // and 'c' does not exist in type 'AB'
    

    Playground link to code

    【讨论】:

      【解决方案2】:

      可以通过改变类型来实现:

      type Params<T = Record<string, unknown>> = {
        p: T;
        // other metadata
      }
      
      type ParamsExtract<T extends (params: Params<any>) => any> = T extends (params: Params<infer U>) => any ? U : never;
      
      type Handler = <Fn extends (params: Params<any>) => any>( // <-- if T is unspecified how can i tell ts to infer it from Params<T>?
        func: Fn,
        input: ParamsExtract<Fn>
      ) => string;
      
      type Config = {
        handler: Handler
      }
      
      const c: Config = {
        handler: (params, input) => 'ok' // <-- no possibility to pass/state the generic parameter here
      }
      
      
      type AB = {a:number, b:number}
      const myHandler = (params: Params<AB>) => params.p.a + params.p.b;
      
      c.handler(myHandler, { a:1, b:2, c:"unexpected" }) // error
      

      所以泛型变成了函数类型,并且从中提取了参数。见here

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-06-16
        相关资源
        最近更新 更多