Improve typings related to partial object returns
See original GitHub issueDescribe the bug
The QueryDatabaseResponse
type as defined in api-endpoints.d.ts
does not expose the properties
field on the objects within the results
array. The results array is typed as a large union which does not allow type discrimination to narrow the result type.
To Reproduce Node version: 14 Notion JS library version: 1.0.4 Typescript version: 4.4.5
This snippet will generate the type issue - this is intended for reproduction of the typing issue, and not as validly executable code.
import { Client } from '@notionhq/client';
const client = {} as Client;
const data = await client.databases.query({ database_id: 'foo' });
const resultObject = data.results[0];
if (resultObject.object === 'page') {
resultObject.properties; // <-- a type error is generated here
}
TS error:
TS2339: Property 'properties' does not exist on type '{ parent: { type: "database_id"; database_id: string; } | { type: "page_id"; page_id: string; } | { type: "workspace"; workspace: true; }; properties: Record<string, { type: "number"; number: number | null; id: string; } | ... 17 more ... | { ...; }>; ... 9 more ...; url: string; } | { ...; }'. Property 'properties' does not exist on type '{ object: "page"; id: string; }'.
Expected behavior
I would expect optional properties that are available on the returned results objects to be typed as optional, or a field to be present which can be used to discriminate the union, and allow access to the properties
field.
Additional context
The type is defined as such:
export declare type QueryDatabaseResponse = {
type: "page";
page: EmptyObject;
object: "list";
next_cursor: string | null;
has_more: boolean;
results: Array<{
parent: {
type: "database_id";
database_id: IdRequest;
} | {
type: "page_id";
page_id: IdRequest;
} | {
type: "workspace";
workspace: true;
};
properties: Record<string, { /** truncated here */}>;
/** removed some properties here for brevity */
object: "page";
id: string;
created_time: string;
last_edited_time: string;
archived: boolean;
url: string;
} | {
object: "page";
id: string;
}>;
};
I believe that the last union is the issue - as it has the effect of removing all the previously declared types, and because object: "page";
is shared by both types in this union there is no property that can be used as a discriminator, in order to get access to the other fields.
Two solutions that I see:
- add a type discriminator to the response
- modify this type such that fields that may be optional are defined as optional. For example:
export declare type QueryDatabaseResponse = {
type: "page";
page: EmptyObject;
object: "list";
next_cursor: string | null;
has_more: boolean;
results: Array<{
object: "page";
id: string;
parent?: {
type: "database_id";
database_id: IdRequest;
} | {
type: "page_id";
page_id: IdRequest;
} | {
type: "workspace";
workspace: true;
};
properties?: Record<string, { /** truncated here */}>;
/** removed some properties here for brevity */
created_time?: string;
last_edited_time?: string;
archived?: boolean;
url?: string;
}>;
};
I think 2 is simpler as it does not require any changes to the underlying api - whereas 1 may require an extra field be returned to allow type discrimination.
Issue Analytics
- State:
- Created a year ago
- Reactions:22
- Comments:7 (1 by maintainers)
Top GitHub Comments
This seems to be working for me, maybe this will help somebody:
Hi! In our last release we added some helper and utility functions.
https://github.com/makenotion/notion-sdk-js#typescript
Here is an example:
Please re-open this or open a new issue if there are other utilities you find are lacking here and thank you for the feedback.