You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用TypeScript处理Notion SDK复杂联合类型问题的优雅方案?

处理Notion BlockObjectResponse联合类型的TypeScript最佳实践

1. 自定义类型守卫收窄类型

针对每种Block类型编写类型谓词函数,让TypeScript能精准识别类型,替代零散的type判断:

import type { BlockObjectResponse, ParagraphBlockObjectResponse } from '@notionhq/client';

// 段落类型守卫
function isParagraphBlock(block: BlockObjectResponse): block is ParagraphBlockObjectResponse {
  return block.type === 'paragraph';
}

// 标题类型守卫(示例)
function isHeading1Block(block: BlockObjectResponse): block is Extract<BlockObjectResponse, { type: 'heading_1' }> {
  return block.type === 'heading_1';
}

使用时,TypeScript会自动收窄类型,无需额外断言:

const TARGET_PAGE_ID = 'your-target-page-id';

function cloneRichText(text: any) {
  // 实现富文本克隆逻辑
  return text;
}

function cloneBlock(block: BlockObjectResponse) {
  if (isParagraphBlock(block)) {
    // 此处block自动推断为ParagraphBlockObjectResponse
    return {
      type: block.type,
      parent: { type: 'page_id', page_id: TARGET_PAGE_ID },
      paragraph: {
        rich_text: block.paragraph.rich_text.map(cloneRichText)
      }
    };
  }
  if (isHeading1Block(block)) {
    // 此处block自动推断为Heading1BlockObjectResponse
    return {
      type: block.type,
      parent: { type: 'page_id', page_id: TARGET_PAGE_ID },
      heading_1: {
        rich_text: block.heading_1.rich_text.map(cloneRichText),
        is_toggleable: block.heading_1.is_toggleable
      }
    };
  }
  // 其他类型同理实现
}

2. 用类型映射+处理器对象替代Switch语句

把每种类型的处理逻辑封装成独立函数,通过类型映射关联类型与处理器,比switch更易维护和扩展:

首先定义类型映射:

type BlockType = BlockObjectResponse['type'];
type BlockByType<T extends BlockType> = Extract<BlockObjectResponse, { type: T }>;

// 处理器类型定义
type BlockProcessor<T extends BlockType> = (block: BlockByType<T>) => Omit<BlockByType<T>, 'id' | 'created_time' | 'last_edited_time' | 'created_by' | 'last_edited_by'> & { parent: { type: 'page_id'; page_id: string } };

然后创建处理器对象:

const blockProcessors: Record<BlockType, BlockProcessor<any>> = {
  paragraph: (block) => ({
    type: 'paragraph',
    parent: { type: 'page_id', page_id: TARGET_PAGE_ID },
    paragraph: { rich_text: block.paragraph.rich_text.map(cloneRichText) }
  }),
  heading_1: (block) => ({
    type: 'heading_1',
    parent: { type: 'page_id', page_id: TARGET_PAGE_ID },
    heading_1: {
      rich_text: block.heading_1.rich_text.map(cloneRichText),
      is_toggleable: block.heading_1.is_toggleable
    }
  }),
  // 依次实现其他30+类型的处理器
};

最终克隆函数可以简化为:

function cloneBlock(block: BlockObjectResponse) {
  return blockProcessors[block.type](block as BlockByType<typeof block.type>);
}

3. 用工具类型简化属性过滤

利用TypeScript内置的Extract、Omit工具类型,快速生成需要的目标类型,避免手动重复定义:

// 提取指定类型的Block并过滤无关属性
type TargetParagraphBlock = Omit<
  Extract<BlockObjectResponse, { type: 'paragraph' }>,
  'id' | 'created_time' | 'last_edited_time' | 'created_by' | 'last_edited_by'
> & { parent: { type: 'page_id'; page_id: string } };

// 克隆函数返回值自动匹配目标类型
function cloneParagraphBlock(block: Extract<BlockObjectResponse, { type: 'paragraph' }>): TargetParagraphBlock {
  return {
    type: 'paragraph',
    parent: { type: 'page_id', page_id: TARGET_PAGE_ID },
    paragraph: { rich_text: block.paragraph.rich_text.map(cloneRichText) }
  };
}

4. 替代JSON序列化的类型安全方案

不要用JSON.parse(JSON.stringify(block))来规避类型报错,直接构造新对象并保留所需属性:

// 错误示例:丢失类型信息且不安全
// const cloned = JSON.parse(JSON.stringify(block));

// 正确示例:类型安全的属性复制
function cloneBlockBaseProperties(block: BlockObjectResponse) {
  return {
    type: block.type,
    parent: { type: 'page_id', page_id: TARGET_PAGE_ID }
  };
}

// 结合类型守卫使用
if (isParagraphBlock(block)) {
  const base = cloneBlockBaseProperties(block);
  return {
    ...base,
    paragraph: { rich_text: block.paragraph.rich_text.map(cloneRichText) }
  };
}

内容的提问来源于stack exchange,提问作者Recaptchow

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 22:55:14