chore: started initial work on implementing interal vs external props

Signed-off-by: benjdlambert <ben@blam.sh>
This commit is contained in:
benjdlambert
2025-07-25 17:44:30 +02:00
parent abe4d879f8
commit 10ad6cde05
2 changed files with 124 additions and 7 deletions
@@ -22,4 +22,59 @@ describe('createComponentRef', () => {
expect(ref.id).toBe('foo');
expect(String(ref)).toBe('ComponentRef{id=foo}');
});
it('should allow defining a default component implementation', () => {
const Test = () => <div>test</div>;
createComponentRef<{ foo: string }, { bar: string }>({
id: 'foo',
mode: 'sync',
defaultComponent: ({ bar }) => <Test key={bar} />,
});
createComponentRef<{ foo: string }, { bar: string }>({
id: 'foo',
mode: 'async',
defaultComponent: async () => <Test />,
});
// @ts-expect-error - this should be an error as mode is sync
createComponentRef<{ foo: string }, { bar: string }>({
id: 'foo',
mode: 'sync',
defaultComponent: async ({ bar }) => <Test key={bar} />,
});
// todo: why do we have two errors here?
// @ts-expect-error - this should be an error as mode is async
createComponentRef<{ foo: string }, { bar: string }>({
id: 'foo',
mode: 'async',
defaultComponent: ({ bar }) => <Test key={bar} />,
});
// todo: why does this not work?
createComponentRef<{ foo: string }, { bar: string }>({
id: 'foo',
// @ts-expect-error - this should be an error as default mode is async
defaultComponent: ({ bar }) => <Test key={bar} />,
});
expect(Test).toBeDefined();
});
it('should allow transformings props', () => {
createComponentRef<{ foo: string }, { bar: string }>({
id: 'foo',
transformProps: props => ({ foo: props.bar }),
});
createComponentRef<{ foo: string }, { bar: string }>({
id: 'foo',
// @ts-expect-error - this should be an error as foo is not a string
transformProps: props => ({ foo: 1 }),
});
expect(true).toBe(true);
});
});
@@ -15,20 +15,82 @@
*/
/** @public */
export type ComponentRef<T extends {} = {}> = {
export type ComponentRef<
TInnerComponentProps,
TExternalComponentProps,
TMode extends 'sync' | 'async' = 'async',
> = {
id: string;
T: T;
mode: TMode;
transformProps?: (props: TExternalComponentProps) => TInnerComponentProps;
defaultComponent?: TMode extends 'async'
? (props: TExternalComponentProps) => Promise<JSX.Element>
: (props: TExternalComponentProps) => JSX.Element;
};
/** @public */
export function createComponentRef<T extends {} = {}>(options: {
export interface ComponentRefOptions<
TInnerComponentProps,
TExternalComponentProps,
TMode extends 'sync' | 'async' = 'async',
> {
id: string;
}): ComponentRef<T> {
const { id } = options;
mode?: TMode;
defaultComponent?: TMode extends 'async'
? (props: TExternalComponentProps) => Promise<JSX.Element>
: (props: TExternalComponentProps) => JSX.Element;
transformProps?: (props: TExternalComponentProps) => TInnerComponentProps;
}
/**
* Creates a new component ref that is synchronous.
* @public
*/
export function createComponentRef<
TInnerComponentProps,
TExternalComponentProps,
>(
options: ComponentRefOptions<
TInnerComponentProps,
TExternalComponentProps,
'sync'
>,
): ComponentRef<TInnerComponentProps, TExternalComponentProps, 'sync'>;
/**
* Creates a new component ref that is asynchronous.
* @public
*/
export function createComponentRef<
TInnerComponentProps,
TExternalComponentProps,
>(
options: ComponentRefOptions<
TInnerComponentProps,
TExternalComponentProps,
'async'
>,
): ComponentRef<TInnerComponentProps, TExternalComponentProps, 'async'>;
export function createComponentRef<
TInnerComponentProps,
TExternalComponentProps,
TMode extends 'sync' | 'async',
>(
options: ComponentRefOptions<
TInnerComponentProps,
TExternalComponentProps,
TMode
>,
): ComponentRef<TInnerComponentProps, TExternalComponentProps, TMode> {
const { id, mode = 'async', defaultComponent, transformProps } = options;
return {
id,
mode,
defaultComponent,
transformProps,
toString() {
return `ComponentRef{id=${id}}`;
},
} as ComponentRef<T>;
} as ComponentRef<TInnerComponentProps, TExternalComponentProps, TMode>;
}