|
54 | 54 |
|
55 | 55 | ## Adding New Types |
56 | 56 |
|
57 | | -### Adding a New Block Kit Element |
58 | | - |
59 | | -1. Add the interface to the appropriate file in `src/block-kit/`: |
60 | | - - **Layout blocks** → `blocks.ts` |
61 | | - - **Interactive elements** → `block-elements.ts` |
62 | | - - **Composition objects** → `composition-objects.ts` |
63 | | - - **Message/modal extensions** → `extensions.ts` |
64 | | -2. Add the new type to the relevant discriminated union (e.g., `KnownBlock`, `BlockElement`). |
65 | | -3. Verify it is exported through `src/index.ts` (already covered if the file is re-exported). |
| 57 | +### Adding a New Block Kit Type |
| 58 | + |
| 59 | +Block Kit types live in `src/block-kit/` across four files: |
| 60 | + |
| 61 | +| File | Contents | |
| 62 | +|------|----------| |
| 63 | +| `blocks.ts` | Layout blocks (`SectionBlock`, `ActionsBlock`, `HeaderBlock`, etc.) | |
| 64 | +| `block-elements.ts` | Interactive elements (`Button`, `Datepicker`, `StaticSelect`, etc.) | |
| 65 | +| `composition-objects.ts` | Reusable objects (`PlainTextElement`, `MrkdwnElement`, `Option`, `ConfirmationDialog`, etc.) | |
| 66 | +| `extensions.ts` | Mixins for shared behaviors (`Actionable`, `Confirmable`, `Focusable`, `Placeholdable`, etc.) | |
| 67 | + |
| 68 | +#### Steps |
| 69 | + |
| 70 | +1. **Define the interface** in the appropriate file. Extend the correct base type and set a string literal `type`: |
| 71 | + |
| 72 | + ```typescript |
| 73 | + export interface MyCustomBlock extends Block { |
| 74 | + /** @description The type of block. For this block, `type` is always `my_custom`. */ |
| 75 | + type: 'my_custom'; |
| 76 | + /** @description A required text field. */ |
| 77 | + text: TextObject; |
| 78 | + /** @description An optional field. Maximum length is 300 characters. */ |
| 79 | + optional_field?: string; |
| 80 | + } |
| 81 | + ``` |
| 82 | + |
| 83 | + Key conventions: |
| 84 | + - Extend `Block` for layout blocks. For elements, use mixins from `extensions.ts` (`Actionable`, `Confirmable`, `Focusable`, `Placeholdable`, etc.) instead of duplicating shared fields. |
| 85 | + - The `type` field must be a string literal matching the Slack API type value. |
| 86 | + - Use snake_case for field names to match the Slack JSON API. |
| 87 | + - Include `@description` JSDoc on every field and `@see` with an API reference link on the interface. |
| 88 | + - Mark fields as required or optional based on the Slack API documentation. |
| 89 | + |
| 90 | +2. **Add to the discriminated union.** This is the most commonly missed step: |
| 91 | + - **Layout blocks** → add to the `KnownBlock` union in `blocks.ts` |
| 92 | + - **Elements** → add to the element-specific unions where the element is allowed (e.g., `ActionsBlockElement`, `InputBlockElement`, `SectionBlockElement`) |
| 93 | + - **Composition objects** → add to or create the relevant union type |
| 94 | + |
| 95 | + Forgetting this means the type won't be recognized in typed arrays like `KnownBlock[]`. |
| 96 | + |
| 97 | +3. **Verify barrel export.** If the file is already re-exported from `src/index.ts` (all `block-kit/` files are), no extra step is needed. If you created a new file, add `export * from './<filename>'` to `src/index.ts`. |
| 98 | + |
| 99 | +4. **Add type tests** in `test/blocks.test-d.ts` (or the appropriate test file). Cover: |
| 100 | + - Happy path: `expectAssignable<MyCustomBlock>({ type: 'my_custom', text: { type: 'mrkdwn', text: 'hi' } })` |
| 101 | + - Sad path: `expectError<MyCustomBlock>({})` (missing required fields) |
| 102 | + - Union assignability: `expectAssignable<KnownBlock>({ type: 'my_custom', text: { type: 'mrkdwn', text: 'hi' } })` |
| 103 | + |
| 104 | +5. **Build and test:** |
| 105 | + |
| 106 | + ```bash |
| 107 | + npm run build --workspace=packages/types |
| 108 | + npm run test:types --workspace=packages/types |
| 109 | + ``` |
66 | 110 |
|
67 | 111 | ### Adding a New Event Type |
68 | 112 |
|
|
0 commit comments