diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.test.ts b/packages/frontend-plugin-api/src/wiring/createExtension.test.ts index 14b1fd2d50..104f03c84d 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.test.ts @@ -49,4 +49,42 @@ describe('createExtension', () => { }); expect(extension.id).toBe('test'); }); + + it('should create an extension with a some optional output', () => { + const extension = createExtension({ + id: 'test', + at: 'root', + output: { + foo: stringData, + bar: stringData.optional(), + }, + factory({ bind }) { + bind({ + foo: 'bar', + }); + bind({ + foo: 'bar', + bar: 'baz', + }); + bind({ + // @ts-expect-error + foo: 3, + }); + bind({ + foo: 'bar', + // @ts-expect-error + bar: 3, + }); + // @ts-expect-error + bind({ bar: 'bar' }); + // @ts-expect-error + bind({}); + // @ts-expect-error + bind(); + // @ts-expect-error + bind('bar'); + }, + }); + expect(extension.id).toBe('test'); + }); }); diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.ts b/packages/frontend-plugin-api/src/wiring/createExtension.ts index 1ef0d3c272..c545fe7fea 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.ts @@ -18,10 +18,26 @@ import { PortableSchema } from '../schema'; import { BackstagePlugin } from './createPlugin'; import { AnyExtensionDataMap, Extension } from './types'; +type OnlyRequiredKeys = { + [K in keyof TOutput]: TOutput[K]['config']['optional'] extends true + ? never + : K; +}[keyof TOutput]; + +type OnlyOptionalKeys = { + [K in keyof TOutput]: TOutput[K]['config']['optional'] extends false + ? never + : K; +}[keyof TOutput]; + /** @public */ -export type ExtensionDataBind = (outputs: { - [K in keyof TOutput]: TOutput[K]['T']; -}) => void; +export type ExtensionDataBind = ( + outputs: { + [K in OnlyRequiredKeys]: TOutput[K]['T']; + } & { + [K in OnlyOptionalKeys]?: TOutput[K]['T']; + }, +) => void; /** @public */ export type ExtensionDataValue = { diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionDataRef.ts b/packages/frontend-plugin-api/src/wiring/createExtensionDataRef.ts index 10a931a65d..95e8bdeb95 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionDataRef.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionDataRef.ts @@ -15,14 +15,35 @@ */ /** @public */ -export type ExtensionDataRef = { +export type ExtensionDataRef< + TData, + TConfig extends { optional: boolean } = { optional: false }, +> = { id: string; - T: T; + T: TData; + config: TConfig; $$type: 'extension-data'; }; /** @public */ -// TODO: change to options object with ID. -export function createExtensionDataRef(id: string): ExtensionDataRef { - return { id, $$type: 'extension-data' } as ExtensionDataRef; +export interface ConfigurableExtensionDataRef< + TData, + TConfig extends { optional: boolean }, +> extends ExtensionDataRef { + optional(): ConfigurableExtensionDataRef; +} + +/** @public */ +// TODO: change to options object with ID. +export function createExtensionDataRef( + id: string, +): ConfigurableExtensionDataRef { + return { + id, + $$type: 'extension-data', + config: { optional: false }, + optional() { + return { ...this, config: { ...this.config, optional: true } }; + }, + } as ConfigurableExtensionDataRef; } diff --git a/packages/frontend-plugin-api/src/wiring/types.ts b/packages/frontend-plugin-api/src/wiring/types.ts index 3779a5c676..f3f5008f27 100644 --- a/packages/frontend-plugin-api/src/wiring/types.ts +++ b/packages/frontend-plugin-api/src/wiring/types.ts @@ -20,7 +20,7 @@ import { ExtensionDataRef } from './createExtensionDataRef'; import { BackstagePlugin } from './createPlugin'; /** @public */ -export type AnyExtensionDataMap = Record>; +export type AnyExtensionDataMap = Record>; /** @public */ export interface Extension {