【问题标题】:Why does T[K] constraint only work when an instance of T is passed to the function为什么 T[K] 约束仅在将 T 的实例传递给函数时才起作用
【发布时间】:2020-05-13 03:00:43
【问题描述】:
class Test  {
    constructor(public stringprop: string, public numprop: number){}
}

function generic<T, K extends keyof T>(propertyName: K, value: T[K]) {
    return `${propertyName} = ${value}`;
}

function instance<T, K extends keyof T>(instance: T, propertyName: K, value: T[K]) {
    return `${propertyName} = ${value}`;
}

// OK
generic<Test, keyof Test>("numprop", "das")
// Argument of type '"das"' is not assignable to parameter of type 'number'.
instance(new Test("das", 1), "numprop", "das")

基于此我有两个问题:

  1. 为什么第一个调用被认为正常,而第二个调用却不行?
  2. 有没有办法只使用第一个泛型参数 (Test) 并且不传递实例来正​​确键入它?

【问题讨论】:

    标签: typescript


    【解决方案1】:

    回答

    有没有办法只使用第一个泛型参数 (Test) 而没有实例传递来正确键入它?

    TypeScript 目前不支持泛型类型参数的部分推断。请参阅microsoft/TypeScript#26242 进行讨论。现在,您要么必须在调用函数时手动指定所有类型参数,要么让编译器推断所有类型参数。没有混合和匹配。 (如果你想知道,generic type parameter defaults 也不要给你这个。)

    目前有几种方法可以继续;我更喜欢使用柯里化函数,其中第一个函数有一个需要手动指定的泛型类型参数,它返回另一个泛型函数,该泛型类型参数意味着要推断:

    const hybrid = <T>() => function <K extends keyof T>(propertyName: K, value: T[K]) {
      return `${propertyName} = ${value}`;
    }
    

    你会这样称呼它:

    hybrid<Test>()("numprop", "das"); // error
    hybrid<Test>()("numprop", 123); // okay
    

    执行此操作的另一种方法是传递与手动指定的参数相对应的类型的虚拟参数,这与您的instance 类似,只是您实际上不需要真实实例。您可以使用type assertion 假装您正在传递一个:

    instance(null! as Test, "numprop", "das"); // error
    instance(null! as Test, "numprop", 123); // okay
    

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

    Playground link to code

    【讨论】:

      【解决方案2】:

      你的两个函数调用中的泛型参数的值是不等价的。没有实例的等效通用调用将被称为:

      generic<Test, "numprop">("numprop", "das") // error as expected
      

      当你这样做时:

      generic<Test, keyof Test>("numprop", "das")
      

      您是说通用参数Kkeyof T,这意味着它可以是任何keyof T。当您将其作为泛型类型传递时,您将覆盖任何推理打字稿可能会做的事情。现在propertyNamekeyof T,而不是“numprop”。

      所以替换泛型,你的函数变成:

      function generic(propertyName: keyof Test, value: Test[keyof Test]) {
          return `${propertyName} = ${value}`;
      }
      

      现在"numprop" 满足keyof Test"das" 满足Test[keyof Test] 因为string 是它的某些属性的类型之一 (string | number)。

      第二种形式:

      instance(new Test("das", 1), "numprop", 123) // good
      

      Typescript 从参数"numprop" 推断出propertyName 的具体类型。现在Test["numprop"] 解析为string,正如您所料,一切正常。

      Playground

      【讨论】:

      • 我确实认为差异一定意味着泛型参数不等价。我只是不知道等价物是什么。有没有办法通过仅将 Test 指定为通用参数来键入它?
      • 虽然它们并不等价。一个是keyof Test,它的具体程度低于另一个"numprop" extends keyof Test。可悲的是,泛型可以完全推断,或者必须明确提供。您不能部分推断泛型。 More on that here
      猜你喜欢
      • 1970-01-01
      • 2023-03-11
      • 2019-02-02
      • 1970-01-01
      • 2021-01-30
      • 2019-04-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多