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>
whereV
represents the "value" being edited by Slate. In the most generic editor,V
would be equivalent toTElement[]
(since that is what is accepted as children of the editor). But in a custom editor, you might haveTEditor<Array<Paragraph | Quote>>
.- Other
TEditor
-and-TNode
-related methods have been also made generic, so for example if you usegetLeafNode(editor, path)
it knows that the return value is aTText
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 SlateEditor
type, so Plate has forked (type-only) all Slate methods that useEditor
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 oureditor.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.