TypeScript: funktion, der returnerer en generisk type, som udvider Record


Jeg vil oprette en parse<Type>()-funktion, der parser en streng og returnerer et objekt, men jeg ønsker, at parse() skal returnere den korrekte type, og jeg har svært ved det.

type Identity = {
  name: string;
};

type ContactDetails = {
  phone: string;
  email: string;
};

function parse<T extends Record<string, string>>(input: string): T {
  const result = {};
  input.split('&').forEach(bits => {
    const [name, value] = bits.split('=');
    result[name] = value;
  });
  return result;
}

parse<Identity>('name=Paulin');
parse<ContactDetails>('phone=0123456789&[email ');

Jeg får en TypeScript-fejl:

Type '{}'' kan ikke tildeles typen 'T'. '{}' kan tildeles begrænsningen af typen 'T', men 'T' kunne blive instansieret med en anden under-type af begrænsningen 'Record'.ts(2322)

Jeg forstår, hvad problemet er: {} er ikke kompatibelt med en generisk Record-type. Men hvordan kan jeg løse dette?

Mange tak!

Svar #1

Jeg er ret sikker på, at dette er et af de steder, hvor du er nødt til at ty til en typepåstand. I dette tilfælde:

function parse<T extends Record<string, string>>(input: string): T {
  const result: Record<string, string> = {};
// −−−−−−−−−−−^^^^^^^^^^^^^^^^^^^^^^^^
  input.split('&').forEach(bits => {
    const [name, value] = bits.split('=');
    result[name] = value;
  });
  return result as T;
// −−−−−−−−−−−−^^^^^
}

På legepladsen

Det er dog vigtigt at forstå begrænsningerne her. Hvis dine Identity eller ContactDetails typer havde specifikke implementeringer (det har de ikke i dit eksempel, de er bare interfaces), vil de objekter, som parse returnerer, ikke være understøttet af den implementering. Det er bare et Record<string, string>. Så du ville muligvis være bedre tjent med at lade parse returnere et Record<string, string> i stedet, og lade det være op til kaldende part at træffe en informeret beslutning om typen.

Svar #2

Baseret på hvad T.J. Crowder foreslog: lad det være op til den, der ringer, at træffe beslutningen om typen. her er min endelige implementering:

type Identity = {
  name: string;
};

type ContactDetails = {
  phone: string;
  email: string;
};

function parse(input: string): Record<string, string> {
  const result: Record<string, string> = {};
  input.split('&').forEach(bits => {
    const [name, value] = bits.split('=');
    result[name] = value;
  });
  return result;
}

const identity = parse('name=Paulin') as Identity;
const details = parse('phone=0123456789&[email ') as ContactDetails;

Relaterede artikler