Typescript generisk og IkkeNullable


Jeg har et skriveproblem med min generiske funksjon. Problemet dukker opp gjennom flere tredjepartsbiblioteker, men jeg klarte å lage et minimalt eksempel.

interface MyType {
  id: string;
}

function myFunction<T extends MyType>(param: T) {
  // Type 'string' is not assignable to type 'NonNullable<T["id"]>'.
  const a: NonNullable<T["id"]> = "my-id";
  console.log(a);
}

Hvis jeg skriver om denne funksjonen uten generisk, fungerer typing fint:

function myFunction(param: MyType) {
  const a: NonNullable<MyType["id"]> = "my-id";
  console.log(a);
}

Jeg sjekket dokumentasjonen for generics og kan ikke finne hvorfor den første koden er feil. Kan noen hjelpe meg med det?

Svar #1

Opp til og med TypeScript 4.7 ble hjelpefunksjonstypen NonNullable<T> implementert som en betinget type, T extends null | undefined ? never : T.

Når en betinget type avhenger av en generisk typeparameter, som i typen NonNullable<T["id"]> inne i kroppen til myFunction(), utsetter kompilatoren fullstendig evalueringen av den; den vet rett og slett ikke hvilken type det er ennå, i det hele tatt. Slike typer er i hovedsak ugjennomsiktige for kompilatoren, så den kan ikke verifisere at en verdi av en annen type kan tildeles den. Du må refaktorere eller bruke typeassertioner for å utføre en slik tildeling:

const a = "my-id" as NonNullable<T["id"]>; // okay, TS4.7-

Heldigvis vil TypeScript 4.8 introdusere noe bedre støtte for reduksjon av snitt og innsnevring, som inkluderer redefinering av NonNullable<T> som T & {}. Det vil si at i stedet for en betinget type, er det bare et snitt med den tomme objekttypen (som kan være hva som helst bortsett fra null eller undefined). Detaljer om hvordan dette fungerer finnes i microsoft/TypeScript#49119, men for ditt formål trenger du bare vite at ditt opprinnelige eksempel bare vil begynne å fungere:

const a: NonNullable<T["id"]> = "my-id"; // okay TS4.8+

Lenke til kode på lekeplassen

Relaterte artikler