Varför kan inte TypeScript härleda returtypen för den här funktionen?
Jag försöker skriva några hjälpfunktioner som klonar objekt, men ändrar djupa egenskaper.
Här är ett exempel på en något värdelös version som bara ersätter en namngiven egenskap hos ett objekt med ett annat värde som kan vara av en annan typ. Koden fungerar - den ersätter den namngivna egenskapen med det nya värdet - men av någon anledning kan TypeScript inte lista ut typen på det skapade objektet och ger ett kompileringsfel. Varför gillar inte TypeScript detta och hur kan jag få det att fungera?
Jag skulle kunna specificera returtypen uttryckligen - men detta blir besvärligt med versioner av den här funktionen som ändrar en egenskap flera nivåer djupt.
it('change type of input object', () => {
const cloneWith = <T extends Record<string, unknown>, A extends keyof T, V>(
i: T,
a: A,
value: V
) => ({
...i,
[a]: value,
});
const source = { name: 'bob', age: 90 };
const result = cloneWith(source, 'age', 'old!');
// This test passes
expect(result.age).to.equal('old!');
// But compile error - number and string have no overlap -
// on this line
if (result.age === 'old!') {
console.log('That person is old!');
}
});
Svar #1
De härledda typerna för objektliteral som skapas med spridningssyntax är inte alltid idealiska, särskilt när beräknade egenskaper är inblandade. I det här fallet korsas T med en indexsignature {[x: string]: V}, medan Omit<T, A> & {[K in A]: V} skulle vara mer logiskt (korsningen av T utan egenskap A och ett separat objekt med nyckel A och värde T).
Jag ser inte hur du skulle kunna lösa detta utan en explicit returtyp, men om du lägger till returtypen, får åtminstone de korrekta typerna härledas. Kanske kan du göra något liknande för de andra versionerna av din funktion.
const cloneWith = <T, A extends keyof T, V>(
i: T,
a: A,
value: V
): Omit<T, A> & {[K in A]: V} => ({
...i,
[a]: value,
});
const source = { name: 'bob', age: 90 };
const result = cloneWith(source, 'age', 'old!');
type TestSource = typeof source.age // number
type TestResult = typeof result.age // string
TypeScript-lekplats
