使用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
相关产品推荐
相关产品推荐

