Visual Studio Code - Hvordan kan jeg se den fullstendig utvidede kontrakten til en Typescript-type?


Hvis jeg har en samling av typer som ser litt slik ut:

type ValidValues = string | number | null
type ValidTypes = "text" | "time" | "unknown"

type Decorated = {
  name?: string | null
  type?: ValidTypes
  value?: ValidValues
  title: string
  start: number
}

type Injected = {
  extras: object
}

// overriding the types from Decorated
type Text = Decorated & Injected & {
  name: string
  type: "text"
  value: string
}

Min faktiske kode har mer som skjer, men dette viser kjerneideen. Jeg vil ikke måtte stole på meg selv for å få forholdet mellom typene helt riktig. Jeg vil at verktøy skal vise meg hva typedefinisjonen for Text «evalueres» til, etter all typealgebraen.

Så for eksemplet ovenfor håper jeg at feltene spesifisert i Text vil overstyre de tidligere erklæringene gjort i Decorated-typen, og at utdataene fra en hypotetisk verktøytips skal vise meg noe som dette:

{
  name: string
  type: "text"
  value: string
  title: string
  start: number
  extras: object
}

Finnes det en enkel måte å få tak i denne informasjonen på?

Svar #1

Den raske informasjonen for en type som vises med IntelliSense etterlater ofte noe å ønske; du får vanligvis en enkelt fremstilling for en gitt type, som kan vise seg å være for kortfattet eller til og med for omstendelig for dine formål. Det finnes noen forslag for å gjøre det mer fleksibelt (f.eks. microsoft/TypeScript#25784 og microsoft/TypeScript#28508) slik at brukere kunne utvide/sammentrekke typedefinisjoner i sine IDEer. Men jeg vet ikke om de vil bli fulgt opp i nær eller til og med fjern fremtid, så la oss ikke vente på det.


Her er en typealias jeg noen ganger bruker for å prøve å utvide en type på den måten du snakker om:

// expands object types one level deep
type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;

// expands object types recursively
type ExpandRecursively<T> = T extends object
  ? T extends infer O ? { [K in keyof O]: ExpandRecursively<O[K]> } : never
  : T;

De bruker betinget typeinferens for å "kopiere" en type T inn i en ny typevariabel O og deretter en identitetslignende kartlagt type som itererer gjennom de kopierte typens egenskaper.

Den betingede typeinferensen er konseptuelt en no-op, men den brukes for å distribuere unionstyper og for å tvinge kompilatoren til å evaluere «true»-grenen av den betingede (hvis du omdefinerer Expand<T> uten den, vil kompilatoren noen ganger bare produksere den mappede typen {[K in keyof RelevantType]: RelevantType[K]}, noe som ikke er det du ønsker å se).

Forskjellen mellom Expand og ExpandRecursively er om det bare skal vise egenskapstypene som de er (Expand), eller om det skal utvide egenskapstypene (ExpandRecursively). Det hjelper i det rekursive tilfellet å ikke prøve å grave ned i primitive typer, så derfor er T extends object-betingelsen inkludert.


OK, la oss se hva som skjer når vi bruker det på typen din. Vi trenger ikke ExpandRecursively i ditt tilfelle, men vi kan bruke det... det gir samme resultat:

type ExpandedText = Expand<Text>;

som, når vi holder musen over det i IDE-en (TypeScript Playground og VSCode uansett), vises som:

/* type ExpandedText = {
  name: string;
  type: "text";
  value: string;
  title: string;
  start: number;
  extras: object;
 } */

som du ønsket.

Lenke til kode

Svar #2

Bare supplerer jcalz sitt svar med versjoner som fungerer med funksjoner.

De endrede hjelperne:

export type Expand<T> = T extends (...args: infer A) => infer R
  ? (...args: Expand<A>) => Expand<R>
  : T extends infer O
  ? { [K in keyof O]: O[K] }
  : never;

export type ExpandRecursively<T> = T extends (...args: infer A) => infer R
  ? (...args: ExpandRecursively<A>) => ExpandRecursively<R>
  : T extends object
  ? T extends infer O
    ? { [K in keyof O]: ExpandRecursively<O[K]> }
    : never
  : T;

For å teste, legger jeg til en subobject-nøkkel til Text-typen, og legger til et funksjonsgrensesnitt:

type Text = Decorated &
  Injected & {
    name: string;
    type: "text";
    value: string;
    subobject: Injected;
  };

interface SomeFunction {
  (...args: Text[]): Injected & { error: boolean };
}

Dette gir:

Objekter fungerer fortsatt bra:

Svar #3

Jeg fant ut at denne VS Code-utvidelsen fungerer perfekt for dette:

Pynt opp TypeScript: Bedre typeforhåndsvisninger

Den viser til og med dypt nestede typer når du holder musepekeren over dem.

Relaterte artikler