Skip to content

Commit f9f26ac

Browse files
Support more edge-cases
1 parent 3308621 commit f9f26ac

File tree

2 files changed

+67
-7
lines changed

2 files changed

+67
-7
lines changed

public-types/reflect.d.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,22 @@ export function list<
184184

185185
// variant types
186186

187+
/**
188+
* Computes final props type based on Props of the view component and Bind object for variant operator specifically
189+
*
190+
* Difference is important since in variant case Props is a union
191+
*
192+
* Props that are "taken" by Bind object are made **optional** in the final type,
193+
* so it is possible to overwrite them in the component usage anyway
194+
*/
195+
type FinalPropsVariant<Props, Bind extends BindFromProps<Props>> = Show<
196+
Props extends any
197+
? Omit<Props, keyof Bind> & {
198+
[K in Extract<keyof Bind, keyof Props>]?: Props[K];
199+
}
200+
: never
201+
>;
202+
187203
/**
188204
* Operator to conditionally render a component based on the reactive `source` store value.
189205
*
@@ -237,7 +253,7 @@ export function variant<
237253
*/
238254
useUnitConfig?: UseUnitConfig;
239255
},
240-
): FC<FinalProps<Props, Bind>>;
256+
): FC<FinalPropsVariant<Props, Bind>>;
241257

242258
// fromTag types
243259
/**

type-tests/types-variant.tsx

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import { expectType } from 'tsd';
3131
},
3232
});
3333

34-
expectType<React.FC>(VariableInput);
34+
<VariableInput />;
3535
}
3636

3737
// variant allows to pass incompatible props between cases - resulting component will have union of all props from all cases
@@ -60,7 +60,7 @@ import { expectType } from 'tsd';
6060
},
6161
});
6262

63-
expectType<React.FC>(VariableInput);
63+
<VariableInput />;
6464
}
6565

6666
// variant allows not to set every possble case
@@ -88,7 +88,7 @@ import { expectType } from 'tsd';
8888
default: NotFoundPage,
8989
});
9090

91-
expectType<React.FC>(CurrentPage);
91+
<CurrentPage />;
9292
}
9393

9494
// variant warns about wrong cases
@@ -116,7 +116,7 @@ import { expectType } from 'tsd';
116116
default: NotFoundPage,
117117
});
118118

119-
expectType<React.FC>(CurrentPage);
119+
<CurrentPage />;
120120
}
121121

122122
// overload for boolean source
@@ -139,14 +139,15 @@ import { expectType } from 'tsd';
139139
else: FallbackPage,
140140
bind: { context: $ctx },
141141
});
142-
expectType<React.FC>(CurrentPageThenElse);
142+
143+
<CurrentPageThenElse />;
143144

144145
const CurrentPageOnlyThen = variant({
145146
if: $enabled,
146147
then: HomePage,
147148
bind: { context: $ctx },
148149
});
149-
expectType<React.FC>(CurrentPageOnlyThen);
150+
<CurrentPageOnlyThen />;
150151
}
151152

152153
// supports nesting
@@ -286,3 +287,46 @@ import { expectType } from 'tsd';
286287
else: Button<'a'>,
287288
});
288289
}
290+
291+
// variant should allow not-to pass required props - as they can be added later in react
292+
{
293+
const Input: React.FC<{
294+
value: string;
295+
onChange: (newValue: string) => void;
296+
color: 'red';
297+
}> = () => null;
298+
const $variants = createStore<'input' | 'fallback'>('input');
299+
const Fallback: React.FC<{ kek?: string }> = () => null;
300+
const $value = createStore<string>('');
301+
const changed = createEvent<string>();
302+
303+
const InputBase = reflect({
304+
view: Input,
305+
bind: {
306+
value: $value,
307+
onChange: changed,
308+
},
309+
});
310+
311+
const ReflectedInput = variant({
312+
source: $variants,
313+
cases: {
314+
input: InputBase,
315+
fallback: Fallback,
316+
},
317+
});
318+
319+
const App: React.FC = () => {
320+
// missing prop must still be required in react
321+
// but in this case it is not required, as props are conditional union
322+
return <ReflectedInput />;
323+
};
324+
325+
<ReflectedInput kek="kek" />;
326+
327+
const AppFixed: React.FC = () => {
328+
return <ReflectedInput color="red" />;
329+
};
330+
expectType<React.FC>(App);
331+
expectType<React.FC>(AppFixed);
332+
}

0 commit comments

Comments
 (0)