javascript - Kan inte få TypeScript-typningarna att matcha


Jag kan inte få följande kod att fungera.

Jag försöker hämta lite data och hantera alla falska/felaktiga fall och sedan skicka vidare den rensade datan till en annan funktion. Men typningen verkar inte stämma när jag kallar den andra funktionen.

Typen 'Error | number[]' kan inte tilldelas typen 'number[]'

interface SomeType {
  a: number[] | null | Error
  b: number[] | null
}

type Omit <T, K> = Pick<T, Exclude<keyof T, K>>
type ExcludeUnusableValues<T,K extends keyof T,C = Error | null>
  = Omit<T, K> & {
    [a in K]-? : Exclude<T[a], C>
  }

function fetchData() {
  const obj: SomeType = {
    a: [1, 2, 3],
    b: null
  }
  if (obj.a === null || obj.a instanceof Error) {
    return null
  }

  //  Type 'Error | number[]' is not assignable to type 'number[]'
  useData(obj)
}

function useData(param1: ExcludeUnusableValues < SomeType, 'a' > ) {
  console.log(param1)
}

TypeScript-lekplatslänk

Svar #1

Du kan kringgå problemet genom att flytta din typkontrollslogik till en typ-predikat. Här är ditt exempel, modifierat med isUsableType:

interface SomeType {
  a: number[] | null | Error
  b: number[] | null
}

function isUsableType(obj: SomeType): obj is ExcludeUnusableValues < SomeType, 'a' > {
  return obj.a!==null && !(obj.a instanceof Error)
}

type Omit <T, K> = Pick<T, Exclude<keyof T, K>>
type ExcludeUnusableValues<T,K extends keyof T,C = Error | null>
  = Omit<T, K> & {
    [a in K]-? : Exclude<T[a], C>
  }

function fetchData() {
  const obj: SomeType = {
    a: [1, 2, 3],
    b: null
  }

  if (isUsableType(obj)) {
    useData(obj)
  } else return null;

}

function useData(param1: ExcludeUnusableValues < SomeType, 'a' > ) {  
  console.log(param1)
}

Svar #2

Kort svar: Ta bort typen Error från SomeType.a.
Utförligt svar:

a: number[] | null;

a kan vara antingen en array av nummer eller null. Det är helt okej eftersom antingen finns a eller så finns den inte.

a: number[] | null | Error;

Här orsakar typen Error problem eftersom Error inte har samma egenskaper/funktioner som en array av nummer har. För att detta ska fungera måste alla typer ha vissa liknande egenskaper.

En annan lösning: Typkasta obj till lämplig typ:

useData(<{a: number[], b: number[]}>obj)