I’m working with the Notion API in TypeScript and running into a type issue when trying to access task block properties.
const fetchTaskBlocks = async () => {
const pageId = process.env.PAGE_ID;
const result = await notionClient.blocks.children.list({
block_id: pageId,
page_size: 25,
});
console.log(result.results[0].task);
};
TypeScript complains that task property doesn’t exist on type (PartialBlockObjectResponse | BlockObjectResponse). When I check the type definitions, the property is clearly defined:
type BlockObjectResponse = {
type: "task";
task: {
text_content: Array<RichTextItemResponse>;
theme: ApiColor;
completed: boolean;
};
id: string;
created_time: string;
// other properties...
}
I attempted creating a type guard:
function isTaskBlock(block: PartialBlockObjectResponse | BlockObjectResponse): block is BlockObjectResponse {
return "task" in block;
}
But got error about PartialBlockObjectResponse not being found. Also tried importing the type directly but it’s not exported from the package.
How can I properly access task block properties without TypeScript errors?
You need to narrow the type by checking both the block type and ensuring it’s a complete response. The issue is that PartialBlockObjectResponse lacks the detailed properties you need.
const fetchTaskBlocks = async () => {
const pageId = process.env.PAGE_ID;
const result = await notionClient.blocks.children.list({
block_id: pageId,
page_size: 25,
});
const block = result.results[0];
if (block.object === 'block' && 'type' in block && block.type === 'to_do') {
console.log(block.to_do);
}
};
Note that task blocks are actually called to_do in the API response, not task. I made this mistake initially and spent hours debugging. The type checking on block.object === 'block' ensures you’re working with a full BlockObjectResponse rather than a partial one, then you can safely access the to_do property after confirming the type.
The union type from Notion’s API response is causing the issue here. What worked for me was using type assertion after proper runtime checks. Instead of creating complex type guards, you can handle this more directly:
const fetchTaskBlocks = async () => {
const pageId = process.env.PAGE_ID;
const result = await notionClient.blocks.children.list({
block_id: pageId,
page_size: 25,
});
const block = result.results[0];
if ('type' in block && block.type === 'to_do') {
const taskBlock = block as any;
console.log(taskBlock.to_do);
}
};
The any assertion might seem crude but it’s practical when dealing with these API response types that aren’t perfectly typed. Make sure you’re checking for to_do not task as mentioned above. I’ve found that Notion’s TypeScript definitions sometimes lag behind their actual API implementation, so this approach gives you the flexibility to work with the real response structure while maintaining some type safety through the runtime checks.
honestly just cast it directly after checking the type exists. i’ve been using (block as any).to_do once i verify block.type === 'to_do' and it works fine. notion’s typescript support is kinda wonky anyway so dont overthink it