Validating FlowTypes for HTTP PATCH Requests in RESTful APIs

Let’s say I have a todo list application. I might represent the type for the objects associated with these endpoints as:

type Todo = {
id?: number,
title: string,
description: string
};

Now consider the following code:

// @flow
type Todo = {
id?: number,
title: string,
description: string
};
function patchTodo(todo: Todo) {}
const todo = {
title: 'title'
};
patchTodo(todo);

If I try to type todo as Todo Flow will return the following error:

15: patchTodo(todo);
^^^^^^^^^^^^^^^ function call
9: function patchTodo(todo: Todo) {}
^^^^ property `description`. Property not found in
15: patchTodo(todo);
^^^^ object literal

description is a required key for Todo, even though we don’t need to send it as part of our PATCH request in order to update the title. How can we type patchTodo(todo) to allow us to send partial objects?

One approach would be to maintain a second type with all optional arguments:

type PatchTodo = {
id?: number,
title?: string,
description?: string
};

The problem is this approach isn’t very DRY. Every time we add, update, or delete a property on this object we have to do it in two places. Ideally we want to only maintain one type.

Enter $Shape<T>

$Shape<T> is a utility type I found reading this post. It has the following properties:

Type $Shape<T> has two differences from T: objects of this type cannot contain properties not described in T and may not contain required properties of type T.

This means we can validate PATCH requests using our original Todo type:

// @flow
function patchTodo(todo: $Shape<Todo>) {}
const todo = {
title: 'title'
};
const malformedTodo = {
title: 'title',
baz: 'baz'
};
patchTodo(todo);
patchTodo(malformedTodo);

patchTodo(malformedTodo) will give us the following error:

21: patchTodo(malformedTodo);
^^^^^^^^^^^^^^^^^^^^^^^^ function call
21: patchTodo(malformedTodo);
^^^^^^^^^^^^^ property `baz` of object literal. Property not found in
9: function patchTodo(todo: $Shape<Todo>) {}
^^^^ object type

Now any time ourTodo type changes thePATCH type check will still look for these updated properties without us having to maintain two types!