Skip to main content

Typescript

note

The core types will be the default of Slate in the near future, see this PR.

See our full Typescript example here.

Plate is providing a typed layer on top of Slate, using mostly generic types.

Differences with Slate types:

  • Editor type: TEditor<V> where V represents the "value" being edited by Slate. In the most generic editor, V would be equivalent to TElement[] (since that is what is accepted as children of the editor). But in a custom editor, you might have TEditor<Array<Paragraph | Quote>>.
  • Other TEditor-and-TNode-related methods have been also made generic, so for example if you use getLeafNode(editor, path) it knows that the return value is a TText node. But more specifically, it knows that it is the text node of the type you've defined in your custom elements (with any marks you've defined). TEditor type is not matching with Slate Editor type, so Plate has forked (type-only) all Slate methods that use Editor that you should use.
  • This replaces the declaration merging approach, and provides some benefits. One of the drawbacks to declaration merging was that it was impossible to know whether you were dealing with an "unknown" or "known" element, since the underlying type was changed. Similarly, having two editors on the page with different schemas wasn't possible to represent. Hopefully this approach with generics will be able to smoothly replace the declaration merging approach. (While being easy to migrate to, since you can pass those same custom element definitions into TEditor still.)

The following example is defining all the types in plateTypes.ts:

  • MyValue type, which will be the type of our editor.children
  • MyValue is the most important type as most of the core types are derived from it
  • The types following MyValue are optional but highly recommended, writing generic types are redundant so try doing it only once
  • A few typed functions are also provided
import React, { useState } from 'react';
import { Plate } from '@udecode/plate';
import { MyEditor, MyValue } from './typescript/plateTypes';

export default () => {
  // eslint-disable-next-line @typescript-eslint/no-use-before-define
  const [value, setValue] = useState(initialValue);

  return (
    // Because of the TypeScript-awareness you'll also get an error if you
    // initialize the editor with an invalid value or other invalid props.
    <Plate<MyValue, MyEditor> value={value} onChange={(v) => setValue(v)} />
  );
};

// Slate is TypeScript-aware, so if you try to use any unrecognizes `type`
// properties in this initial value you will get a compiler error.
const initialValue: MyValue = [
  {
    type: 'p',
    children: [
      {
        text: 'All the Slate examples are written in TypeScript. However, ',
      },
      { text: 'most', italic: true },
      { text: ' of them use ' },
      { text: 'implicit', bold: true },
      {
        text:
          " typings in many places because it makes it easier to see the actual Slate-specific code—especially for people who don't know TypeScript.",
      },
    ],
  },
  {
    type: 'p',
    children: [
      { text: 'This example is written with ' },
      { text: 'explicit', bold: true },
      {
        text:
          ' typings in all places, so you can see what a more realistic TypeScript usage would look like.',
      },
    ],
  },
  {
    type: 'h1',
    children: [{ text: 'Quotes' }],
  },
  {
    type: 'p',
    children: [{ text: "We'll throw in a few things like quotes…" }],
  },
  {
    type: 'blockquote',
    children: [{ text: 'A wise quote.' }],
  },
  {
    type: 'h1',
    children: [{ text: 'Images' }],
  },
  {
    type: 'p',
    children: [{ text: 'And images…' }],
  },
  {
    type: 'img',
    url: 'https://source.unsplash.com/kFrdX5IeQzI',
    children: [{ text: '' }],
  },
];

Naming convention:

  • T...Element is used because ...Element is already used by Plate UI components and we don't want naming conflicts between imports. T... is also a way to differenciate Plate types with Slate types.
  • My is for the library consumer types. It's short and explicit. You could replace it by your project name.