Skip to content

Commit 6230dc7

Browse files
authored
Augmentation-based shape and binding types (tldraw#7091)
continues the work done in tldraw#6739 ### Change type - [ ] `bugfix` - [x] `improvement` - [ ] `feature` - [ ] `api` - [ ] `other` ### Test plan 1. Tested manually in https://github.com/Andarist/tldraw-augmentation-test 2. The most complex `TLIndexedShapes` also tested manually in [TS playground](https://www.typescriptlang.org/play/?ts=6.0.0-dev.20251027#code/JYOwLgpgTgZghgYwgAgCoBkBCcDOEDKAFnAA4QA8qAnmQDTIAKUA9iTgHzIDeAUMsmBoQAXGiEBuPshIs2opqxySAvjx6CyadAEEoLAO5FSKALxbseI2XIAiOHub6b9LsgAmwKKJxgooAObIyuySGigYmMwAHlam5rgExNY2AEbRztzIUaIgAK4AtinQ4shUOQVFUEEh6kJaAML2brHIZhEJsbYITRmuCMwANsxeyD5+IIHBoXUYAOIsuSQtbVgdSRQ2-gskvcjAbt6+AdXTmhgAavsQzMvxluu2AG5XzLtgwPkiyHmFxdIDcCoAVEaUGEDgIBOajCWgAIhB4LkBmBllIAD5aXQGWLo8zRHH8DEYRpQZrrXFzbYE5BE9CXNzXWKSHgQKIkYZgPbgaDwJBaWZDFJwAaxBRsACypG4qhZbI5XMgsEQ4XQAuYQpF6zFOElJG4Um6pPKAwGJQA9GbkKyEANcgzkPpgGBCMh4AMcFQDbkfMx8qJXEKAF4BOCHcaTc2W1mQEBuB1Ol1wZD9fIkAYQSADKjJ71gX2jcn8Z4M5j+gQfL4-SolNOA4HIUHpiElYVgVDRsPHZSRq1RGNxhmI5EF4xSLbMRbG03IC17MAAchwyCT-SgKBw63oTuTEPnnKK7mAODgKXTbjNzEe0D8DJAPBlrPZUE5MIwAElY6yIGTjEuzLx+FnAB1FAnWgOBIGQS9oGXZBchAYBmEhZgYGXXJ-E+bk4wAawgKglwhAcETgJFOQ3YwBCEHAADopFnVBCCPZcTUcJdvQEZguRtO0UAACmGZMkI8d4kOFLNe24+0BOgvQrgASgEQgUEHEjh3Isgl1AKCQBQfxmFowCzToy0P0UlAAANcHM5MAW9FB9BQewUBgYBkWgY4J05Dxj1Pb8Rw0gzkAAbQAaS5KRCWQXCqBQ-lBWFUVZB1UgIppOFiNI2IgpsMIbAAXWXJcwujCBYyXDB4SHFF1my3K8tSgB+GdLVXdd1gItdmP0QECMeOBXJPdNl1jHcQBAZh9xQGSb1vUZOIc5AACtcy4217WdCB8lS-hir7Uq3CXTZthsbbkCakLTtEXb+yXaLYrmeLNWMbVdVOprZ2AVDtxKsrYLyE0jxdPrbRQDa4LwKpiCXbzBr850mPU0G6hwebnNcxU5yg3JOUdZ1vggK8oFO-gHvVBKtSS3VQoKn6Du+JEBjS+DB1Ab9ibO-HCfZq7LuQC7+H4K68qu3sbuQI6Jx2RrmsEzrEY6xyBm6-Dlz6gbfOGuNujGiaGymwmZtKuaHRQZafFWnizK2gWBYAUT7KBEDASh0Eq1TquMFxKLIEXglSkXafK12MuRLKcqEfLpY+r6FwI9wQ7I9ZvfslAdLhziECUhBsL2GPFz1o3pv2W9Tuu-bbrw+7VUexLFFem3+Hey1PsxqGC+Qg2rkhDbITssycx9fJ-OUhFQCdRC7wbgX2nuYxyBC+hSY1WuJVIan2F56PW9wb5daKQvO9mnvkDXPvWSPd4JmT5jgASJcYAEoowAxmFvNrT0p-4e3fCdl23cyzcmQwi+w3jbUQs4+5gwQLmfMiN44uQQiJSeDcZ6JDngvOKZMnpkBemvMKAAyKKldUJL3Js9SmpA8o1G7GoWca4UYDCvHGd8n4ojfliDgYQPALTcMtK+dAH4GRsJ-BpVo+pZw22gYPUQqDOg2CkXmfIuxZwKNgeSCRn8gwhk7BMSQGj+BTF4Q3Ys1wZGrFnskExrwvazgAFS02-LY5AVijGf3LJ8covwoB6MtG49+9ZGzghAD4txzE2wdlGEcXRriBaGP0cuBw+gzFYkcEyGWrUEkGBifwNI2Q8QxHWD2DJuSjGqFnLOWEAB5W2+AABy85UDIGIFeZA9ioiSUcTuUkPAgA) (previous iteration can be found in: [TS playground](https://www.typescriptlang.org/play/?ts=6.0.0-dev.20251027#code/JYOwLgpgTgZghgYwgAgCoBkBCcDOEDKAFnAA4QA8qAnmQDTIAKUA9iTgHzIDeAUMsmBoQAXGiEBuPshIs2opqxySAvjx6CyadAEEoLAO4K2yALzdkAE2BRROMFFABzZKo0oMug0VIozGbHjeZOQA5HB6zPoh9B4RhrIc6kJamMwAHkY4puZpoiAArgC2AEbQ4shUeUWlUC5Jmv7pQb4puATEwSHF6dEp6Zns9e7oAMLhFpnZXMgIzAA2zDbIdg4gzq7JGGNQFs3Z-m3NoQjjvVvjA0NaAGrAFhDMk2bTYMCFIsgFJWXSc3BUTlE3XmEDgIDqbhudweez8WEOHQoIQAbtDmGd0Ld7o8EoMrhgACIQeD5OZgWFaTyRPYAHz6aVpWm2u0RyDpGCxMMRanuCD+UBQsxAdgEcxGi3aPlE+RAwAAjvkUDgqCV5jwIGkSIswAJkuKBUdUJw-GzdWRmDBRfrJZoAPS2mYS5aIrInEAgZg60rIZjI6AOCz3EA8Xn8wXMYU6sBzIkksnNaWyhVKlXdObqzXas0oADyfqg+gcYDgxTmEFjcFJ5MRlGNaFNBTmc1NMvuMFAEAsprcFtFFarzTUoEgsEQw378cRmQAsqRuFJwgZRHnoIXgMXS+XiZXJz5KDo4gNJPwTjtl-m1xuyxPq3vzjsj1JurlkNbDVh+rjj8hUdjz6uixLa9twHGsOTRR9VAzLUoB1YdoHgJAtAAcQWYo4DmZoZznDVIBACwskJEDdzIbCSG4KCNRguDwAQscULQjCsISWdyN4E9xiqJtyntZANT5fJ7mQfR10IZB4DmZUpAQfI7GYQpRGmdCAC8nDgWx7CcFweIdXCIHw4TROQOBHUKEgy0gOYqBmWSwHk50fCkX8HkUgQ3g+L4anKcz-kBZBgTLMFygwsBUFwjTVnWHS+LSPCu1AYzTJIOBXk3YT-h4SjM1g5B4NHJCMFQ5h0MwqcWLned+F4xdIlc-RiEgfMIq05RkF40BZjMlLgDSvThWACMZgFFKICyME+IiWpgEtKhmHyZAZU695wFysBMrUKis0hDAAEl8I1TtmiyZ4eAASHa8EwEIFAAANcBumY-lklB9BesEo2YcTgDJaAfXyHUrBwIDOwcsgcDOgBtABpXLwQAawgWbLUKxjSp8MjjKyGG+oI5AEaRrQb2YxRWLO06AH42odWYBVB0bjNpjD9H+MbkTgb7geMgy3Q9L0UF9f07iDZZPte5AACtbNhgShKuiBCjJ06e0taM31ZHHCPQImypJ0hoYAXUVymocV0R2stdcYrisbPlJOZgBwMS2bmRUBGuha8FqYgskB4Guyuh26ezEXhJQdsfqmnU5p1ESrs+CB80V06UeKpidbYViDat-TccbZs6VbYkOwsJPKZABPoCT0QTdO07q-1s3qadR2fDGxm5mZqhWfZv40rBLsec9fz+fzAMgzO7HYpzrJ8d7aZoYAJlhvHEd7Ii41vUjyvI3Bs17VWJT2DXCeIzeIDIxf9eQY2l7yCuoAbk+N+JjO9ahher9UWvKYAUViqBEBgH3NrHw9AXhCGri4QYtdRBVQdNNN2KZVTNkDnYb6zY5YCnoJbd4YIshyw9r9b2nwh4CwLEWPCq1Fa8RujDAAZCvAm68dxnzIg9TSCA4a5SyM9Ls11aZ2T4sKfIAjCCB2KAAkACAxIRissZEg5lgD00EQQtsLCg5uBwLQahDpPT8JEngGyclCg+nLj7Iu5cuz2wRsgG6JBQCKWUA9fQc05hdmSjgfBYjXTXU4YrZWfZT5HynvhTWRUSovxwJnehjC15ayCenKJpBDa1wpvwKmHstI3QOIEGsUN6B-xlhQcJad0bbwNvQPOLZ9rtksd2IQ+8YwJJ8OwdgzjXFdhcVALh-CIBJ14sUf6AhPqFDgDYyEgMfJd2QIUJ0QoEAOwgPQMWz1EGOj0KNLU+EtJtmLsHDCwA2jg1SadP+9hAHAOaXQcwbhIHKGgTA9JvFakYSssslAw1pFrJksYumWRen0FUQ7P2fzQ7t2GhYay4cRwg2jv5ayBC7o4AenySseAk45JtBQfJyAAByEZcV22BvuEpaMt66xIAbVp60eC8QFDgeYfouy7X2mkQ6LphC0ttFy4OLL7hspZK3KYPL0lGLsgpVouS9wACIfniulWAjJcr7ItzICK0V6SVJqWamsSQvFRX3L1Q6UVzlmCiExUcaVpqFXmF4gAKhxp2O1P40Tqo1W5d4VRvhQCNe69JUy-IBVBCAX1frjJkjCrFHVjhQ3pMNW6hmS5KRxGaNFGmKAar6ATc+c1H4GSIjTU6Z8PLVC8V4gSHMP98C4pCKgZAxA-TIAdWkIpFhnWngsEAA)) ### Description The main goal of this PR is to allow users to provide users to strictly type all their custom shapes' props. Previously people would have to resort to using an unknown shape, casting, or "casting" through generics (where tldraw types would just accept their type arguments happily). With the new system in place users can augment a builtin tldraw interface like this: ```ts const CIRCLE_CLIP_TYPE = 'circle-clip' declare module 'tldraw' { export interface TLGlobalShapePropsMap { [CIRCLE_CLIP_TYPE]: { w: number h: number } } } export type CircleClipShape = TLShape<typeof CIRCLE_CLIP_TYPE> ``` Once this is done, users can now use the shape type like this: ```ts editor.updateShape({ id: shapeId, type: 'circle-clip', props: { w: 100, h: 100, }, }) ``` Thanks to the power of discriminated unions, this will now error if the user tries to update the shape with the wrong props. That said, for the time-being, I kept generic parameters in the editor methods to: - maintain backwards compatibility - keep this documented pattern working: https://tldraw.dev/docs/shapes#Meta-information ⚠️ this allows users to bypass the created augmentation system and locally call methods with shapes having *more* properties than prescribed by the augmentation system. Given the TS structural type system... this is something that can always happen when arguments are not passed inline so it's largely fine as the users should start using the new system anyway. This should allow them to do it gradually though. This PR consists of: - the mentioned augmentation system. Related changes are mostly local to [`TLShape` file](https://github.com/tldraw/tldraw/pull/7091/files#diff-74a7534c2bdfff2f6ef9e068496c69dca2a7b09d05c1ff482485fd30480694d3) - using the new system in the [examples](https://github.com/tldraw/tldraw/pull/7091/files#diff-5f23c6b955fcdcf8a402446d96178e12241ca0cb01af4e73535cc1ca261f81f0), [templates](https://github.com/tldraw/tldraw/pull/7091/files#diff-5845112b17d3eb903a41e90e834858e5b8ab71b6350348811d183137824e4f07) and tests - updating the docs to reflect the new system [here](https://github.com/tldraw/tldraw/pull/7091/files#diff-1b0ad2aab7ced6fc26f40f9929d2d16269b8fa505bdf4b18b5b746facc5bcefc) and in the "guides" in the examples - followup code changes required by the new system, they can be found all over the place. - the same kind of changes related to the bindings (this PR includes the work done in tldraw#7133 ) ### API changes Numerous typings changes! make @ds300 compile a list <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Introduces module-augmentation-driven typing for custom shapes and bindings, updates core editor APIs and utilities to support it, and migrates docs, examples, templates, and tests accordingly. > > - **Types & Schema**: > - Add `TLGlobalShapePropsMap`/`TLGlobalBindingPropsMap`, `TLIndexedShapes`/`TLIndexedBindings`, `ExtractShapeByProps`, and `TLCreateShapePartial`. > - Refactor `TLBaseShape`/`TLBaseBinding` and shape/binding record validators to work with augmented types. > - **Editor API**: > - Overload `getShapeUtil`, `isShapeOfType`, and `getBindings*` for typed shape/binding keys. > - Update `createShape(s)`/`updateShape(s)` generics and accept typed partials. > - Tighten types across selection, snapping, export, reparenting, etc. > - **Bindings**: > - Generic `BindingUtil`/callbacks now use typed `TLBinding`; connection/arrow binding utilities updated. > - **Docs & Guides**: > - Rewrite shape/binding guides to use module augmentation (`TLShape<typeof TYPE>`), update API docs. > - **Examples & Templates**: > - Migrate all examples and app templates to declare module augmentations and use typed shapes; remove legacy generic casts. > - **Tests**: > - Update tests to new typing model and API overloads; add tests for type narrowing and create/update generics. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit ac324a6. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 574dfa0 commit 6230dc7

219 files changed

Lines changed: 2434 additions & 1612 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/docs/content/docs/editor.mdx

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -476,19 +476,29 @@ To create a binding of your own, you can define a custom binding.
476476

477477
#### The binding type
478478

479-
First, you need to create a type that describes what the binding object will look like:
479+
First, we register the binding's props using TypeScript module augmentation:
480480

481481
```ts
482-
import { TLBaseBinding } from 'tldraw'
482+
const STICKER_TYPE = 'sticker'
483483

484-
type StickerBinding = TLBaseBinding<'sticker', { x: number; y: number }>
484+
declare module 'tldraw' {
485+
export interface TLGlobalBindingPropsMap {
486+
[STICKER_TYPE]: { x: number; y: number }
487+
}
488+
}
489+
```
490+
491+
Then, we can extract the binding type using `TLBinding`:
492+
493+
```ts
494+
import { TLBinding } from 'tldraw'
495+
496+
type StickerBinding = TLBinding<typeof STICKER_TYPE>
485497
```
486498
487-
With [TLBaseBinding](?) we define the binding's type (`sticker`) and `props` property (`{x: number,
488-
y: number}`). The type can be any string, and the props must be a plain JSON object (ie not a class
489-
instance).
499+
The type can be any string but the props must be a regular [JSON-serializable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#description) JavaScript object.
490500
491-
The [TLBaseBinding](?) helper adds the other base properties like `id`, `toId`, and `fromId`.
501+
The created binding types extend [TLBaseBinding](?) and thus include all the base properties of a binding, such as `id`, `toId`, and `fromId`.
492502
493503
#### The `BindingUtil` class
494504

apps/docs/content/docs/shapes.mdx

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,15 +95,29 @@ You can create your own custom shapes. In the examples below, we will create a c
9595

9696
In tldraw's data model, each shape is represented by a JSON object. Let's first create a type that describes what this object will look like.
9797

98+
First, we register the shape's props using TypeScript module augmentation:
99+
98100
```ts
99-
import { TLBaseShape } from 'tldraw'
101+
const CARD_TYPE = 'card'
100102

101-
type CardShape = TLBaseShape<'card', { w: number; h: number }>
103+
declare module 'tldraw' {
104+
export interface TLGlobalShapePropsMap {
105+
[CARD_TYPE]: { w: number; h: number }
106+
}
107+
}
102108
```
103109

104-
With the [TLBaseShape](?) helper, we define the shape's `type` property (`card`) and the shape's `props` property (`{ w: number, h: number }`). The type can be any string but the props must be a regular [JSON-serializable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#description) JavaScript object.
110+
Then, we can extract the shape type using `TLShape`:
105111

106-
The [TLBaseShape](?) helper adds the other base properties of a shape, such as `x`, `y`, `rotation`, and `opacity`.
112+
```ts
113+
import { TLShape } from 'tldraw'
114+
115+
type CardShape = TLShape<typeof CARD_TYPE>
116+
```
117+
118+
The type can be any string but the props must be a regular [JSON-serializable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#description) JavaScript object.
119+
120+
The created shape types extend [TLBaseShape](?) and thus include all the base properties of a shape, such as `x`, `y`, `rotation`, and `opacity`.
107121
108122
### Shape Util
109123
@@ -112,10 +126,12 @@ While tldraw's shapes themselves are simple JSON objects, we use [ShapeUtil](?)
112126
Let's create a [ShapeUtil](?) class for the shape.
113127
114128
```tsx
115-
import { HTMLContainer, ShapeUtil } from 'tldraw'
129+
import { HTMLContainer, Rectangle2d, ShapeUtil } from 'tldraw'
130+
131+
// ...
116132

117133
class CardShapeUtil extends ShapeUtil<CardShape> {
118-
static override type = 'card' as const
134+
static override type = CARD_TYPE
119135

120136
getDefaultProps(): CardShape['props'] {
121137
return {

apps/dotcom/client/src/fairy/actions/LabelActionUtil.ts

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,22 @@
11
import { LabelAction, Streaming } from '@tldraw/fairy-shared'
2-
import { TLShapeId, toRichText } from 'tldraw'
2+
import { ExtractShapeByProps, TLRichText, TLShape, TLShapeId, toRichText } from 'tldraw'
33
import { AgentHelpers } from '../fairy-agent/agent/AgentHelpers'
44
import { AgentActionUtil } from './AgentActionUtil'
55

6+
type ShapeWithRichText = ExtractShapeByProps<{ richText: TLRichText }>
7+
8+
function isShapeWithRichText(shape: TLShape | null | undefined): shape is ShapeWithRichText {
9+
return !!(shape && 'richText' in shape.props)
10+
}
11+
12+
function assertShapeWithRichText(
13+
shape: TLShape | null | undefined
14+
): asserts shape is ShapeWithRichText {
15+
if (!isShapeWithRichText(shape)) {
16+
throw new Error('Shape is not a valid ShapeWithRichText')
17+
}
18+
}
19+
620
export class LabelActionUtil extends AgentActionUtil<LabelAction> {
721
static override type = 'label' as const
822

@@ -17,8 +31,17 @@ export class LabelActionUtil extends AgentActionUtil<LabelAction> {
1731
if (!action.complete) return action
1832

1933
const shapeId = helpers.ensureShapeIdExists(action.shapeId)
20-
if (!shapeId) return null
21-
34+
if (!shapeId || !this.agent?.editor) {
35+
return null
36+
}
37+
const shape = this.agent.editor.getShape(`shape:${shapeId}` as TLShapeId)
38+
if (!shape) {
39+
return null
40+
}
41+
if (!isShapeWithRichText(shape)) {
42+
console.warn(`Shape type "${shape.type}" does not support richText labels`)
43+
return null
44+
}
2245
action.shapeId = shapeId
2346
return action
2447
}
@@ -30,11 +53,7 @@ export class LabelActionUtil extends AgentActionUtil<LabelAction> {
3053

3154
const shapeId = `shape:${action.shapeId}` as TLShapeId
3255
const shape = editor.getShape(shapeId)
33-
if (!shape) return
34-
if (!('richText' in shape.props)) {
35-
console.warn(`Shape type "${shape.type}" does not support richText labels`)
36-
return
37-
}
56+
assertShapeWithRichText(shape)
3857

3958
editor.updateShape({
4059
id: shapeId,

apps/dotcom/client/src/fairy/actions/PenActionUtil.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { PenAction, Streaming, asColor, convertFocusFillToTldrawFill } from '@tldraw/fairy-shared'
2-
import { TLDrawShape, TLDrawShapeSegment, Vec, VecModel, createShapeId, last } from 'tldraw'
2+
import { TLDrawShapeSegment, Vec, VecModel, createShapeId, last } from 'tldraw'
33
import { AgentHelpers } from '../fairy-agent/agent/AgentHelpers'
44
import { AgentActionUtil } from './AgentActionUtil'
55

@@ -77,7 +77,7 @@ export class PenActionUtil extends AgentActionUtil<PenAction> {
7777
},
7878
]
7979

80-
this.agent.editor.createShape<TLDrawShape>({
80+
this.agent.editor.createShape({
8181
id: createShapeId(),
8282
type: 'draw',
8383
x: minX,

apps/dotcom/client/src/tla/components/TlaEditor/useExtraToolDragIcons.ts

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,5 @@
11
import { useMemo } from 'react'
2-
import {
3-
DefaultSizeStyle,
4-
onDragFromToolbarToCreateShape,
5-
TLDrawShape,
6-
TLUiOverrides,
7-
VecModel,
8-
} from 'tldraw'
2+
import { DefaultSizeStyle, onDragFromToolbarToCreateShape, TLUiOverrides, VecModel } from 'tldraw'
93
import { trackEvent } from '../../../utils/analytics'
104

115
export function useExtraDragIconOverrides() {
@@ -16,7 +10,7 @@ export function useExtraDragIconOverrides() {
1610
onDragFromToolbarToCreateShape(editor, info, {
1711
createShape: (id) => {
1812
const sizeStyle = editor.getStyleForNextShape(DefaultSizeStyle)
19-
return editor.createShape<TLDrawShape>({
13+
return editor.createShape({
2014
id,
2115
type: 'draw',
2216
props: {
@@ -39,7 +33,7 @@ export function useExtraDragIconOverrides() {
3933
onDragFromToolbarToCreateShape(editor, info, {
4034
createShape: (id) => {
4135
const sizeStyle = editor.getStyleForNextShape(DefaultSizeStyle)
42-
return editor.createShape<TLDrawShape>({
36+
return editor.createShape({
4337
id,
4438
type: 'draw',
4539
props: {
@@ -62,7 +56,7 @@ export function useExtraDragIconOverrides() {
6256
onDragFromToolbarToCreateShape(editor, info, {
6357
createShape: (id) => {
6458
const sizeStyle = editor.getStyleForNextShape(DefaultSizeStyle)
65-
return editor.createShape<TLDrawShape>({
59+
return editor.createShape({
6660
id,
6761
type: 'draw',
6862
props: {

apps/examples/e2e/tests/export-snapshots.spec.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import {
1313
TLAsset,
1414
TLBindingCreate,
1515
TLShapePartial,
16-
TLTextShape,
1716
degreesToRadians,
1817
mapObjectMapValues,
1918
mockUniqueId,
@@ -1007,7 +1006,7 @@ test.describe('Export snapshots', () => {
10071006
.deleteShapes(editor.getSelectedShapeIds())
10081007

10091008
const titleId = tldrawApi.createShapeId()
1010-
editor.createShape<TLTextShape>({
1009+
editor.createShape({
10111010
id: titleId,
10121011
type: 'text',
10131012
x: 0,
@@ -1019,7 +1018,7 @@ test.describe('Export snapshots', () => {
10191018

10201019
for (const [rowName, testCases] of Object.entries(snapshot)) {
10211020
const rowTitleId = tldrawApi.createShapeId()
1022-
editor.createShape<TLTextShape>({
1021+
editor.createShape({
10231022
id: rowTitleId,
10241023
type: 'text',
10251024
x: 0,
@@ -1034,7 +1033,7 @@ test.describe('Export snapshots', () => {
10341033
testCases
10351034
)) {
10361035
const testCaseTitleId = tldrawApi.createShapeId()
1037-
editor.createShape<TLTextShape>({
1036+
editor.createShape({
10381037
id: testCaseTitleId,
10391038
type: 'text',
10401039
x,

apps/examples/e2e/tests/test-text.spec.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ test.describe('text measurement', () => {
253253
test('for auto-font-sizing shapes, should do normal font size for text that does not have long words', async () => {
254254
const shape = await page.evaluate(() => {
255255
const id = 'shape:testShape' as TLShapeId
256-
editor.createShapes<TLNoteShape>([
256+
editor.createShapes([
257257
{
258258
id,
259259
type: 'note',
@@ -275,7 +275,7 @@ test.describe('text measurement', () => {
275275
test('for auto-font-sizing shapes, should auto-size text that have slightly long words', async () => {
276276
const shape = await page.evaluate(() => {
277277
const id = 'shape:testShape' as TLShapeId
278-
editor.createShapes<TLNoteShape>([
278+
editor.createShapes([
279279
{
280280
id,
281281
type: 'note',
@@ -297,7 +297,7 @@ test.describe('text measurement', () => {
297297
test('for auto-font-sizing shapes, should auto-size text that have long words', async () => {
298298
const shape = await page.evaluate(() => {
299299
const id = 'shape:testShape' as TLShapeId
300-
editor.createShapes<TLNoteShape>([
300+
editor.createShapes([
301301
{
302302
id,
303303
type: 'note',
@@ -319,7 +319,7 @@ test.describe('text measurement', () => {
319319
test('for auto-font-sizing shapes, should wrap text that has words that are way too long', async () => {
320320
const shape = await page.evaluate(() => {
321321
const id = 'shape:testShape' as TLShapeId
322-
editor.createShapes<TLNoteShape>([
322+
editor.createShapes([
323323
{
324324
id,
325325
type: 'note',
@@ -369,7 +369,7 @@ test.describe('text measurement', () => {
369369

370370
await page.evaluate(() => {
371371
const id = 'shape:testShape' as TLShapeId
372-
editor.createShapes<TLNoteShape>([
372+
editor.createShapes([
373373
{
374374
id,
375375
type: 'note',

apps/examples/src/examples/add-tool-to-toolbar/sticker-tool-util.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { StateNode, TLTextShape, toRichText } from 'tldraw'
1+
import { StateNode, toRichText } from 'tldraw'
22

33
// Check out the custom tool example for a more detailed explanation of the tool class.
44

@@ -12,7 +12,7 @@ export class StickerTool extends StateNode {
1212

1313
override onPointerDown() {
1414
const { currentPagePoint } = this.editor.inputs
15-
this.editor.createShape<TLTextShape>({
15+
this.editor.createShape({
1616
type: 'text',
1717
x: currentPagePoint.x - OFFSET,
1818
y: currentPagePoint.y - OFFSET,

apps/examples/src/examples/after-create-update-shape/AfterCreateUpdateShapeExample.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { Editor, TLShape, TLShapeId, Tldraw, createShapeId, toRichText } from 'tldraw'
22

3+
type ShapeWithColor = Extract<TLShape, { props: { color: string } }>
4+
35
// this function takes a shape ID, and if that shape is red, sets all other red shapes on the same
46
// page to black.
57
function ensureOnlyOneRedShape(editor: Editor, shapeId: TLShapeId) {
@@ -13,7 +15,10 @@ function ensureOnlyOneRedShape(editor: Editor, shapeId: TLShapeId) {
1315
// find any other red shapes on the same page:
1416
const otherRedShapesOnPage = Array.from(editor.getPageShapeIds(pageId))
1517
.map((id) => editor.getShape(id)!)
16-
.filter((otherShape) => otherShape.id !== shape.id && isRedShape(otherShape))
18+
.filter(
19+
(otherShape): otherShape is ShapeWithColor =>
20+
otherShape.id !== shape.id && isRedShape(otherShape)
21+
)
1722

1823
// set the color of all those shapes to black:
1924
editor.updateShapes(

apps/examples/src/examples/after-delete-shape/AfterDeleteShapeExample.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ function createDemoShapes(editor: Editor) {
5151
},
5252
...[50, 180, 310].map((x) => ({
5353
id: createShapeId(),
54-
type: 'geo',
54+
type: 'geo' as const,
5555
parentId: frameId,
5656
x,
5757
y: 120,

0 commit comments

Comments
 (0)