【问题标题】:Does TypeScript have difficulty using Generics for indexing?TypeScript 使用泛型进行索引是否有困难?
【发布时间】:2021-08-27 20:00:08
【问题描述】:

扩展来自 Effective Typescript 的示例,我不知道我是否遇到了类型检查器或我的理解的限制。

这是第 33 页的原始示例

interface Point {
  x: number
  y: number
}

function sortBy<K extends keyof T, T>(vals: T[], key: K): T[] {
  // ...
}

const pts: Point[] = [
  { x: 1, y: 1 },
  { x: 2, y: 0 },
]

console.log(sortBy(pts, "x"))

这是我尝试用有用的东西替换 //...

interface Point {
  x: number
  y: number
}

function sortBy<K extends keyof T, T>(vals: T[], key: K): T[] {
  // my solution
  return vals.sort((a, b) => {
    return b[key] - a[key] // Error: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.ts(2362)
  })
  // end my solution
}

const pts: Point[] = [
  { x: 1, y: 1 },
  { x: 2, y: 0 },
]

console.log(sortBy(pts, "x"))

但如果我替换为

return (b[key] as any) - (a[key] as any)

有效

我想不通的是,为什么类型检查器认为 a[key] 或 b[key] 可能不是数字,因为 a 和 b 必须是 Point 类型,而 key 必须是 keyof Point。

如果我在没有泛型的情况下编写它,它工作正常,这让我觉得这里发生了一些奇怪的事情。

function sortBy(vals: Point[], key: "x" | "y"): Point[] {
  return vals.sort((a, b) => {
    return b[key] - a[key]
  })
}

【问题讨论】:

    标签: typescript typescript-generics


    【解决方案1】:

    无论如何调用或使用,泛型函数或类型必须在内部有效。换句话说,sortBy 不知道Point 是否存在,或者它最终可能会被该类型调用。它需要处理您可能想要传入的任何对象形状数组,而不仅仅是Point[]

    如果你看这个函数类型:

    function sortBy<K extends keyof T, T>(vals: T[], key: K): T[] {
      return vals.sort((a, b) => {
        return b[key] - a[key]
      })
    }
    

    number 类型没有出现,但您正在执行需要个数字的操作。

    这是它抱怨的情况:

    sortBy([{ a: 'a string' }], 'a')
    

    上述函数签名类型中没有任何内容表明这是无效的。但是当函数运行时它会崩溃,因为string - string 不是一个有效的操作。

    向它传递带有数字属性的对象这一事实并不重要,因为您可以告诉它也查找其他类型。


    解决方案是将K 泛型限制为仅允许产生number 类型的键。

    function sortBy<K extends string, T extends Record<K, number>>(vals: T[], key: K): T[] {
      return vals.sort((a, b) => {
        return b[key] - a[key];
      })
    }
    
    console.log(sortBy([{a: 'asd', b: 123}], "b")) // works
    
    console.log(sortBy([{a: 'asd', b: 123}], "a")) // type error
    // Type 'string' is not assignable to type 'number'.(2322)
    

    现在为了满足调用此函数的要求,K 可以是任何 string,但对象必须有一个属性,该属性在命名属性上具有 number

    Playground

    【讨论】:

    • 嗯,这样好多了!不过,我以为我前几天尝试过,但遇到了有关缺少类型签名的错误。不知道我在做什么......我会更新答案。
    • 1) 这是打字稿中的泛型还是一般泛型的限制? 2)这是否意味着书中的示例完全没有用,因为在当前形式下,如果不进行类型转换就无法查看 T 内部? 3) 如果你调用带有不可键值的函数,比如 sortBy(pts, ["x"]),TS 足够聪明地知道“Type 'string[]' is not assignable to type '"y"'”,这意味着检查是查看整个场景,包括 T 的结构,而不仅仅是盲目地接受泛型函数原样,而是它是如何使用的,在这种情况下它应该更好地了解 T跨度>
    • 1) 我根本不会将其归类为限制。您不能安全地将某些东西视为未以某种方式输入以保证它是数字的数字。这就是打字稿的重点。 2)我没有这本书,但我会说它不是没用的。它表明您可以将key 约束为数组成员类型的键。但是,如果您想实际使用任何值,您必须知道它们是什么才能知道您是否可以使用这些值做您想做的事情。我不确切知道作者的示例意图。
    • 3) 当您调用该函数时,它会检查参数是否与调用签名匹配。如果没有,则会引发类型错误。这就是sortBy(pts, ['x']) 不起作用的原因。因为数组[x] 不是pts 的成员的键。在函数本身中,它会检查以确保您正在执行的操作是由参数类型所允许的。泛型函数的意义在于您不知道确切地在调用它们时它的泛型将设置为什么类型。允许调用它们的类型由您的通用约束设置。
    猜你喜欢
    • 2021-12-30
    • 2014-08-19
    • 1970-01-01
    • 2014-06-14
    • 2017-09-27
    • 1970-01-01
    • 1970-01-01
    • 2022-01-14
    • 2019-08-23
    相关资源
    最近更新 更多