You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: blog/2025-02-14-typescript-sdk-release.md
+27-21Lines changed: 27 additions & 21 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -11,7 +11,6 @@ This release is a big step forward, significantly improving the type safety of o
11
11
- Lexicon derived interfaces now have an explicitly defined `$type` property, allowing to properly discriminate unions.
12
12
- Lexicon derived `is*` utility methods no longer unsafely type cast their input.
13
13
- Lexicon derived `validate*` utility methods now return a more precise type.
14
-
- New Lexicon derived `isValid*` utility methods are now available.
15
14
16
15
## Context
17
16
@@ -153,18 +152,21 @@ Notice how the `$type` property is defined as optional (`?:`) here. This is due
153
152
```typescript
154
153
exportinterfaceMain {
155
154
$type?:'app.bsky.embed.recordWithMedia'
156
-
record:AppBskyEmbedRecord.Main// Also used in post's `embed` property
155
+
record:AppBskyEmbedRecord.Main
157
156
media:/* omitted */
158
157
}
159
158
```
160
159
161
160
Since there is no ambiguity as to the type of the data here, making the `$type` property required would cause unnecessary bloat. Making the `$type` property optional allows declaring a "Record With Media" as follows:
162
161
163
162
```typescript
164
-
const recordWithMedia:RecordWithMedia= {
163
+
const recordWithMedia:$Typed<RecordWithMedia> = {
164
+
// $type is required here because of the $Typed<> utility
165
165
$type: 'app.bsky.embed.recordWithMedia',
166
+
166
167
record: {
167
-
// $type is not needed here
168
+
// $type is not needed here, as there is no ambiguity
169
+
168
170
record: {
169
171
/* omitted */
170
172
},
@@ -218,6 +220,10 @@ if (embed?.$type === 'app.bsky.embed.images') {
218
220
}
219
221
```
220
222
223
+
### `$type` property in `record` definitions
224
+
225
+
While optional in interfaces generated from Lexicon `object` definitions, the `$type` property is **required** in interfaces generated from Lexicon `record` definitions.
226
+
221
227
### `is*` utility methods
222
228
223
229
The example above shows how data can be discriminated based on the `$type` property. The SDK provides utility methods to perform this kind of discrimination. These methods are named `is*` and are generated from the lexicons. For example, the `app.bsky.embed.images` Lexicon used to generate the following `isMain` utility method:
@@ -241,7 +247,7 @@ export function isMain(value: unknown): values is Main {
241
247
242
248
That implementation of the discriminator is invalid.
243
249
244
-
- Fist because a `$type` is not allowed to end with `#main` (as per atproto specification).
250
+
- Fist because a `$type` is not allowed to end with `#main` ([as per AT Protocol specification](https://atproto.com/specs/lexicon#lexicon-files)).
245
251
- Second because the `isMain` function does not actually check the structure of the object, only its `$type` property.
246
252
247
253
This invalid behavior could yield runtime errors that could otherwise have been avoided during development:
@@ -316,7 +322,7 @@ if (isValidImages(embed)) {
316
322
317
323
These methods perform data validation, making them somewhat slower than the `is*` utility methods. They can, however, be used in place of the `is*` utilities when migrating to this new version of the SDK.
318
324
319
-
## `validate*` utility methods
325
+
###`validate*` utility methods
320
326
321
327
As part of this update, the signature of the `validate*` utility methods was updated to properly describe the type of the `value` in case of success:
322
328
@@ -340,6 +346,21 @@ if (result.success) {
340
346
}
341
347
```
342
348
349
+
### New `asPredicate` function
350
+
351
+
The SDK exposes a new `asPredicate` function. This function allows to convert a `validate*` function into a predicate function. This can be useful when working with libraries that expect a predicate function to be passed as an argument.
// This will be typed as `AppBskyEmbedImages.Main[]`
361
+
const images =someArray.filter(isValidImage)
362
+
```
363
+
343
364
## Removal of the `[x: string]` index signature
344
365
345
366
Another property of Atproto being an "open protocol" is the fact that objects are allowed to contain additional — unspecified — properties (although this should be done with caution to avoid incompatibility with properties that are added in the future). This used to be represented in the type system using a `[k: string]: unknown` index signature in generated interfaces. This is how the video embed used to be represented:
The SDK exposes a new `asPredicate` function. This function allows to convert a `validate*` function into a predicate function. This can be useful when working with libraries that expect a predicate function to be passed as an argument.
// This will be typed as `AppBskyEmbedImages.Main[]`
403
-
const images =someArray.filter(isValidImage)
404
-
```
405
-
406
412
## Other considerations
407
413
408
414
When upgrading, please make sure that your project does not depend on multiple versions of the `@atproto/*` packages. Use [resolutions](https://classic.yarnpkg.com/en/docs/selective-version-resolutions/) or [overrides](https://docs.npmjs.com/cli/v9/configuring-npm/package-json#overrides) in your `package.json` to pin the dependencies to the same version.
0 commit comments