Typescript Typeguard kontrollera om arrayen är av typen


Jag vill skriva en typvakt för att kontrollera om alla barn i en array är av typen T och på så sätt göra det till en Array där T är en generisk typ

TS Lekplats

// 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
}

Svar #1

Det bästa du kan göra är detta:

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 kommer aldrig kunna gissa att arrayen typad som any[] faktiskt innehåller NewType-element. Som allt i TypeScript är typförutsättningar statiska och kommer inte att returnera en typ dynamiskt baserat på vad som skickas som parameter vid körning. Men om du typar den som NewType[] kan du då extrahera NewType-typen från den.

Svar #2

Redigera 1:

Detta svar har två brister (nämnda i kommentarer)

  • Egenskapen behöver inte vara egen för att den ska existera på objektet x={a: 1}; y=Object.create(x); y.b=2
  • Funktionen kräver att du manuellt uppräknar alla nycklar i objektet. Detta kan därför introducera mänskliga fel

Jag tror att lösningen fortfarande kan användas som en tillfällig lösning i specifika omständigheter

Original :

Om en array a är av typen newType[] så kommer varje element i a, till exempel x=a[0], att vara av typen newType. x är av typen newType eftersom x uppfyller alla egenskaper och metoder för typen newType.

Så om det omvändes, om x y z är av typen newType och de är de enda och alla element i arrayen a, så är varje element i a av typen newType, vilket uppfyller villkoret för att a ska vara av typen newType[]

// 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 Lekplats