Typescript undvik att exportera typer med generiska


Jag lär mig Typescript och har problem med generics.

Jag har skapat en enkel funktion som skriver ut nycklar/värden för ett givet objekt i en viss ordning. En ytterligare etikett-egenskap kan tillhandahållas för att skriva ut ett alternativ till nyckelsträngen.

// object-printer.ts
interface Spec<T> {
  key: keyof T;
  label?: string;
}

export default function objectPrinter<T>(ob: T, printSpec: Spec<T>[]): void {
  printSpec.forEach((spec) => {
    let label = spec.label || spec.key;
    console.log(`${label}: ${ob[spec.key]}`);
  });
}

Jag importerar sedan funktionen och använder den

import objectPrinter from "./object-printer";

interface User {
  firstName: string;
  lastName: string;
  age: number;
}

const user: User = {
  firstName: "Wurzel",
  lastName: "Gummage",
  age: 100,
};

const printSpec = [
  { key: "age", label: "Trips Around Sun" },
  { key: "lastName" },
];

objectPrinter(user, printSpec); // Typescript complains about type of printSpec
// Trips Around Sun: 100
// lastName: Gummage

Typescript ger mig ett fel:

Argument av typen '{ key: string; label: string; } | { key: string; label?: undefined; }[]' kan inte tilldelas parametern av typen 'Spec[]'. Typen '{ key: string; label: string; } | { key: string; label?: undefined; }' kan inte tilldelas typen 'Spec'. Typen '{ key: string; label: string; }' kan inte tilldelas typen 'Spec'. Typerna för egenskapen 'key' är inkompatibla. Typen 'string' kan inte tilldelas typen 'keyof User'.

För att åtgärda detta kan jag också exportera interface Spec och ange typen för printSpec;

Finns det ett bättre sätt att härleda typen med generics eller är det nödvändigt att exportera typen Spec?

Svar #1

Problemet uppstår eftersom Typescript tilldelar en typ till din printSpec-variabel som använder string för att beskriva värdena "age" och "lastName". string är för bred eftersom vi inte längre vet att dessa värden är giltiga nycklar i User-gränssnittet.

Lösning #1: Ingen mellanvariabel

Du kommer inte att få några fel om du undviker den mellanliggande variabeln printSpec eftersom ingen typvidgning sker. Du kan skicka din specifikation direkt till objectPrinter-funktionen:

objectPrinter(user, [
  { key: "age", label: "Trips Around Sun" },
  { key: "lastName" },
]);

Lösning #2: as const

Typescript har en speciell as const-påstående som får det att se en variabel som en oföränderlig konstant och läsa alla strängar som deras bokstavliga värden.

Om vi deklarerar hela printSpec-variabeln as const måste vi hantera readonly-problem i typerna. Men vi kan lägga till as const till nyckelsträngarna.

const printSpec = [
  { key: "age" as const, label: "Trips Around Sun" },
  { key: "lastName" as const},
];

objectPrinter(user, printSpec);

Lösning #3: Tilldela Spec-typ

För att åtgärda detta kan jag också exportera interface Spec och ange typen för printSpec

Det du föreslår kommer definitivt att fungera. Du vet uppenbarligen redan detta men jag inkluderar det här för fullständighetens skull.

const printSpec: Spec<User>[] = [
  { key: "age", label: "Trips Around Sun" },
  { key: "lastName" },
];

objectPrinter(user, printSpec);