Repository: jaredpalmer/formik (ts)

Issue:
Warning: You should not use a non-string <Field component> and <Field children> in the same <Field> component; <Field component> will be ignored.
Using a custom Select field gives me the warning ` Warning: You should not use a non-string <Field component> and <Field children> in the same <Field> component; <Field component> will be ignored.`.

I dont' see how I can use a custom Select component on a Field, not be a string and also have children, I think this warning isn't valid.

This is the code that generates the warning.
```jsx
<Field
  component={FormikSelectField}
  name="availability"
  floatingLabelText="Availability"
  fullWidth
>
  {Object.keys(USER_AVAILABILITY_LABELS).map(availability => (
    <MenuItem
      key={availability}
      value={availability}
      primaryText={USER_AVAILABILITY_LABELS[availability]}
    />
  ))}
</Field>
```

```jsx
// FormikSelectField.js
import React from 'react';
import SelectField from 'material-ui/SelectField';

const FormikSelectField = ({
  field: { name, value },
  form: { touched, errors, setFieldValue, setFieldTouched },
  onChange,
  ...otherProps
}) => {
  const handleChange = (event, key, data) => {
    setFieldTouched(name, true);
    setFieldValue(name, data);

    if (onChange) {
      onChange(event, key, data);
    }
  };

  return (
    <SelectField
      errorText={touched[name] && errors[name]}
      value={value}
      onChange={handleChange}
      {...otherProps}
    />
  );
};

export default FormikSelectField;
```

Pull request description:
Improve Field warnings
I tried to improve warnings to cover all invalid cases.

First of all I removed `component` default prop and moved it to the `render()` as default argument. This way can check for `component`'s existence rather if it isn't a string.

This passed checks before:

```javascript
<Field
  name="text"
  component="select"
  render={() => <input type="text" />}
/>
```

Now it warns user that `You should not use <Field component> and <Field render> in the same <Field> component; <Field component> will be ignored`. It of course still warns user, if React component is passed and it works if nothing is specified.

Same condition is also applied for the check with `children`, but here I also changed condition to check if children is a function to fix #255.

**PROPOSAL**
`warning` logic is inconsistent between `Field` and [formik](https://github.com/jaredpalmer/formik/blob/master/src/formik.tsx#L271). Maybe we should make it so?

**Qs**
In React documentation, `createElement`'s states to pass children as a third argument - https://reactjs.org/docs/react-api.html#createelement, does it make any difference?

When running `test` script a get bunch of `Warning: Accessing PropTypes via the main React package is deprecated, and will be removed in  React v16.0. Use the latest available v15.* prop-types package from npm instead. For info on usage, compatibility, migration and more, see https://fb.me/prop-types-docs`, is it happening to you too?

Fixes #255 
Closes #256 

Patch:
diff --git a/README.md b/README.md
index bfb77d9..62913fa 100644
--- a/README.md
+++ b/README.md
@@ -304,9 +304,7 @@ npm install yup --save
 ```
 
 <!-- START doctoc generated TOC please keep comment here to allow auto update -->
-
 <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
-
 **Table of Contents**
 
 * [Usage](#usage)
@@ -365,6 +363,7 @@ npm install yup --save
   * [React Native](#react-native)
     * [Why use `setFieldValue` instead of `handleChange`?](#why-use-setfieldvalue-instead-of-handlechange)
     * [Avoiding new functions in render](#avoiding-new-functions-in-render)
+    * [Usage with TypeScript](#usage-with-typescript)
 * [Organizations and projects using Formik](#organizations-and-projects-using-formik)
 * [Authors](#authors)
 * [Contributors](#contributors)
@@ -1284,6 +1283,119 @@ const MyReactNativeForm = props => (
 export default MyReactNativeForm;
 ```
 
+#### Usage with TypeScript
+
+To create a typed instance of Formik, create an interface for the form's values,
+then create an empty class which extends `Formik`, using `FormikConfig` and the
+values interface as a generic, like so:
+
+```tsx
+import * as React from 'react'
+import { Formik, FormikConfig } from 'formik'
+
+interface LoginFormValues {
+  username: string
+  password: string
+}
+
+class LoginForm extends Formik<FormikConfig<LoginFormValues>> {}
+
+class LoginView extends React.Component {
+  render() {
+    return (
+      <LoginForm
+        initialValues={/* ... */}
+        onSubmit={/* ... */}
+        render={/* ... */}
+      />
+    )
+  }
+}
+```
+
+Doing this allows us to have static type checks for `initialValues` and other
+values props when using the component.
+
+```tsx
+<LoginForm
+  // Compiler error, misspelled "username"
+  initialValues={{ usename: '', password: '' }}
+/>
+```
+
+Here's an example using the `withFormik` HOC:
+
+```tsx
+import React from 'react';
+import Yup from 'yup';
+import { withFormik, FormikProps, FormikErrors, Form, Field } from 'formik';
+
+// Shape of form values
+interface FormValues {
+  email: string;
+  password: string;
+}
+
+const InnerForm = (props: FormikProps<FormValues>) => {
+  const { touched, errors, isSubmitting } = props;
+  return (
+    <Form>
+      <Field type="email" name="email" />
+      {touched.email && errors.email && <div>{errors.email}</div>}
+
+      <Field type="password" name="password" />
+      {touched.password && errors.password && <div>{errors.password}</div>}
+
+      <button type="submit" disabled={isSubmitting}>
+        Submit
+      </button>
+    </Form>
+  );
+};
+
+// The type of props MyForm receives
+interface MyFormProps {
+  initialEmail?: string;
+}
+
+// Wrap our form with the using withFormik HoC
+const MyForm = withFormik<MyFormProps, FormValues>({
+  // Transform outer props into form values
+  mapPropsToValues: props => {
+    return {
+      email: props.initialEmail || '',
+      password: '',
+    };
+  },
+
+  // Add a custom validation function (this can be async too!)
+  validate: (values: FormValues) => {
+    let errors: FormikErrors = {};
+    if (!values.email) {
+      errors.email = 'Required';
+    } else if (!isValidEmail(values.email)) {
+      errors.email = 'Invalid email address';
+    }
+    return errors;
+  },
+
+  handleSubmit: values => {
+    // do submitting things
+  },
+})(InnerForm);
+
+// Use <MyForm /> anywhere
+const Basic = () => (
+  <div>
+    <h1>My Form</h1>
+    <p>This can be anywhere in your application</p>
+    <MyForm />
+  </div>
+);
+
+export default Basic;
+```
+
 ## Organizations and projects using Formik
 
 [List of organizations and projects using Formik](https://github.com/jaredpalmer/formik/issues/87)
diff --git a/src/Field.tsx b/src/Field.tsx
index f4edb93..90fb636 100644
--- a/src/Field.tsx
+++ b/src/Field.tsx
@@ -2,7 +2,7 @@ import * as PropTypes from 'prop-types';
 import * as React from 'react';
 
 import { FormikProps } from './formik';
-import { isEmptyChildren } from './utils';
+import { isFunction, isEmptyChildren } from './utils';
 import warning from 'warning';
 
 export type GenericFieldHTMLAttributes =
@@ -86,10 +86,6 @@ export class Field<Props extends FieldAttributes = any> extends React.Component<
     formik: PropTypes.object,
   };
 
-  static defaultProps = {
-    component: 'input',
-  };
-
   static propTypes = {
     name: PropTypes.string.isRequired,
     component: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
@@ -98,33 +94,28 @@ export class Field<Props extends FieldAttributes = any> extends React.Component<
   };
 
   componentWillMount() {
+    const { render, children, component } = this.props;
+
     warning(
-      !(typeof this.props.component !== 'string' && this.props.render),
+      !(component && render),
       'You should not use <Field component> and <Field render> in the same <Field> component; <Field component> will be ignored'
     );
 
     warning(
-      !(
-        typeof this.props.component !== 'string' &&
-        this.props.children &&
-        !isEmptyChildren(this.props.children)
-      ),
-      'You should not use a non-string <Field component> and <Field children> in the same <Field> component; <Field component> will be ignored.'
+      !(component && children && isFunction(children)),
+      'You should not use <Field component> and <Field children> as a function in the same <Field> component; <Field component> will be ignored.'
     );
 
     warning(
-      !(
-        this.props.render &&
-        this.props.children &&
-        !isEmptyChildren(this.props.children)
-      ),
+      !(render && children && !isEmptyChildren(children)),
       'You should not use <Field render> and <Field children> in the same <Field> component; <Field children> will be ignored'
     );
   }
 
   render() {
-    const { component, render, children, name, ...props } = this
+    const { name, render, children, component = 'input', ...props } = this
       .props as FieldConfig;
+
     const { formik } = this.context;
     const field = {
       value:
@@ -141,7 +132,7 @@ export class Field<Props extends FieldAttributes = any> extends React.Component<
       return (render as any)(bag);
     }
 
-    if (typeof children === 'function') {
+    if (isFunction(children)) {
       return (children as (props: FieldProps<any>) => React.ReactNode)(bag);
     }
 


Test patch:
diff --git a/test/Field.test.tsx b/test/Field.test.tsx
index c497b4e..dc79c50 100644
--- a/test/Field.test.tsx
+++ b/test/Field.test.tsx
@@ -164,11 +164,35 @@ describe('A <Field />', () => {
         />,
         node
       );
-      console.log(node.innerHTML);
+
       expect(node.innerHTML).toContain(TEXT);
     });
 
-    it('warns if both non-string component and children', () => {
+    it('warns if both string component and children as a function', () => {
+      let output = '';
+      let actual;
+
+      (global as any).console = {
+        error: jest.fn(input => (output += input)),
+      };
+
+      ReactDOM.render(
+        <TestForm
+          render={() => (
+            <Field name="name" component="select">
+              {() => <option value="Jared">{TEXT}</option>}
+            </Field>
+          )}
+        />,
+        node
+      );
+
+      expect(output).toContain(
+        'You should not use <Field component> and <Field children> as a function in the same <Field> component; <Field component> will be ignored.'
+      );
+    });
+
+    it('warns if both non-string component and children children as a function', () => {
       let output = '';
       let actual;
       const Component = props => (actual = props) && null;
@@ -181,7 +205,7 @@ describe('A <Field />', () => {
         <TestForm
           render={() => (
             <Field component={Component} name="name">
-              <option value="Jared">{TEXT}</option>
+              {() => <option value="Jared">{TEXT}</option>}
             </Field>
           )}
         />,
@@ -189,7 +213,33 @@ describe('A <Field />', () => {
       );
 
       expect(output).toContain(
-        'You should not use a non-string <Field component> and <Field children> in the same <Field> component; <Field component> will be ignored.'
+        'You should not use <Field component> and <Field children> as a function in the same <Field> component; <Field component> will be ignored.'
+      );
+    });
+
+    it('warns if both string component and render', () => {
+      let output = '';
+      let actual;
+
+      (global as any).console = {
+        error: jest.fn(input => (output += input)),
+      };
+
+      ReactDOM.render(
+        <TestForm
+          render={() => (
+            <Field
+              component="select"
+              name="name"
+              render={() => <div>{TEXT}</div>}
+            />
+          )}
+        />,
+        node
+      );
+
+      expect(output).toContain(
+        'You should not use <Field component> and <Field render> in the same <Field> component; <Field component> will be ignored'
       );
     });