Custom Widget type not assignable to CmsFieldBase - Typescript
- Dominant language
- JavaScript
- Stars
- 19.4k
- Forks
- 3.1k
- Avg merge
- 1d 14h
- Merged PRs (30d)
- 9
Description
Hello, team. Thank you for your hard work on this package.
Following the docs, I created a custom widget: `videoEmbed`.
But when I try to add it as a field in one of my collections of files, of course it's not an acceptable widget type according to `netlify-cms-core`'s shipped typings.
**To Reproduce**
1. Create custom widget & register with the CMS
2. Create a `config.ts` file which specifies the `CmsConfig` object which is passed into `CMS.init(config)`
3. In the CmsConfig object, add a File Collection with a single file with a single file/page.
4. In that file's `fields` , add the custom widget (in my case `videoEmbed`)
5. Notice the Typescript error (screenshots)

## TLDR: Expected Behavior
I would expect a typescript-safe way to specify custom widgets in a typescript config file. Maybe there is?
Is there a "nice" and sanctioned way to add type support for custom widgets in `cmsConfig.ts`?
## Workaround 1
Add `as unknown as CmsField` after each custom widget field like so:
```typescript
{
label: 'Video',
name: 'videoEmbed',
widget: 'videoEmbed',
} as unknown as CmsField,
```
This "works" as you can see in the screenshot:

### Downside
The more I use custom widgets on more and more pages, the more I have to repeat the use of `as unknown as CmsField`. This doesn't feel right.
Of course, I could just create a single const and use that instead but I _still_ don't think this is the right way to go:
```typescript
const videoEmbedCmsField = {
label: 'Video',
name: 'videoEmbed',
widget: 'videoEmbed',
} as unknown as CmsField;
// ...
{
label: 'Intro',
name: 'intro',
widget: 'markdown',
},
videoEmbedCmsField,
{
label: 'Show Gallery?',
name: 'showGallery',
widget: 'boolean',
},
// ...
```
## Workaround 2
The other approach is to modify the `CmsConfig` type _aaaaaaall_ the way until I reach the `field: CmsField` type so that I can add `{ widget: 'videoEmbed' }` as an acceptable widget type.
This **feels** like the right way to do it could it have some unintended side-effects since I need to adjust the init function to make it happy `CMS.init({ config: cmsConfig as CmsConfig });`?
This screenshot shows how I got it to work (with a helper type `Modify = Omit & R`).

This all works and it _**feels**_ like the right approach. Is it?

## Workaround 3
Modify `type CmsField` in `netlify-cms-core/index.d.ts` to include the new widget type:
```typescript index.d.ts
export interface CmsFieldVideoEmbed {
widget: 'videoEmbed';
default?: string;
}
export type CmsField = CmsFieldBase &
(
| CmsFieldBoolean
| CmsFieldCode
| CmsFieldColor
| CmsFieldDateTime
| CmsFieldFileOrImage
| CmsFieldList
| CmsFieldMap
| CmsFieldMarkdown
| CmsFieldNumber
| CmsFieldObject
| CmsFieldRelation
| CmsFieldSelect
| CmsFieldHidden
| CmsFieldStringOrText
| CmsFieldMeta
| CmsFieldVideoEmbed // <-- my custom widget type
);
```
Obviously, this is not the right way to do it **but** it allows my project to run locally. 🎉
# Conclusion
Is there a proper way to do this? Thank you for your time.
### Applicable Versions
- netlify-cms-app 2.15.72
- netlify-cms-core 2.55.2
- Git provider: GitHub, BitBucket
- OS: macOS Monterey 12.5
- Browser version Chrome 103
- Node.JS version: v16.10.0
## CMS configuration
```typescript
{
backend: {
name: 'git-gateway',
branch: 'main',
commit_messages: {
create: 'docs: create {{collection}} “{{slug}}”',
update: 'docs: update {{collection}} “{{slug}}”',
delete: 'docs: delete {{collection}} “{{slug}}”',
uploadMedia: 'docs: upload “{{path}}”',
deleteMedia: 'docs: delete “{{path}}”',
openAuthoring: 'docs: {{message}}',
},
},
local_backend: true,
media_folder: '/public/uploads',
public_folder: '/uploads',
collections: [
{
label: 'Pages',
name: 'pages',
files: [
{
label: 'Home',
name: 'home',
file: 'content/home.md',
fields: [
{
label: 'Title',
name: 'title',
widget: 'string',
},
{
label: 'Intro',
name: 'intro',
widget: 'markdown',
},
{
label: 'Show Gallery?',
name: 'showGallery',
widget: 'boolean',
},
{
label: 'Video',
name: 'videoEmbed',
widget: 'videoEmbed', // <-- This is the issue here
},
{
label: 'Features',
name: 'features',
widget: 'list',
fields: [
{ label: 'Title', name: 'title', widget: 'string' },
{
label: 'Content',
name: 'content',
widget: 'markdown',
modes: ['rich_text', 'raw'],
},
],
},
{
label: 'Gallery Images',
name: 'gallery',
widget: 'list',
label_singular: 'Image',
add_to_top: true,
fields: [{ label: 'Image', name: 'image', widget: 'image' }],
},
],
},
],
},
],
}
```
Contributor guide
Assessment
This issue has not been assessed yet.