A mapped type can make every property optional for update objects.

optional mapped property Adding `?` inside a mapped type creates a version where each key may be omitted.

Partial Update Shape

doneValue
partial_update.ts
Replay: real traced execution (multi-file project)
type Task = {
    title: string;
    done: boolean;
    points: number;
};

type Update<T> = {
    [K in keyof T]?: T[K];
};

function applyUpdate(task: Task, update: Update<Task>): string {
    const next: Task = { ...task, ...update };
    return `${next.title}:${next.done}:${next.points}`;
}

const doneValue: boolean = false;
const original: Task = { title: "Trace", done: false, points: 3 };
const update: Update<Task> = { done: doneValue, points: 5 };

console.log(applyUpdate(original, update));
type Task = {
    title: string;
    done: boolean;
    points: number;
};

type Update<T> = {
    [K in keyof T]?: T[K];
};

function applyUpdate(task: Task, update: Update<Task>): string {
    const next: Task = { ...task, ...update };
    return `${next.title}:${next.done}:${next.points}`;
}

const doneValue: boolean = true;
const original: Task = { title: "Trace", done: false, points: 3 };
const update: Update<Task> = { done: doneValue, points: 5 };

console.log(applyUpdate(original, update));
  1. doneValue ← false, original ← [object Object], update ← [object Object]

    13    return `${next.title}:${next.done}:${next.points}`;14}1516const doneValue→ false: boolean = false;  //@doneValue=true17const original→ [object Object]: Task = { title: "Trace", done: false, points: 3 };18const update→ [object Object]: Update<Task> = { done: doneValuefalse, points: 5 };1920console.log(applyUpdate(original[object Object], update[object Object]));
  2. next ← [object Object]

    8    [K in keyof T]?: T[K];9};1011function applyUpdate(task[object Object]: Task, update[object Object]: Update<Task>): string {12    const next→ [object Object]: Task = { ...task[object Object], ...update[object Object] };13    return `${next.titleTrace}:${next.donefalse}:${next.points5}`;14}
  3. console.log(applyUpdate(original, update));

    17const original: Task = { title: "Trace", done: false, points: 3 };18const update: Update<Task> = { done: doneValue, points: 5 };1920console.log(applyUpdate(original[object Object], update[object Object]));
    outputTrace:false:5
  1. doneValue ← true, original ← [object Object], update ← [object Object]

    13    return `${next.title}:${next.done}:${next.points}`;14}1516const doneValue→ true: boolean = true;17const original→ [object Object]: Task = { title: "Trace", done: false, points: 3 };18const update→ [object Object]: Update<Task> = { done: doneValuetrue, points: 5 };1920console.log(applyUpdate(original[object Object], update[object Object]));
  2. next ← [object Object]

    8    [K in keyof T]?: T[K];9};1011function applyUpdate(task[object Object]: Task, update[object Object]: Update<Task>): string {12    const next→ [object Object]: Task = { ...task[object Object], ...update[object Object] };13    return `${next.titleTrace}:${next.donetrue}:${next.points5}`;14}
  3. console.log(applyUpdate(original, update));

    17const original: Task = { title: "Trace", done: false, points: 3 };18const update: Update<Task> = { done: doneValue, points: 5 };1920console.log(applyUpdate(original[object Object], update[object Object]));
    outputTrace:true:5