4

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

4
  • have you already taken a look at the typescript typeof operator? Commented Oct 5, 2020 at 12:14
  • 1
    Consider reading typescript docs. It's kinda basics of the basics. Commented Oct 5, 2020 at 12:16
  • @A_A But that doesn't resolve a constructor like String to the type string-you're still relying on the types you supply. OP is trying to use runtime info for static typing. Commented Oct 5, 2020 at 12:22
  • 1
    I think what you actually want is a union type which enforces a relationship between properties inputType and type. Commented Oct 5, 2020 at 15:31

3 Answers 3

3

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for your answer. You helped me a lot. I am trying to create own form generator, without using 3rd party libraries. I added a repo link to the question, maybe it will make my question clearer, I am not good at explaining something I don't fully understand.
2

Use alternative types.

export interface FieldPropInterface {
  name: string;
  inputType: string;
  type: Boolean | String;
}

Comments

-1

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

3 Comments

This is not the way to go, you can't change the interface during the runtime.
Then you have only this way to use - type: Boolean | String;
Exactly. And this is what I have posted above.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.