I have a requirement to create an interface with generic types that can accept an object with keys representing "root field names" and values as arrays of objects defining sub-fields with the key as the name of the sub-field and the type as the value's data type. Here is an example:
interface Inputs{
emails: { email: string, active: boolean, i: number }[]
}
const obj:Inputs = {emails: [ {email: "example@mail.com", active: true, i: 100} ]}
The interface which receives this as a generic type should have a "name" property that will take the (keyof) name of the sub-field (e.g., active) and a function with a parameter that must receive the type of the sub-field defined in the name property.
Here is an example:
[
{
name: "active",
component: ({ value, values }) => {
console.log(value, values);
return <>Component</>;
}
}
]
In this scenario, the 'value' variable should only accept the type 'boolean' since the 'active' key in the object has a boolean type.
I have managed to achieve almost everything I intended to do. The only issue is that instead of receiving the exact type of the subfield, the function's parameter gets a union of all types present in the object.
For instance, in the previous example, since 'email' is a string type, 'value' should also be a string type, but it actually becomes string | number | boolean (all available types in the object).
I hope I have explained this clearly enough, but I have set up a sandbox for better clarity
https://codesandbox.io/s/boring-fast-pmmhxx?file=/src/App.tsx
interface Options<
T extends { [key: string]: unknown }[],
Key extends keyof T[number]
> {
values: T;
value: Key;
}
interface InputDef<
T extends { [key: string]: any }[],
Key extends keyof T[number]
> {
name: Key;
component: (props: Options<T, T[number][Key]>) => React.ReactNode;
}
interface Props<T extends { [key: string]: [] }, Key extends keyof T> {
name: Key;
inputs: InputDef<T[Key], keyof T[Key][number]>[];
callback: (values: T) => void;
}
interface Inputs {
firstName: string;
lastName: string;
emails: { email: string; active: boolean; other: number }[];
}
const GenComponent = <T extends { [key: string]: any }, Key extends keyof T>({
name,
inputs
}: Props<T, Key>) => {
console.log(inputs);
return (
<div>
{name} {JSON.stringify(inputs)}
</div>
);
};
interface MainComponentProps {
callback: TestCallback<Inputs>;
}
const MainComponent: React.FC<MainComponentProps> = ({ callback }) => {
return (
<>
<GenComponent
callback={callback}
name="emails"
inputs={[
{
name: "active",
component: ({ value, values }) => {
console.log(value, values);
return <>Component</>;
}
}
]}
/>
</>
);
};
type TestCallback<Data> = (values: Data) => void;
function test<Data>(values: Data): void {
console.log(values);
}
export default function App() {
return (
<div className="App">
<MainComponent callback={test} />
</div>
);
}
On line 57, given that the name in the object is "active", the type of 'value' should ideally be "boolean" rather than "string | number | boolean". How can I resolve this?
Thank you!