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;
