Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript & React Component that accepts onChange for both TextArea and Input

I'm new to typescript and I am trying to create an input component that, if it receives type="text" it renders an input and if it receives type="textarea" it renders, you got it, a textarea. The problem is that typescript is complaining when I use the component on my code together with a onChange, it seems it wont allow me to use two types on the same event?

It shows me:

Type '(e: ChangeEvent<HTMLInputElement>) => void' is not assignable to type '(e?: ChangeEvent<HTMLInputElement> | ChangeEvent<HTMLTextAreaElement> | undefined) => void'.
Types of parameters 'e' and 'e' are incompatible.
Type 'ChangeEvent<HTMLInputElement> | ChangeEvent<HTMLTextAreaElement> | undefined' is not assignable to type 'ChangeEvent<HTMLInputElement>'.
Type 'undefined' is not assignable to type 'ChangeEvent<HTMLInputElement>'.

input.js

interface InputProps {
  className?: string;
  type?: string;
  placeholder?: string;
  onChange?: (e?: React.ChangeEvent<HTMLInputElement> | React.ChangeEvent<HTMLTextAreaElement>) => void;
}

const Input: FunctionComponent<InputProps> = ({ type = 'text', ...props }) => {
  if (type === 'textarea') {
    return <textarea {...props} />;
  }
  return <input type={type} {...props} />;
};

usage:

class Usage extends React.Component<State> {
  state: State;

  onInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    this.setState({ input: e.target.value });
  };

  render() {
    return (
        <Input placeholder="Write an something..." onChange={this.onInputChange} />
    );
  }
}

How can I fix it?


UPDATE

The way I solved it for the moment is by saying that event can be of type any, but that's kind of a hack

type CommonProps = {
  className?: string;
  placeholder?: string;
  type?: string;
  onChange?: (e: any) => void;
};
like image 221
JCQuintas Avatar asked Dec 27 '18 10:12

JCQuintas


People also ask

What is TypeScript used for?

TypeScript extends JavaScript and improves the developer experience. It enables developers to add type safety to their projects. Moreover, TypeScript provides various other features, like interfaces, type aliases, abstract classes, function overloading, tuple, generics, etc.

Is TypeScript better than JavaScript?

TypeScript is better than JavaScript in terms of language features, reference validation, project scalability, collaboration within and between teams, developer experience, and code maintainability.

Is TypeScript frontend or backend?

TypeScript is neither a frontend or backend language, but rather a superset of the already established and well-known software language, JavaScript.

What is difference between JavaScript and TypeScript?

TypeScript is an object-oriented programming language developed by Microsoft Corporation, whereas JavaScript is the programming language for the web. TypeScript is an open-source language to build large-scale web apps, whereas JavaScript is a server-side programming language that helps to develop interactive web pages.


2 Answers

You can use a discriminated union to pass in two types of arguments, one for text and the other for textarea. This has the added bonus of ensuring the handler and the type are in sync.

type InputProps = { // The common Part
    className?: string;
    placeholder?: string;
} & ({ // The discriminated union
    type?: "text";
    onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
} | {
    type: "textarea";
    onChange?: (e: React.ChangeEvent<HTMLTextAreaElement>) => void;
})

const Input: FunctionComponent<InputProps> = (props: InputProps) => {
    if (props.type === 'textarea') {
        return <textarea {...props} />;
    }
    return <input {...props} />;
};


class Usage extends React.Component<State> {
    state: State;

    onInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
        this.setState({ input: e.target.value });
    };

    render() {
        return (
            <Input placeholder="Write an something..." onChange={this.onInputChange} />
        );
    }
}
like image 152
Titian Cernicova-Dragomir Avatar answered Oct 19 '22 03:10

Titian Cernicova-Dragomir


You need to create a class incase you are using typescript. The normal function does not allows the | in the typescript prop types.

This should be your Input.js file:

export interface IInputProps {
  className?: string;
  type?: string;
  placeholder?: string;
  onChange?: (e?: React.ChangeEvent<HTMLInputElement> | React.ChangeEvent<HTMLTextAreaElement>) => void;
}

export class Input extends React.Component<IInputProps, {}> {
  constructor(props: IInputProps) {
    super(props);
  }
  render() {
    const { props, props: { type } } = this;
    if (type === 'textarea') {
      return <textarea {...props} />;
    }
    return <input type={type} {...props} />;
  }
}

and here is how it can be used:

class Usage extends React.Component<State> {
  state: State;

  onInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    this.setState({ input: e.target.value });
  };

  render() {
    return (
      <Input placeholder="Write an something..." onChange={this.onInputChange} />
    );
  }
}

This is how it will evaluate if it is a Input or a TextArea:

enter image description here

like image 41
Harish Soni Avatar answered Oct 19 '22 02:10

Harish Soni