Is it possible to cast the ...rest
in an object destructuring assignment to be the same type as the original object?
The use case: a method that processes a Partial<Record>
of 10-ish properties and returns a new record of the same type as the input record, but with some properties removed, some properties modified, and most others unchanged.
A vastly simplified version of this function is below to illustrate the problem. TS Playground link.
type ABCD = Partial<{ a: number, b: number, c: number, d: number }>;
/** If `a` is positive, double it and remove `b`. If negative, just remove `a`. */
function f(record: ABCD) {
const {a, b, ...result} = record;
if (a !== undefined && a > 0) {
result.a = a * 2;
// Property 'a' does not exist on type '{ c?: number | undefined; d?: number | undefined; }'
} else {
result.b = b;
// Property 'b' does not exist on type '{ c?: number | undefined; d?: number | undefined; }'
}
return result;
}
I could solve the problem by removing type safety, as suggested in this answer:
const {a, b, ...result}: {a?: number, b?: number, [key: string]: number | undefined } = record;
But I'd prefer to retain type safety.
I could also fix this type issue by changing runtime code: use delete
instead of destructuring, assign result
to a new variable with the correct type, etc.
But this function is part of a TS port of a JS library that uses this "destructure and add properties back" pattern, and it's really convenient to retain wherever possible the same runtime code as the original library so that when the underlying JS library changes, the it's easier to apply those diffs to our TS port.
So I'm wondering if there's a way to solve this problem with only TS type casts or other TS syntax that's transpiled away at runtime, but without losing type safety on the result. Is there a way?