diff --git a/packages/@sanity/types/src/portableText/asserters.ts b/packages/@sanity/types/src/portableText/asserters.ts index 0bce91b7fce..3a18ef77186 100644 --- a/packages/@sanity/types/src/portableText/asserters.ts +++ b/packages/@sanity/types/src/portableText/asserters.ts @@ -1,26 +1,70 @@ -import type {Block, Span} from './types' +import type {PortableTextTextBlock, PortableTextSpan, PortableTextObject} from './types' function isRecord(value: unknown): value is Record { return !!value && (typeof value == 'object' || typeof value == 'function') } -/** @internal */ -export function isBlock(value: unknown): value is Block { +/** + * Assert that a given object is a portable-text text-block type object + * + * @remarks + * * The `markDefs` and `style` property of a block is optional. + * * Block types can be named, so expect anything of the _type property. + * + * @alpha + */ +export function isPortableTextTextBlock( + value: unknown +): value is PortableTextTextBlock { return ( isRecord(value) && typeof value._type === 'string' && // block types can be named, so expect anything here. - typeof value.style === 'string' && Array.isArray(value.children) && - Array.isArray(value.markDefs) + value.children.every((child) => isRecord(child)) && + ('markDefs' in value // optional property + ? Array.isArray(value.markDefs) && value.markDefs.every((def) => isRecord(def)) + : false) && + ('style' in value ? typeof value.style === 'string' : true) // optional property ) } -/** @internal */ -export function isSpan(value: unknown): value is Span { +/** + * Assert that a given object is a portable-text span-type object + * + * @remarks + * The `marks` property of a block is optional. + * + * @alpha + */ +export function isPortableTextSpan(value: unknown): value is PortableTextSpan { return ( isRecord(value) && value._type === 'span' && typeof value.text === 'string' && - Array.isArray(value.marks) + ('marks' in value // optional property + ? Array.isArray(value.marks) && value.marks.every((mark) => typeof mark === 'string') + : true) + ) +} + +/** + * Assert that a given object is a portable-text list-text-block-type object + * + * @remarks + * Uses `isPortableTextTextBlock` and checks for `listItem` and `level` + * + * @see isPortableTextTextBlock + * + * @alpha + */ +export function isPortableTextListBlock( + value: unknown +): value is PortableTextTextBlock { + return ( + isPortableTextTextBlock(value) && + 'listItem' in value && + typeof value.listItem === 'string' && + 'level' in value && + Number.isInteger(value.level) ) } diff --git a/packages/@sanity/types/src/portableText/types.ts b/packages/@sanity/types/src/portableText/types.ts index 07dcd28316c..0fb4a01ef08 100644 --- a/packages/@sanity/types/src/portableText/types.ts +++ b/packages/@sanity/types/src/portableText/types.ts @@ -1,23 +1,38 @@ -/** @public */ -export interface Block { +/** @alpha */ + +export type PortableTextBlock = PortableTextTextBlock | PortableTextObject + +/** @alpha */ +export interface PortableTextTextBlock { _type: string _key: string - style: string children: TChild[] - markDefs: MarkDefinition[] + markDefs?: PortableTextObject[] + listItem?: string + style?: string + level?: number } -/** @public */ -export interface Span { - _type: 'span' +/** @alpha */ +export interface PortableTextObject { + _type: string _key: string - marks: string[] - text: string + [other: string]: unknown } -/** @public */ -export interface MarkDefinition { - [key: string]: unknown - _type: string +/** @alpha */ +export interface PortableTextSpan { _key: string + _type: 'span' + text: string + marks?: string[] +} + +/** @alpha */ +export type PortableTextChild = PortableTextObject | PortableTextSpan + +/** @alpha */ +export interface PortableTextListBlock extends PortableTextTextBlock { + listItem: string + level: number }