Skip to content

Commit 01dbb85

Browse files
tidy
1 parent 66bc1b5 commit 01dbb85

File tree

1 file changed

+27
-21
lines changed

1 file changed

+27
-21
lines changed

blog/2025-02-14-typescript-sdk-release.md

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ This release is a big step forward, significantly improving the type safety of o
1111
- Lexicon derived interfaces now have an explicitly defined `$type` property, allowing to properly discriminate unions.
1212
- Lexicon derived `is*` utility methods no longer unsafely type cast their input.
1313
- Lexicon derived `validate*` utility methods now return a more precise type.
14-
- New Lexicon derived `isValid*` utility methods are now available.
1514

1615
## Context
1716

@@ -153,18 +152,21 @@ Notice how the `$type` property is defined as optional (`?:`) here. This is due
153152
```typescript
154153
export interface Main {
155154
$type?: 'app.bsky.embed.recordWithMedia'
156-
record: AppBskyEmbedRecord.Main // Also used in post's `embed` property
155+
record: AppBskyEmbedRecord.Main
157156
media: /* omitted */
158157
}
159158
```
160159

161160
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:
162161

163162
```typescript
164-
const recordWithMedia: RecordWithMedia = {
163+
const recordWithMedia: $Typed<RecordWithMedia> = {
164+
// $type is required here because of the $Typed<> utility
165165
$type: 'app.bsky.embed.recordWithMedia',
166+
166167
record: {
167-
// $type is not needed here
168+
// $type is not needed here, as there is no ambiguity
169+
168170
record: {
169171
/* omitted */
170172
},
@@ -218,6 +220,10 @@ if (embed?.$type === 'app.bsky.embed.images') {
218220
}
219221
```
220222

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+
221227
### `is*` utility methods
222228

223229
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 {
241247

242248
That implementation of the discriminator is invalid.
243249

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)).
245251
- Second because the `isMain` function does not actually check the structure of the object, only its `$type` property.
246252

247253
This invalid behavior could yield runtime errors that could otherwise have been avoided during development:
@@ -316,7 +322,7 @@ if (isValidImages(embed)) {
316322

317323
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.
318324

319-
## `validate*` utility methods
325+
### `validate*` utility methods
320326

321327
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:
322328

@@ -340,6 +346,21 @@ if (result.success) {
340346
}
341347
```
342348

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.
352+
353+
```typescript
354+
import { AppBskyEmbedImages, asPredicate } from '@atproto/api'
355+
356+
const isValidImage = asPredicate(AppBskyEmbedImages.validateMain)
357+
358+
declare const someArray: unknown[]
359+
360+
// This will be typed as `AppBskyEmbedImages.Main[]`
361+
const images = someArray.filter(isValidImage)
362+
```
363+
343364
## Removal of the `[x: string]` index signature
344365

345366
Another property of Atproto being an "open protocol" is the fact that objects are allowed to contain additional &mdash; unspecified &mdash; 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:
@@ -388,21 +409,6 @@ const embed: AppBskyEmbedVideo.Main = {
388409
}
389410
```
390411

391-
## New `asPredicate` function
392-
393-
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.
394-
395-
```typescript
396-
import { AppBskyEmbedImages, asPredicate } from '@atproto/api'
397-
398-
const isValidImage = asPredicate(AppBskyEmbedImages.validateMain)
399-
400-
declare const someArray: unknown[]
401-
402-
// This will be typed as `AppBskyEmbedImages.Main[]`
403-
const images = someArray.filter(isValidImage)
404-
```
405-
406412
## Other considerations
407413

408414
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

Comments
 (0)