Overview
ProseKit provides a set of type assertion utilities that help you check the type of ProseMirror objects at runtime. These are especially useful when working with the editor state and need to determine what kind of object you’re dealing with.
Node Type Checks
isProseMirrorNode()
Checks if a value is a ProseMirror node instance.
function isProseMirrorNode(value: unknown): value is ProseMirrorNode
Parameters
Return Value
Returns true if the value is a ProseMirrorNode instance, false otherwise. When true, TypeScript will narrow the type to ProseMirrorNode.
Example
import { isProseMirrorNode } from 'prosekit/core'
function processContent(content: unknown) {
if (isProseMirrorNode(content)) {
// TypeScript knows content is a ProseMirrorNode here
console.log(content.type.name)
console.log(content.textContent)
}
}
// Usage
processContent(editor.state.doc) // true
processContent({ type: 'doc' }) // false
Mark Type Checks
isMark()
Checks if a value is a Mark instance.
function isMark(value: unknown): value is Mark
Parameters
Return Value
Returns true if the value is a Mark instance, false otherwise.
Example
import { isMark } from 'prosekit/core'
function processMark(mark: unknown) {
if (isMark(mark)) {
// TypeScript knows mark is a Mark here
console.log(mark.type.name)
console.log(mark.attrs)
}
}
// Usage
const marks = editor.state.doc.firstChild?.marks || []
marks.forEach(processMark)
Fragment Type Checks
isFragment()
Checks if a value is a Fragment instance.
function isFragment(value: unknown): value is Fragment
Parameters
Return Value
Returns true if the value is a Fragment instance, false otherwise.
Example
import { isFragment } from 'prosekit/core'
function processContent(content: unknown) {
if (isFragment(content)) {
// TypeScript knows content is a Fragment here
console.log(content.size)
content.forEach(node => console.log(node.type.name))
}
}
// Usage
processContent(editor.state.doc.content) // true
Slice Type Checks
isSlice()
Checks if a value is a Slice instance.
function isSlice(value: unknown): value is Slice
Parameters
Return Value
Returns true if the value is a Slice instance, false otherwise.
Example
import { isSlice } from 'prosekit/core'
function processClipboard(content: unknown) {
if (isSlice(content)) {
// TypeScript knows content is a Slice here
console.log(content.size)
console.log(content.openStart, content.openEnd)
}
}
Selection Type Checks
isSelection()
Checks if a value is a Selection instance.
function isSelection(value: unknown): value is Selection
Parameters
Return Value
Returns true if the value is a Selection instance, false otherwise.
Example
import { isSelection } from 'prosekit/core'
function processSelection(sel: unknown) {
if (isSelection(sel)) {
// TypeScript knows sel is a Selection here
console.log(sel.from, sel.to)
console.log(sel.empty)
}
}
// Usage
processSelection(editor.state.selection) // true
isTextSelection()
Checks if a selection is a TextSelection instance.
function isTextSelection(value: Selection): value is TextSelection
Parameters
The selection to check (must already be a Selection)
Return Value
Returns true if the selection is a TextSelection, false otherwise.
Example
import { isTextSelection } from 'prosekit/core'
const { selection } = editor.state
if (isTextSelection(selection)) {
// TypeScript knows selection is a TextSelection here
console.log(selection.$cursor)
console.log(selection.$anchor, selection.$head)
}
isNodeSelection()
Checks if a selection is a NodeSelection instance.
function isNodeSelection(value: Selection): value is NodeSelection
Parameters
Return Value
Returns true if the selection is a NodeSelection, false otherwise.
Example
import { isNodeSelection } from 'prosekit/core'
const { selection } = editor.state
if (isNodeSelection(selection)) {
// TypeScript knows selection is a NodeSelection here
console.log(selection.node)
console.log(selection.node.type.name)
}
isAllSelection()
Checks if a selection is an AllSelection instance (entire document selected).
function isAllSelection(value: Selection): value is AllSelection
Parameters
Return Value
Returns true if the selection is an AllSelection, false otherwise.
Example
import { isAllSelection } from 'prosekit/core'
const { selection } = editor.state
if (isAllSelection(selection)) {
console.log('Entire document is selected')
}
Common Use Cases
Handling Different Selection Types
import {
isTextSelection,
isNodeSelection,
isAllSelection
} from 'prosekit/core'
function getSelectionInfo(editor) {
const { selection } = editor.state
if (isTextSelection(selection)) {
return {
type: 'text',
from: selection.from,
to: selection.to,
empty: selection.empty
}
}
if (isNodeSelection(selection)) {
return {
type: 'node',
node: selection.node.type.name,
pos: selection.from
}
}
if (isAllSelection(selection)) {
return {
type: 'all',
docSize: editor.state.doc.content.size
}
}
return { type: 'unknown' }
}
Safe Node Processing
import { isProseMirrorNode } from 'prosekit/core'
function safelyProcessNode(value: unknown) {
if (!isProseMirrorNode(value)) {
console.error('Not a valid node')
return
}
// Safe to use node methods
console.log(value.type.name)
console.log(value.textContent)
value.forEach(child => {
console.log(child.type.name)
})
}
Type Guard in Event Handlers
import { isNodeSelection } from 'prosekit/core'
import { defineClickHandler } from 'prosekit/core'
const clickHandler = defineClickHandler((view, pos, event) => {
const { selection } = view.state
if (isNodeSelection(selection)) {
// Handle node selection click
console.log('Clicked on node:', selection.node.type.name)
return true
}
return false
})
Clipboard Data Validation
import { isSlice, isFragment } from 'prosekit/core'
function processClipboardData(data: unknown) {
if (isSlice(data)) {
console.log('Received slice:', data.size)
return data
}
if (isFragment(data)) {
console.log('Received fragment:', data.childCount)
return Slice.maxOpen(data)
}
console.error('Unknown clipboard data type')
return null
}
Mark Validation
import { isMark } from 'prosekit/core'
function validateMarks(marks: unknown[]): Mark[] {
return marks.filter(isMark)
}
function hasMarkType(marks: unknown[], typeName: string): boolean {
return marks.some(mark =>
isMark(mark) && mark.type.name === typeName
)
}
Advanced Patterns
Type-Safe Node Walker
import { isProseMirrorNode } from 'prosekit/core'
function walkNodes(
node: unknown,
callback: (node: ProseMirrorNode, pos: number) => void
) {
if (!isProseMirrorNode(node)) {
return
}
node.descendants((child, pos) => {
callback(child, pos)
})
}
// Usage
walkNodes(editor.state.doc, (node, pos) => {
console.log(`Node at ${pos}:`, node.type.name)
})
Selection Type Router
import {
isTextSelection,
isNodeSelection,
isAllSelection
} from 'prosekit/core'
type SelectionHandler<T> = {
text?: (selection: TextSelection) => T
node?: (selection: NodeSelection) => T
all?: (selection: AllSelection) => T
fallback?: (selection: Selection) => T
}
function handleSelection<T>(
selection: Selection,
handlers: SelectionHandler<T>
): T | undefined {
if (isTextSelection(selection) && handlers.text) {
return handlers.text(selection)
}
if (isNodeSelection(selection) && handlers.node) {
return handlers.node(selection)
}
if (isAllSelection(selection) && handlers.all) {
return handlers.all(selection)
}
return handlers.fallback?.(selection)
}
// Usage
const info = handleSelection(editor.state.selection, {
text: (sel) => `Text: ${sel.from}-${sel.to}`,
node: (sel) => `Node: ${sel.node.type.name}`,
all: () => 'All selected',
fallback: () => 'Unknown selection'
})
Type-Safe Content Validator
import { isProseMirrorNode, isFragment } from 'prosekit/core'
function validateContent(content: unknown): boolean {
if (isProseMirrorNode(content)) {
return content.check() === undefined
}
if (isFragment(content)) {
let valid = true
content.forEach(node => {
if (!isProseMirrorNode(node)) {
valid = false
}
})
return valid
}
return false
}
TypeScript Benefits
These type guards provide excellent TypeScript integration:
function example(value: unknown) {
// value is unknown here
if (isProseMirrorNode(value)) {
// TypeScript automatically narrows the type
value.type // ✓ OK
value.content // ✓ OK
value.attrs // ✓ OK
}
const { selection } = editor.state
// selection is Selection here
if (isTextSelection(selection)) {
// TypeScript knows it's TextSelection
selection.$cursor // ✓ OK (TextSelection specific)
}
}
See Also