Reputation: 43
Is it possible to have dynamic type? I have a json like this
{
"fieldName": "Some text",
"type": String,
"inputType": "text"
},
{
"fieldName": "Some bool",
"type": Boolean,
"inputType": "checkbox
}
And based on that json I would like to render field components like this one
const Field: React.FC<FieldInterface> = ({ name, type, handler }) => {
const [value, setValue] = useState<type>()
const handleOnChane = (e: ChangeEvent<HTMLInputElement>) => {
setValue(e.target.value)
handler(name, e.target.value)
}
return (
<div>
<label htmlFor={name}>{name}</label>
<input
type={type}
id={name}
onChange={handleOnChane}
value={value}
></input>
</div>
)
}
export default Field
here are my interfaces
export interface FormInterface {
fields: FieldPropInterface[]
submitAction: Function
}
export interface FieldPropInterface {
name: string
inputType: string
type: <Here I would like to have something but don't know what>
}
export interface FieldInterface {
name: string
type: string
handler: Function
}
You see, I need that type to set type of useState hook variable. Is it possible to do that?
Repo link: https://github.com/johnathan-codes/react-form-from-json
Upvotes: 4
Views: 8271
Reputation: 42298
The other responses will work, but they aren't making use of the full power of typescript. What you want is to establish a relationship between the fields inputType
and type
such that fields with {inputType: "checkbox"}
must always be boolean
, {inputType: "text"}
must always be string
, and so on.
Here's how you would do that with a Union Type (you could also make use of a map or a conditional, but I won't get in to that).
type FieldPropInterface = {
fieldName: string;
} & ({
type: "string";
inputType: "text";
} | {
type: "boolean";
inputType: "checkbox";
})
You need to read up a bit more on the HTML input element and its props because you want to treat a checkbox differently than a text input. Checkboxes set their value through a boolean
property called checked
rather than value
which is a string. There are also loads of packages on npm that can make dealing with forms a lot easier.
If you want useState
to be generic then your component Field
should be generic.
You've also said that your FieldInterface.handler
can be any type of Function
, but you need to be specific about what that function is.
{ handler: (e: ChangeEvent<HTMLInputElement>) => void; }
or maybe it can become a function of value
rather than event
with a different setup.
Are you sure that you know what you're doing when using the capitalized name String
? The capitalized name means that the value is the String constructor, whereas the lowercase means that the value is a string
. From the docs:
Don't ever use the types Number, String, Boolean, Symbol, or Object These types refer to non-primitive boxed objects that are almost never used appropriately in JavaScript code.
If you are using this as a flag, just to say "this value is a string" or "this value is a boolean" you might consider using the literal string "string"
and "boolean"
rather than the object constructors, but I don't know where and how this is actually being used in your code.
Upvotes: 3
Reputation: 667
You can also do something like this:
export interface FieldInterface<T> {
name: string
type: T
handler: Function
}
const Field: React.FC<FieldInterface<typeof type>> = ({ name, type, handler }) => {
const [value, setValue] = useState<type>()
const handleOnChane = (e: ChangeEvent<HTMLInputElement>) => {
setValue(e.target.value)
handler(name, e.target.value)
}
return (
<div>
<label htmlFor={name}>{name}</label>
<input
type={type}
id={name}
onChange={handleOnChane}
value={value}
></input>
</div>
)
}
export default Field
Upvotes: -1
Reputation: 41913
Use alternative types.
export interface FieldPropInterface {
name: string;
inputType: string;
type: Boolean | String;
}
Upvotes: 2