Skip to main content

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

value
unknown
required
The value to check

Return Value

result
boolean
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

value
unknown
required
The value to check

Return Value

result
boolean
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

value
unknown
required
The value to check

Return Value

result
boolean
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

value
unknown
required
The value to check

Return Value

result
boolean
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

value
unknown
required
The value to check

Return Value

result
boolean
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

value
Selection
required
The selection to check (must already be a Selection)

Return Value

result
boolean
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

value
Selection
required
The selection to check

Return Value

result
boolean
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

value
Selection
required
The selection to check

Return Value

result
boolean
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