TypeScript and errors
The types shipped in the package's .d.ts, and how failures reach JavaScript.
The package ships a .d.ts generated by wasm-bindgen. It declares the TiptapDoc class
and the module functions, plus hand-written types for the plain objects that cross the
boundary.
Shipped types
Import them like any other type:
import type {
Assoc,
BlockInline,
Change,
DiffGranularity,
DiffOptions,
JSONContent,
Mark,
NormalizeOptions,
PosContent,
PosEdit,
PosRange,
Position,
Range,
ResolvedPos,
TextPoint,
Violation,
} from "@tiptap-studio/rusty-parser";They are declared as follows:
export interface Mark { type: string; attrs?: Record<string, unknown>; [k: string]: unknown; }
export interface JSONContent {
type?: string;
attrs?: Record<string, unknown>;
content?: JSONContent[];
marks?: Mark[];
text?: string;
[k: string]: unknown;
}
export interface Position { child: number; offset: number; }
export interface Range { start: Position; end: Position; }
export type Change =
| { op: "setAttr"; path: number[]; key: string; value: unknown }
| { op: "removeAttr"; path: number[]; key: string }
| { op: "setText"; path: number[]; text: string | null }
| { op: "spliceText"; path: number[]; from: number; lenDel: number; insert: string }
| { op: "setMarks"; path: number[]; marks: Mark[] | null }
| { op: "setExtra"; path: number[]; key: string; value: unknown }
| { op: "removeExtra"; path: number[]; key: string }
| { op: "insert"; path: number[]; index: number; node: JSONContent }
| { op: "remove"; path: number[]; index: number }
| { op: "replace"; path: number[]; node: JSONContent }
| { op: "move"; path: number[]; from: number; to: number };
export interface Violation { path: number[]; kind: unknown; child?: number; mark?: number; }
export interface NormalizeOptions {
mergeAdjacentText?: boolean;
removeEmptyText?: boolean;
removeEmptyNodes?: boolean;
}
export interface TextPoint { path: number[]; offset: number; }
export interface ResolvedPos {
pos: number;
depth: number;
path: number[];
parentOffset: number;
index: number;
textOffset: TextPoint | null;
}
export interface PosRange { from: number; to: number; }
export interface BlockInline { block: number[]; inline: Position; }
export type Assoc = "left" | "right";
export type DiffGranularity =
| "block"
| "inline"
| { smart: { replaceThreshold: number } };
export interface DiffOptions { text?: DiffGranularity; }
export type PosContent =
| { type: "text"; text: string; marks?: Mark[] }
| { type: "nodes"; nodes: JSONContent[] };
export type PosEdit =
| { type: "insert"; pos: number; content: PosContent }
| { type: "delete"; from: number; to: number }
| { type: "replace"; from: number; to: number; content: PosContent }
| { type: "addMark"; from: number; to: number; mark: Mark }
| { type: "removeMark"; from: number; to: number; markType: string }
| { type: "setBlockAttrs"; pos: number; attrs: Record<string, unknown> };There are no declared types for the schema object or the toHTMLWith options. Their shapes
are on the Schema and
TiptapDoc pages.
Method signatures use any
wasm-bindgen types every parameter and return value that crosses the boundary as a plain
JS value as any. For example:
export class TiptapDoc {
private constructor();
free(): void;
[Symbol.dispose](): void;
static fromJSON(value: any): TiptapDoc;
toJSON(): any;
addMark(path: any, mark_type: string, attrs: any): boolean;
diffWith(other: TiptapDoc, options: any): any;
// ...
}
export function mapPosition(pos: number, edits: any, assoc: any): number;Primitive parameters and results are typed (string, number, boolean, TiptapDoc),
and childCount returns number | undefined. Everything else is any, so annotate the
values yourself to get checking:
const json: JSONContent = doc.toJSON();
const changes: Change[] = a.diffWith(b, { text: "inline" } satisfies DiffOptions);
const edits: PosEdit[] = [{ type: "delete", from: 1, to: 3 }];
const patch: Change[] = doc.applyPosEdits(edits);
const found: number[][] = doc.pathsByType("heading");Optional parameters are declared required
Parameters that accept undefined, such as attrs on addMark, marks on insertText,
options on diffWith and assoc on mapPosition, are declared without ?. Plain
JavaScript can leave them out. TypeScript reports a missing argument, so pass undefined:
doc.addMark([0, 0], "bold", undefined);
doc.insertText([0], { child: 0, offset: 0 }, "Hi ", undefined);
mapPosition(10, edits, undefined); // same as "left"Errors
Every method and function that can fail throws a standard JS Error. TypeScript does not
declare which ones throw. The message carries the Rust error's text:
| Source | Example message |
|---|---|
| JSON parsing | json error: EOF while parsing an object at line 1 column 1 |
| An argument of the wrong shape | Error: unknown variant `chars`, expected one of `block`, `inline`, `smart` |
| A path-addressed edit on a missing node | no node at path |
| Flat positions | pos: 99 out of range (document size 20) |
| Inline ranges | range: offset 99 out of range in child 0 |
| Block structure | block: no previous sibling to join at [0] |
| Position edits | pos-edit: overlapping edit at [2,4) |
| Change lists | apply: no node at path [9] |
A freed TiptapDoc | null pointer passed to rust |
Messages are meant for people and logs. Do not parse them: catch the error, and decide what to do from the call that failed.
try {
doc.applyPosEdits(edits);
} catch (err) {
console.warn("AI edit rejected:", (err as Error).message);
}Methods that look something up do not throw when it is missing. They return undefined:
nodeAt, firstByType, childCount and removeChild. mapPath returns null when the
node is gone.