notion-types 完全指南:Notion 核心数据结构的 TypeScript 类型定义深度解析
notion-types 完全指南:Notion 核心数据结构的 TypeScript 类型定义深度解析
notion-types是 react-notion-x 生态中负责描述 Notion 内部数据结构的类型包,只导出 TypeScript 类型、不包含任何运行时代码。本文将带你掌握它的安装方式、统一入口,并逐模块拆解ID、Decoration、Block、Collection、CollectionView、Formula、RecordMap等核心类型的结构与设计意图,最后结合仓库内notion-client、notion-utils、react-notion-x等包的真实消费方式,理解这些类型如何贯穿整个 Notion 渲染链路。
这个包解决什么问题
Notion 的前端页面数据并非普通 JSON,而是一套高度嵌套、带双重重包裹(double-nested)的"记录映射"结构:页面由块(block)构成,块通过 content 数组引用子块 ID,数据库(collection)通过 schema 描述列,视图(collection view)描述同一数据在不同形态(表格、看板、画廊、列表)下的呈现,富文本由 Decoration 数组描述样式。任何想要在 React 中准确还原 Notion 页面的工具,都必须先精确建模这套结构。
notion-types(packages/notion-types)正是为此而生:它的官方描述是 "TypeScript types for core Notion data structures"(Notion 核心数据结构的 TypeScript 类型)。它被设计为:
- 纯类型包:
This package only exports types,零运行时负担,sideEffects: false,可与任何打包器配合做摇树优化; - 全环境兼容:同时适用于 Node.js 与浏览器(在仓库 packages 总览 中被标注为 Universal 环境);
- 生态基石:
notion-client(抓取数据)、notion-utils(数据处理)、react-notion-x(渲染)、notion-compat(官方 API 兼容层)、notion-x-to-md(转 Markdown)全部以它作为类型基础。
安装与使用
在你的项目中安装:
npm install notion-types
它只导出类型,因此既可以用于服务端(如 notion-client 抓取页面后的数据建模),也可以安全地用于浏览器端(如 react-notion-x 渲染组件内部)。
统一命名空间导入即可访问全部类型:
import * as notion from 'notion-types'
包入口 src/index.ts 通过 8 个 export * 聚合了全部模块:
export * from './api'
export * from './block'
export * from './collection'
export * from './collection-view'
export * from './core'
export * from './formula'
export * from './maps'
export * from './user'
如果你只想按需导入,也可以使用具名导入,例如:
import { type Block, type Decoration, type ExtendedRecordMap } from 'notion-types'
从 package.json 可以看到,当前版本为 8.0.8,采用 ESM 与 types: ./src/index.ts 直接指向源码,构建产物由 tsdown 生成(tsdown.config.ts 中配置了 target: es2018、format: ['esm']、dts: true 并输出 build 目录)。注意其 engines 要求 node >= 18。
核心基础类型(core.ts)
core.ts 是整个包的"地基",其他所有模块都从这里导入基础类型。
ID 与 PropertyID
/** UUID */
export type ID = string
export type PropertyID = string
ID 是所有实体的统一标识符,本质是 UUID 字符串。PropertyID 用于标识数据库(collection)的列:它要么是类似 o;Os 的 4 字符哈希,要么是保留的特殊值 title。源码注释明确指出,可以像传统关系型数据库的主键一样理解 title 属性——它保证存在、是集合的"主索引"。
Color(Notion 支持的块颜色)
export type Color =
| 'gray' | 'brown' | 'orange' | 'yellow' | 'teal'
| 'blue' | 'purple' | 'pink' | 'red'
| 'gray_background' | 'brown_background' | ...
前 9 个是文本色,后 9 个是对应的 *_background 背景色变体,与 Notion 编辑器中的调色板一一对应。它被用于块文本颜色、调用块(callout)背景、表格列颜色等场景。
PropertyType(数据库列类型)
export type PropertyType =
| 'title' | 'text' | 'number' | 'select' | 'status'
| 'multi_select' | 'auto_increment_id' | 'date' | 'person'
| 'file' | 'checkbox' | 'url' | 'email' | 'phone_number'
| 'formula' | 'relation'
| 'created_time' | 'created_by'
| 'last_edited_time' | 'last_edited_by'
涵盖 Notion 数据库的全部 20 种属性类型。注意 status 与 person 是较新加入的;auto_increment_id、created_*、last_edited_* 属于系统自动维护的列。
NumberFormat(数字格式)
NumberFormat 定义了超过 40 种数字显示格式,包括通用格式(number_with_commas、percent、number)与各币种格式(dollar、euro、pound、yen、yuan、ruble 等)。这些值直接对应 Notion 属性配置中的"数字格式"选项。
Decoration(富文本装饰结构)
Decoration 是理解整个包的关键概念,它是 Notion 富文本的"元组编码"格式:每个文本片段由基础装饰和一组子装饰组成。
export type BoldFormat = ['b']
export type ItalicFormat = ['i']
export type StrikeFormat = ['s']
export type CodeFormat = ['c']
export type UnderlineFormat = ['_']
export type LinkFormat = ['a', string]
export type ColorFormat = ['h', Color]
export type UserFormat = ['u', string]
export type PageFormat = ['p', string]
export type InlineEquationFormat = ['e', string]
export type DateFormat = ['d', FormattedDate]
export type LinkMentionFormat = ['lm', string]
export type CustomEmojiFormat = ['ce', string]
// ...
export type BaseDecoration = [string]
export type AdditionalDecoration = [string, SubDecoration[]]
export type Decoration = BaseDecoration | AdditionalDecoration
每个格式都是一个元组:['b'] 表示加粗,['a', 'https://...'] 表示带目标的链接,['h', Color] 表示颜色,['e', latex] 表示内联公式,['d', FormattedDate] 表示内联日期(含 type: 'date' | 'daterange' | 'datetime' | 'datetimerange' 及 start_date、end_date、time_zone 等字段)。Decoration 本身是"基础文本 + 子装饰数组"或"仅有基础文本"的联合,Notion 页面上的每个文本块、标题、按钮文字都由 Decoration[] 描述。
这套元组结构在生态中被大量消费:notion-utils 的 get-text-content.ts 用它提取纯文本,notion-x-to-md 的 text.ts 与 property.ts 将它转换为 Markdown 的粗体/斜体/链接语法。
Block 类型体系(block.ts)
block.ts 定义了块类型枚举与每种块的结构化接口,是整个包体量最大的模块。
BlockType 枚举
export type BlockType =
| 'page' | 'text' | 'bookmark' | 'bulleted_list' | 'numbered_list'
| 'header' | 'sub_header' | 'sub_sub_header' | 'header_4'
| 'quote' | 'equation' | 'to_do' | 'table_of_contents' | 'divider'
| 'column_list' | 'column' | 'callout' | 'toggle'
| 'image' | 'embed' | 'gist' | 'video' | 'figma' | 'typeform'
| 'replit' | 'codepen' | 'excalidraw' | 'tweet' | 'maps' | 'pdf'
| 'audio' | 'drive' | 'file' | 'code'
| 'collection_view' | 'collection_view_page'
| 'transclusion_container' | 'transclusion_reference' | 'alias'
| 'table' | 'table_row' | 'external_object_instance'
| 'breadcrumb' | 'button' | 'miro' | 'tab'
50 余种块类型,与仓库 readme.md 的 Supported Blocks 表格基本一一对应:transclusion_container / transclusion_reference 对应 Notion 的同步块(Sync Block),alias 对应页面链接别名,external_object_instance 对应外部对象实例,tab、button、miro、breadcrumb 是最新加入的块类型。
BaseBlock:所有块的公共基座
export interface BaseBlock {
id: ID
type: BlockType
properties?: any
format?: any
content?: ID[] // 子块 ID 数组,构成树形结构
space_id?: ID
parent_id: ID // 父级 ID
parent_table: 'space' | 'block' | 'table' | 'collection' | (string & {})
version: number
created_time: number
last_edited_time: number
alive: boolean
created_by_table: string
created_by_id: ID
last_edited_by_table: string
last_edited_by_id: ID
}
parent_table 允许块挂在 space、block、table、collection 之下,parent_id 对应其 ID;alive 标记块是否被删除。这个字段组合在 notion-client 中扮演重要角色——例如 notion-api.ts 的 fetchRelationPages 正是靠 parent_table === 'collection' 来识别数据库中的记录行并递归抓取关联页面。
三大派生基座
- BaseTextBlock:文本类块(
text、各标题、列表、引用、公式块等),properties.title为Decoration[],format.block_color为Color,且允许偶发携带子块content; - BaseContentBlock:媒体/嵌入类块(
image、video、embed、tweet、pdf、drive等),properties含source(资源地址)、caption、alt_text,format含block_alignment(center | left | right)、block_width、block_height、display_source、block_full_width、block_page_width、block_aspect_ratio、block_preserve_scale等布局参数,并带file_ids用于签名 URL 关联; - BasePageBlock:页面类块,
format含page_full_width、page_small_text、page_cover_position、page_cover、page_icon、block_locked等,并带permissions: { role: Role; type: string }[]权限数组(Role定义在 core.ts 中,为'editor' | 'reader' | 'none' | 'read_and_write')。
代表性具体块
- TodoBlock:
properties.title+properties.checked: (['Yes'] | ['No'])[],用字符串元组表达勾选状态; - CodeBlock:
properties.title、language、caption三个Decoration[]; - TableBlock / TableRowBlock:
TableBlock通过collection_id、view_ids、format.collection_pointer指向一个隐藏数据库,format.table_block_column_order定义列顺序、table_block_column_header/table_block_row_header控制表头;TableRowBlock的properties是{ [column: string]: Decoration[] }的动态列映射; - SyncPointerBlock(
transclusion_reference):format.copied_from_pointer与format.transclusion_reference_pointer记录同步源; - PageLink(
alias):format.alias_pointer.id指向被引用的页面; - ButtonBlock:
format.automation_id关联自动化动作,format.block_color控制颜色,是 Notion 按钮块(可打开 URL、触发 webhook)的类型建模; - GoogleDriveBlock:
format.drive_status(authed、last_fetched)与format.drive_properties(url、file_id、thumbnail、user_name、modified_time等)完整还原 Drive 嵌入卡片。
Block 联合类型将所有具体块收拢为单一可判别联合(discriminated union),react-notion-x 的渲染核心 block.tsx 正是用 switch (block.type) 对 BlockType 的每个分支分发渲染,从 collection_view_page、page 到 text、image、code、to_do、transclusion_reference 等逐一处理。
数据库与属性(collection.ts)
collection.ts 建模 Notion 数据库(Database):
export interface SelectOption {
id: ID
color: Color
value: string
}
export interface CollectionPropertySchema {
name: string
type: PropertyType
options?: SelectOption[] // select / multi_select 的候选项
number_format?: NumberFormat
formula?: Formula // formula 列的公式定义
}
export interface CollectionPropertySchemaMap {
[key: string]: CollectionPropertySchema
}
export interface Collection {
id: ID
version: number
name: Decoration[]
schema: CollectionPropertySchemaMap // 列名哈希 -> 列定义
icon: string
parent_id: ID
parent_table: string
alive: boolean
copied_from: string
template_pages?: Array<ID>
format?: {
collection_page_properties?: Array<{ property: PropertyID; visible: boolean }>
property_visibility?: Array<{ property: PropertyID; visibility: 'show' | 'hide' }>
hide_linked_collection_name?: boolean
}
}
schema 是"列哈希 → 列定义"的映射,键正是 core.ts 中的 PropertyID。format.collection_page_properties 控制数据库页面中列的显示顺序与可见性,property_visibility 控制在关联数据库视图中列的显隐。
在 notion-client 中,Collection 用于识别记录行的父级并解析关联属性——notion-api.ts 的 fetchRelationPages 通过 recordMap.collection[blockValue.parent_id] 拿到集合后,遍历 collection.schema 提取 relation 类型的列,再递归抓取关联页面块。
数据库视图(collection-view.ts)
collection-view.ts 描述 Notion 数据库的"视图"——同一份数据的不同展示形态:
export type CollectionViewType =
| 'table' | 'gallery' | 'list' | 'board' | 'calendar' | 'reducer'
- TableCollectionView:
format.table_properties数组(每列含property、visible、width、wrap),page_sort定义行排序; - GalleryCollectionView:
format.gallery_cover(类型见下)、gallery_cover_size(small | medium | large)、gallery_cover_aspect(cover | contain)、gallery_properties; - BoardCollectionView:看板视图,
board_cover/board_cover_size/board_cover_aspect,board_groups2与board_columns描述分组列(含property、hidden、value: { type: PropertyType; value: string }); - ListCollectionView:
list_properties控制列表视图显示哪些列; - CalendarCollectionView:日历视图(源码标注 TODO,
react-notion-x的 Supported Blocks 表格中同样标注为❌ Missing,从源码结构看属于尚未完整建模/渲染的类型); - ReducerCollectionView:
reducerResults.collection_group_results承载分组聚合结果。
封面类型由 CollectionCardCover 表达:
export type CollectionCardCoverType =
| 'page_cover' | 'page_content' | 'page_content_first'
| 'property' | 'none' | 'file'
export interface CollectionCardCover {
type: CollectionCardCoverType
property?: PropertyID // type 为 'property' 时指向具体列
}
这些类型直接驱动 react-notion-x 的 collection-card.tsx 等第三方组件对数据库卡片的渲染。
公式类型(formula.ts)
formula.ts 完整建模 Notion 公式的抽象语法树:
- FormulaType:
constant | property | operator | function | symbol; - FormulaValueType:
string | number | boolean | date及常量类型'e' | 'false' | 'true' | 'pi'; - FormulaResultType:
text | number | boolean | date | checkbox,即公式求值结果的展示类别; - FormulaOperatorType:算术运算符(
-、*、%、/、+)与比较运算符(!=、<=、==、>、<、>=); - FormulaFunctionType:约 60 个内置函数,分四组:
- 逻辑:
and、or、not、if、empty、equal、unequal、larger、smaller等; - 数值:
abs、add、ceil、floor、round、sqrt、pow、mod、log10、log2、exp、max、min等; - 文本:
concat、contains、format、join、length、replace、replaceAll、slice、test; - 日期时间:
now、dateAdd、dateBetween、dateSubtract、formatDate、fromTimestamp、year、month、day、hour、minute、start、end、timestamp。
- 逻辑:
每种公式节点都有对应接口:ConstantFormula(含 value 与 value_type)、PropertyFormula(通过 id: PropertyID 与 name 引用列)、SymbolFormula、FunctionFormula(name + args: Array<Formula>)、OperatorFormula(operator + name + args)。Formula 即这五者的递归联合。
这套 AST 有两个实际求值器:notion-x-to-md 的 eval-formula.ts 在转 Markdown 时求值公式属性,react-notion-x 的 third-party/eval-formula.ts 在渲染属性时求值,二者都直接消费 FormulaFunctionType 的分支。
用户类型(user.ts)
user.ts 建模 Notion 用户:
export interface User {
id: ID
version: number
email: string
given_name: string
family_name: string
profile_photo: string
onboarding_completed: boolean
mobile_onboarding_completed: boolean
}
用户数据在 RecordMap.notion_user 中按 ID 索引,供 person 属性、created_by / last_edited_by 以及 @提及(UserFormat)等场景解析显示名与头像。
聚合映射与 RecordMap(maps.ts)
maps.ts 是整个包"灵魂"所在——它定义 Notion 页面数据的最终形态。
NotionMap 与双重嵌套
export type NotionMapBox<T> =
| { role: Role; value: T }
| {
// some blocks are double-nested...
role: Role
value: { role: Role; value: T }
}
export interface NotionMap<T> {
[key: string]: NotionMapBox<T>
}
export type BlockMap = NotionMap<Block>
export type UserMap = NotionMap<User>
export type CollectionMap = NotionMap<Collection>
export type CollectionViewMap = NotionMap<CollectionView>
NotionMapBox<T> 的注释 "some blocks are double-nested" 记录了 Notion API 的一个真实坑:部分块的 value 内部还嵌套一层 { role, value }。这正是 notion-utils 中 get-block-value.ts 存在的原因——它递归"拆箱"(unbox),直到拿到真正的 Block 值,且源码注释明确关联到 issue #682。所有上层代码都应通过 getBlockValue 访问块数据,而不是直接假定 value 是一层。
RecordMap 与 ExtendedRecordMap
export interface RecordMap {
block: BlockMap
collection?: CollectionMap
collection_view?: CollectionViewMap
notion_user?: UserMap
}
export interface ExtendedRecordMap extends RecordMap {
collection: CollectionMap
collection_view: CollectionViewMap
notion_user: UserMap
collection_query: {
[collectionId: string]: {
[collectionViewId: string]: CollectionQueryResult
}
}
signed_urls: {
[blockIdOrAssetUrl: string]: string
}
preview_images?: PreviewImageMap
custom_emojis?: CustomEmojiMap
}
源码注释特别强调:ExtendedRecordMap 并非 Notion 原生类型,而是 react-notion-x 为方便使用而扩展的便捷类型。它在 RecordMap 基础上补齐了四个附加映射:
collection_query:集合视图的查询结果缓存,CollectionQueryResult内含total、blockIds、aggregationResults(按PropertyType分组的聚合)、看板视图专用的groupResults、分页游标collection_group_results(blockIds+hasMore)等;signed_urls:原始资源 URL → 签名 URL 的映射(键兼容块 ID 与资源 URL,保证向后兼容),是 PDF、文件、音频等受保护资源渲染的关键;preview_images:PreviewImageMap,url → { originalWidth, originalHeight, dataURIBase64 },支撑 LQIP 低清占位图;custom_emojis:CustomEmojiMap,emojiId → url,支撑自定义表情。
RecordMap 与 ExtendedRecordMap 是生态的"数据契约":
notion-client的 notion-api.ts 中getPage()返回Promise<notion.ExtendedRecordMap>,并在抓取后主动补齐recordMap.collection、recordMap.collection_view、recordMap.notion_user、collection_query、signed_urls等映射;notion-utils的 merge-record-maps.ts 对两份ExtendedRecordMap按block、collection、collection_view、notion_user、collection_query、signed_urls、preview_images七个键逐一合并,用于增量抓取场景;react-notion-x的 renderer.tsx 与 context.tsx 直接以recordMap: ExtendedRecordMap作为NotionRenderer的 props 与NotionContext的核心字段,渲染时从recordMap.block取出根块;notion-compat的 notion-compat-api.ts 作为官方 API 的兼容层,其getPage()同样返回notion.ExtendedRecordMap,说明该契约同时覆盖了官方与私有两套 API 数据源。
其余辅助类型
- PageChunk:
recordMap+cursor.stack,对应分页抓取的原始 chunk 结构; - CollectionInstance:
recordMap+result: CollectionQueryResult,一个集合实例的快照; - PageMap:
{ [pageId: string]: ExtendedRecordMap | null },多页面站点(如博客列表)的页面缓存容器; - PropertyMap:
{ [key: string]: Decoration[] },块的properties的通用形态。
API 请求/响应类型(api.ts)
api.ts 描述 notion-client 与 Notion 交互时用到的请求/响应形态:
export interface SearchParams {
ancestorId: string
query: string
filters?: {
isDeletedOnly: boolean
excludeTemplates: boolean
isNavigableOnly: boolean
requireEditPermissions: boolean
}
limit?: number
searchSessionId?: string
}
export interface SearchResults {
recordMap: RecordMap
results: SearchResult[]
total: number
}
export interface SearchResult {
id: string
isNavigable: boolean
score: number
highlight: { pathText: string; text: string }
}
export interface APIError {
errorId: string
name: string
message: string
}
export interface RecordValues<T> {
results: T[]
}
SearchParams / SearchResults / SearchResult 直接对应 notion-client 的搜索接口与 react-notion-x 内置的搜索弹窗(search-dialog.tsx);react-notion-x 的 types.ts 中 SearchNotionFn = (params: types.SearchParams) => Promise<types.SearchResults> 的签名正是从这里来的。
生态中的真实消费链路
把上述类型串联起来,可以看到一条完整的数据流:notion-client 通过 getPage() 返回 ExtendedRecordMap → notion-utils 用 getBlockValue() 拆箱、getPageContentBlockIds() 枚举内容块、mergeRecordMaps() 合并增量数据 → react-notion-x 的 NotionRenderer 接收 ExtendedRecordMap,按 BlockType 分发到各块组件,数据库场景再由 CollectionViewType 分发到 table/gallery/board/list 等第三方视图组件 → notion-x-to-md 消费 Decoration、Formula、Collection 生成 Markdown;notion-compat 则将官方 API 数据转换为同一套 ExtendedRecordMap 契约。整条链路上,类型定义只此一份,这正是 notion-types 作为生态基石的体现。
小结
notion-types 虽然只导出类型,却是整个 react-notion-x 生态最底层、最核心的"数据字典":core.ts 定义了 ID、颜色、属性类型与富文本 Decoration 元组结构;block.ts 用判别联合精确建模 50 余种块;collection.ts / collection-view.ts 建模数据库与其多形态视图;formula.ts 提供公式 AST;maps.ts 给出 RecordMap / ExtendedRecordMap 这一贯穿抓取、处理、渲染全链路的数据契约。无论你是要在自己的 Next.js 站点中渲染 Notion 页面、把 Notion 转成 Markdown 供 LLM 使用,还是想深度理解 Notion 内部数据结构,从 notion-types 入手都是最高效的路径——安装一行命令,收益是整个 Notion 数据模型的完整类型化视图。
许可证
notion-types 采用 MIT 协议(见 package.json 与仓库根 license),作者为 Travis Fischer,可自由用于商业与非商业项目。