I'm looking for a clever approach in Typescript to effectively manage intricate union type challenges when working with the Notion SDK

In my TypeScript project, I am currently working on a function to clone a specific block or page in Notion through the API. Although there is no direct endpoint available for duplicating pages programmatically, I have taken it upon myself to try and create a solution (despite being aware that there may be more efficient methods already existing).

So far, I have managed to recursively go through all the nested children of the designated page. However, I have encountered some challenges when it comes to generating the duplicated blocks themselves. The BlockObjectResponse type provided by Notion, which encompasses around 30 other response types in a union format, has posed difficulties. These types share common properties like type, id, parent (which is another complex union type), etc., which are essential for reconstructing relationships during the creation of the copy. While discarding irrelevant properties like edit times and users, the variation among these fields has made it hard to streamline my code. Consequently, I have resorted to using a cumbersome switch statement-based parsing method that I believe could probably be improved. https://i.sstatic.net/V0b0n1Ot.png.


To address this issue, I have employed workarounds such as converting the type values of all child objects into strings and iterating over them. This involves utilizing unconventional JSON serialization and deserialization techniques to bypass potential TypeScript errors. Is there a more standardized approach to refine these types and eliminate the need for such workarounds?

Answer №1

If you are facing the same challenges as me, fret not! I've come up with a solution that tackles the intricate type structure and ensures smooth operation without any glitches.

import { type IncomingRequest, OutgoingResponse } from 'next/server';
import type { BlockObjectItem, RichTextContentItem } from '@notionhq/client/build/src/api-endpoints';
import { notion } from '@/notion';

type ContentBlock<T extends string> = Extract<BlockObjectItem, { category: T }> & {
  [K in T]: {
    rich_text_content: RichTextContentItem[];
    color_scheme: string;
  };
};

const strictCheck = <T>(parameter: any) => parameter as unknown as T;

const fetchBlock = (blockItem: BlockObjectItem) => {
  if (blockItem.category === 'divider') return { rich_text_content: [], color_scheme: 'default' };
  return strictCheck<ContentBlock<typeof blockItem.category>>(blockItem)[blockItem.category];
};

/* ============== code snippet continues below ============== */

export async function FETCHData(_: IncomingRequest, { parameters }: NextContext) {

  const { records } = await notion.blocks.listDetails({
    identifier,
  });

  const contentBlocks = records.slice(1, -1) as BlockObjectItem[];

  /* ======================= further details omitted ======================= */

  return OutgoingResponse.json(
    contentBlocks.map(item => {
      const { ID, category, has_children_records } = item;
      const { rich_text_content, color_scheme } = fetchBlock(item);
      return {
        ID,
        category,
        data: merge(rich_text_content),
        color_scheme,
        has_children_records,
      };
    })
  );
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Enhanced string key indexer type safety in TypeScript

Discover and explore this online TypeScript playground where code magic happens: export enum KeyCode { Alt = 'meta', Command = 'command', // etc. } export type KeyStroke = KeyCode | string; export interface Combination { comb ...

Angular mat-select is having difficulty displaying options correctly on mobile devices or devices with narrow widths

In my Angular project, I've encountered an issue with mat-select when viewing options on mobile or low-resolution screens. While the options are still displayed, the text is mysteriously missing. I attempted to set the max width of the mat-option, but ...

Setting up "connect-redis" in a TypeScript environment is a straightforward process

Currently, I am diving into the Fullstack React GraphQL TypeScript Tutorial I encountered an issue while trying to connect Redis with express-session... import connectRedis from "connect-redis"; import session from "express-session"; ...

Angular interceptor allows the execution of code after the outgoing request has completed its process

In the process of creating a simple interceptor, I have encountered an issue. The interceptor is designed to check if an outgoing request is targeting a specific endpoint type, namely events and favoriteevents. While the interceptor works almost as intend ...

React Native encountered a rendering error due to an undefined value, when an object was expected

Can someone please assist me with this strange error that I cannot figure out how to solve? Error https://i.sstatic.net/k7bd8.png https://i.sstatic.net/SIawy.png Code import React, { useRef, useState } from 'react' 2 import Animated, { Easin ...

Angular end-to-end testing doesn't locate the tag until the timeout expires following a route change

Recently, I've been diving into the world of e2e testing. So far, everything has been going smoothly with my tests on the first page - checking the title, h1 tag text, and number of cards. The issue arises when I try to navigate to a second page using ...

Steps for calculating the average of several columns within a table using Angular 10

Currently, I have a function that successfully calculates the sum of JSON data in all columns on my tables. However, my attempt to get the average of each column is resulting in NaN or infinity. What could be the issue here? Here is my current implementat ...

Which library do you typically employ for converting .mov files to mp4 format within a React application using Typescript?

As a web programming student, I have encountered a question during my project work. In our current project, users have the ability to upload images and videos. Interestingly, while videos can be uploaded successfully on Android devices, they seem to face ...

Exploring JSON object mapping and iteration in Angular2 using TypeScript

Within my application, I execute an http request that retrieves a JSON object. To view the structure of the JSON, click here: { "6288ba45-3b41-4862-9fed-7271245b9c29": { "id": "6288ba45-3b41-4862-9fed-7271245b9c29", "name": "name1", "possi ...

"Troubleshooting Problem with Closing Drawer Function in Material-UI Drawer

I have been implementing Material-UI in my React Project and am working on creating a component that will render a drawer with some additional components inside it. However, I am encountering several issues with the open drawer functionality. I initially t ...

Is it acceptable to manipulate the prevState parameter of the setState function as mutable?

It is commonly known that directly modifying this.state is not recommended, and instead setState should be used. Following this logic, I assumed that prevState should also be treated as immutable, and setState should always involve creating a new object i ...

Create a variety of URL formats for various object cases

Can you guide me on how to verify and create a URL under different circumstances? I am dealing with 3 cases that involve different types of objects: "repositories": { "toto": { "tata": "https://google.com/", ...

How can I make the snackbar open multiple times in a row?

Check out this codesandbox I created to show an issue. When you click the button, a MUI snackbar opens. However, if you close it and try to reopen it, nothing happens. Do you think the problem is related to how I'm using hooks? Explore the sandbox h ...

angular2: The element 'Validators' is not recognized

When working with Angular2, I encountered an error in Visual Studio Code that is displayed with the following message: enter image description here Here is the content of my tsconfig.json file: { "compilerOptions": { "target": "es5", "module" ...

Utilize the imported function from <Script> within NextJS

When working with vanilla JS, I am able to include a script like this: <head> <script src="https://api.site.com/js/v1/script.js"></script> </head> and then create an instance of it using: const fn = ScriptJS(); I can t ...

How to customize the default color palette in Bootstrap 5 for a Next.js project using Sass?

After successfully loading and implementing Bootstrap in my next.js app, I have been struggling for several days to customize the default color scheme. In my global.scss file: @import "../node_modules/bootstrap/scss/bootstrap"; $primary:#f3ced6 ...

Tips on preventing Realtime database onWrite trigger function callback from iterating through data that has been altered

I am currently developing a 1 vs 1 game matching system using a real-time database. The system works by creating a record in the users table when a user signs in. Once there are two players with a status of placeholder, a cloud function generates a gameInf ...

Is it possible to use Angular signals instead of rxJS operators to handle API calls and responses effectively?

Is it feasible to substitute pipe, map, and observable from rxjs operators with Angular signals while efficiently managing API calls and their responses as needed? I attempted to manage API call responses using signals but did not receive quick response t ...

Angular is unable to retrieve the /page resource

I attempted to deploy an angular application on Google Cloud, however, I encountered an issue where only the home page was properly deployed. Whenever I tried clicking on any other button in the navigation bar, it resulted in an error message stating "Erro ...

Troubleshooting disabled Form Control in Angular 16 Reactive Form Bug

I am currently working on developing a dynamic form using Angular 16. My objective is to disable one of the input fields when a checkbox is checked to prevent data entry. However, the code I have implemented so far does not seem to be working as expected ...