【问题标题】:Typescript Typeguard check if array is of typeTypescript Typeguard 检查数组是否属于类型
【发布时间】:2022-01-08 10:42:08
【问题描述】:

我想写一个 typeguard 来检查数组的所有子元素是否都是 T 类型,从而使它成为一个 T 是泛型类型的数组

TS Playground

// Assume arr of any type but Array
const arr: any[] = [
  {
    foo: "bleh1",
    bar: 1
  },
  {
    foo: "bleh2",
    bar: 2
  },
]

interface newType {
  foo: string
  bar: number
}

// Check that arr is an array of newType , ie arr: newType[]
const isArrayOf = <T,>(arr: any): arr is Array<T> => {
  // TypeScript mastery needed here
  return true
}

if(isArrayOf<newType>(arr)){
  arr
}

【问题讨论】:

  • 从 GitHub Copilot 获得建议,但无法理解第二个参数 typescript const isArrayOf = &lt;T&gt;(arr: any[], type: new (...args: any[]) =&gt; T): arr is T[] =&gt; { return arr.every((item) =&gt; item instanceof type) }
  • 类型系统在运行时不存在。您无法检查某些内容是否符合泛型类型,因为函数运行时没有“geneics”或“types”。
  • 建议使用具体类型并传入一个类。但是,当类型是接口时,这是不可能的。
  • 我认为它可能像描述的 [here] (2ality.com/2020/06/…) 但我无法理解它的工作原理,因此想对函数参数进行一些解释
  • 同样,如果您有 具体类型,这将有效。运行时存在的东西。一类。如果您有一个接口,则不会 - 仅在编译时存在。

标签: arrays typescript typeguards generic-type-parameters


【解决方案1】:

你能做的最好的事情是:

const arr: NewType[] = [
  {
    foo: "bleh1",
    bar: 1
  },
  {
    foo: "bleh2",
    bar: 2
  },
]

interface NewType {
  foo: string
  bar: number
}

type TypeOfArrayElements<T> = T extends Array<infer U> ? U : never;

type ArrayType = TypeOfArrayElements<typeof arr>;

TypeScript 永远无法猜测类型为 any[] 的数组实际上包含 NewType 元素。与 TypeScript 中的所有内容一样,type predicates 是静态的,不会根据在运行时作为参数传递的内容动态返回类型。但是如果你输入为NewType[],那么你可以从中提取NewType类型。

【讨论】:

    【解决方案2】:

    编辑 1:

    这个答案有两个缺点(在cmets中提到)

    • 该属性不需要是自己的,它存在于对象x = {a: 1}; y = Object.create(x); y.b = 2
    • 该功能需要您手动枚举对象的所有键。因此会引入人为错误

    我认为该解决方案在特定情况下仍然可以用作解决方法

    原文:

    如果数组anewType[] 类型,那么a 的每个元素,考虑x = a[0] 将是newType 类型。 xnewType 类型,因为 x 满足 newType 类型的所有属性和方法。

    因此如果反转,如果x y znewType 类型并且它们是数组a 的唯一和所有元素,因此a 的每个元素都是newType 类型,它满足anewType[] 类型的条件

    // Check if obj has all keys props[]
    const hasAllProperties = <T,>(obj: any, props: (keyof T)[]): obj is T => {
      return props.every((prop) => {
        // console.log(prop)
        return Object.prototype.hasOwnProperty.call(obj, prop)})
    }
    
    // Check that arr is an array of newType , ie arr: newType[]
    const isArrayOf = <T,>(obj: any[], props: (keyof T)[]): obj is T[] => {
      // Check if every elements have all keys in props
      return obj.every((ele) => {
        // console.log(ele)
        return hasAllProperties<T>(ele,props)
      }
      )
    }
    
    if (isArrayOf<newType>(arr, ["foo", "bar"])) {
      console.log("arr is of newType[]")
    }
    

    TS Playground

    【讨论】:

    • Object.prototype.hasOwnProperty.call(obj, prop)}) - 该属性不需要是自己的,它就可以存在于对象上。考虑x = {a: 1}; y = Object.create(x); y.b = 2 现在y 有两个字段ab 因此满足interface AB { a: number; b: number; } 但是a 不是自己的财产。考虑使用prop in obj
    • 函数调用为isArrayOf&lt;newType&gt;(arr, ["foo", "bar"]) 也存在问题 - 这需要您手动枚举对象的所有键。但是,如果您不这样做,这不是编译错误,因此isArrayOf&lt;newType&gt;(arr, ["foo"]) 会声称arr = [{foo: "hello}, {foo: "world"}] 不是newType[]。例如,如果界面发生变化,这可能是一个问题。也许调用曾经是正确的,但将来有人添加或删除了一个属性 - 他们现在必须去更改 isArrayOf 的所有调用
    • 我们可以通过stackoverflow.com/questions/43909566/…避免第二个缺点
    猜你喜欢
    • 2022-01-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-04
    • 1970-01-01
    • 2020-10-26
    • 2020-03-21
    • 1970-01-01
    相关资源
    最近更新 更多