1

I'm trying to use a component written in javascript in a page written in typescript.

I don't want to re-write the component in tsx, is there a way to have tsx avoid looking at this component, or to set types for the component as any for all the inputs?

Thanks in advance, and here is the component below and the codesandbox for the example I'm using for it https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/kitchen-sink,

import {
    useTable,
    usePagination,
    useSortBy,
    useFilters,
    useGroupBy,
    useExpanded,
    useRowSelect,
  } from 'react-table'
  import matchSorter from 'match-sorter'

  import makeData from '../example/makedata.js'


  // Create an editable cell renderer
  const EditableCell = ({
    value: initialValue,
    row: { index },
    column: { id },
    updateMyData, // This is a custom function that we supplied to our table instance
    editable,
  }) => {
    // We need to keep and update the state of the cell normally
    const [value, setValue] = React.useState(initialValue)

    const onChange = e => {
      setValue(e.target.value)
    }

    // We'll only update the external data when the input is blurred
    const onBlur = () => {
      updateMyData(index, id, value)
    }

    // If the initialValue is changed externall, sync it up with our state
    React.useEffect(() => {
      setValue(initialValue)
    }, [initialValue])

    if (!editable) {
      return `${initialValue}`
    }

    return <input value={value} onChange={onChange} onBlur={onBlur} />
  }

  // Define a default UI for filtering
  function DefaultColumnFilter({
    column: { filterValue, preFilteredRows, setFilter },
  }) {
    const count = preFilteredRows.length

    return (
      <input
        value={filterValue || ''}
        onChange={e => {
          setFilter(e.target.value || undefined) // Set undefined to remove the filter entirely
        }}
        placeholder={`Search ${count} records...`}
      />
    )
  }

  // This is a custom filter UI for selecting
  // a unique option from a list
  function SelectColumnFilter({
    column: { filterValue, setFilter, preFilteredRows, id },
  }) {
    // Calculate the options for filtering
    // using the preFilteredRows
    const options = React.useMemo(() => {
      const options = new Set()
      preFilteredRows.forEach(row => {
        options.add(row.values[id])
      })
      return [...options.values()]
    }, [id, preFilteredRows])

    // Render a multi-select box
    return (
      <select
        value={filterValue}
        onChange={e => {
          setFilter(e.target.value || undefined)
        }}
      >
        <option value="">All</option>
        {options.map((option, i) => (
          <option key={i} value={option}>
            {option}
          </option>
        ))}
      </select>
    )
  }

  // This is a custom filter UI that uses a
  // slider to set the filter value between a column's
  // min and max values
  function SliderColumnFilter({
    column: { filterValue, setFilter, preFilteredRows, id },
  }) {
    // Calculate the min and max
    // using the preFilteredRows

    const [min, max] = React.useMemo(() => {
      let min = preFilteredRows.length ? preFilteredRows[0].values[id] : 0
      let max = preFilteredRows.length ? preFilteredRows[0].values[id] : 0
      preFilteredRows.forEach(row => {
        min = Math.min(row.values[id], min)
        max = Math.max(row.values[id], max)
      })
      return [min, max]
    }, [id, preFilteredRows])

    return (
      <>
        <input
          type="range"
          min={min}
          max={max}
          value={filterValue || min}
          onChange={e => {
            setFilter(parseInt(e.target.value, 10))
          }}
        />
        <button onClick={() => setFilter(undefined)}>Off</button>
      </>
    )
  }

  // This is a custom UI for our 'between' or number range
  // filter. It uses two number boxes and filters rows to
  // ones that have values between the two
  function NumberRangeColumnFilter({
    column: { filterValue = [], preFilteredRows, setFilter, id },
  }) {
    const [min, max] = React.useMemo(() => {
      let min = preFilteredRows.length ? preFilteredRows[0].values[id] : 0
      let max = preFilteredRows.length ? preFilteredRows[0].values[id] : 0
      preFilteredRows.forEach(row => {
        min = Math.min(row.values[id], min)
        max = Math.max(row.values[id], max)
      })
      return [min, max]
    }, [id, preFilteredRows])

    return (
      <div
        style={{
          display: 'flex',
        }}
      >
        <input
          value={filterValue[0] || ''}
          type="number"
          onChange={e => {
            const val = e.target.value
            setFilter((old = []) => [val ? parseInt(val, 10) : undefined, old[1]])
          }}
          placeholder={`Min (${min})`}
          style={{
            width: '70px',
            marginRight: '0.5rem',
          }}
        />
        to
        <input
          value={filterValue[1] || ''}
          type="number"
          onChange={e => {
            const val = e.target.value
            setFilter((old = []) => [old[0], val ? parseInt(val, 10) : undefined])
          }}
          placeholder={`Max (${max})`}
          style={{
            width: '70px',
            marginLeft: '0.5rem',
          }}
        />
      </div>
    )
  }

  function fuzzyTextFilterFn(rows, id, filterValue) {
    return matchSorter(rows, filterValue, { keys: [row => row.values[id]] })
  }

  // Let the table remove the filter if the string is empty
  fuzzyTextFilterFn.autoRemove = val => !val

  // Be sure to pass our updateMyData and the skipReset option
  function Table({ columns, data, updateMyData, skipReset }) {
    const filterTypes = React.useMemo(
      () => ({
        // Add a new fuzzyTextFilterFn filter type.
        fuzzyText: fuzzyTextFilterFn,
        // Or, override the default text filter to use
        // "startWith"
        text: (rows, id, filterValue) => {
          return rows.filter(row => {
            const rowValue = row.values[id]
            return rowValue !== undefined
              ? String(rowValue)
                  .toLowerCase()
                  .startsWith(String(filterValue).toLowerCase())
              : true
          })
        },
      }),
      []
    )

    const defaultColumn = React.useMemo(
      () => ({
        // Let's set up our default Filter UI
        Filter: DefaultColumnFilter,
        // And also our default editable cell
        Cell: EditableCell,
      }),
      []
    )

    // Use the state and functions returned from useTable to build your UI
    const {
      getTableProps,
      getTableBodyProps,
      headerGroups,
      prepareRow,
      page, // Instead of using 'rows', we'll use page,
      // which has only the rows for the active page

      // The rest of these things are super handy, too ;)
      canPreviousPage,
      canNextPage,
      pageOptions,
      pageCount,
      gotoPage,
      nextPage,
      previousPage,
      setPageSize,
      state: {
        pageIndex,
        pageSize,
        sortBy,
        groupBy,
        expanded,
        filters,
        selectedRowIds,
      },
    } = useTable(
      {
        columns,
        data,
        defaultColumn,
        filterTypes,
        // updateMyData isn't part of the API, but
        // anything we put into these options will
        // automatically be available on the instance.
        // That way we can call this function from our
        // cell renderer!
        updateMyData,
        // We also need to pass this so the page doesn't change
        // when we edit the data.
        autoResetPage: !skipReset,
        autoResetSelectedRows: !skipReset,
        disableMultiSort: true,
      },
      useFilters,
      useGroupBy,
      useSortBy,
      useExpanded,
      usePagination,
      useRowSelect,
      // Here we will use a plugin to add our selection column
      hooks => {
        hooks.visibleColumns.push(columns => {
          return [
            {
              id: 'selection',
              // Make this column a groupByBoundary. This ensures that groupBy columns
              // are placed after it
              groupByBoundary: true,
              // The header can use the table's getToggleAllRowsSelectedProps method
              // to render a checkbox
              Header: ({ getToggleAllRowsSelectedProps }) => (
                <div>
                  <IndeterminateCheckbox {...getToggleAllRowsSelectedProps()} />
                </div>
              ),
              // The cell can use the individual row's getToggleRowSelectedProps method
              // to the render a checkbox
              Cell: ({ row }) => (
                <div>
                  <IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />
                </div>
              ),
            },
            ...columns,
          ]
        })
      }
    )

    // Render the UI for your table
    return (
      <>
        <table {...getTableProps()}>
          <thead>
            {headerGroups.map(headerGroup => (
              <tr {...headerGroup.getHeaderGroupProps()}>
                {headerGroup.headers.map(column => (
                  <th {...column.getHeaderProps()}>
                    <div>
                      {column.canGroupBy ? (
                        // If the column can be grouped, let's add a toggle
                        <span {...column.getGroupByToggleProps()}>
                          {column.isGrouped ? '🛑 ' : '👊 '}
                        </span>
                      ) : null}
                      <span {...column.getSortByToggleProps()}>
                        {column.render('Header')}
                        {/* Add a sort direction indicator */}
                        {column.isSorted
                          ? column.isSortedDesc
                            ? ' 🔽'
                            : ' 🔼'
                          : ''}
                      </span>
                    </div>
                    {/* Render the columns filter UI */}
                    <div>{column.canFilter ? column.render('Filter') : null}</div>
                  </th>
                ))}
              </tr>
            ))}
          </thead>
          <tbody {...getTableBodyProps()}>
            {page.map(row => {
              prepareRow(row)
              return (
                <tr {...row.getRowProps()}>
                  {row.cells.map(cell => {
                    return (
                      <td {...cell.getCellProps()}>
                        {cell.isGrouped ? (
                          // If it's a grouped cell, add an expander and row count
                          <>
                            <span {...row.getToggleRowExpandedProps()}>
                              {row.isExpanded ? '👇' : '👉'}
                            </span>{' '}
                            {cell.render('Cell', { editable: false })} (
                            {row.subRows.length})
                          </>
                        ) : cell.isAggregated ? (
                          // If the cell is aggregated, use the Aggregated
                          // renderer for cell
                          cell.render('Aggregated')
                        ) : cell.isPlaceholder ? null : ( // For cells with repeated values, render null
                          // Otherwise, just render the regular cell
                          cell.render('Cell', { editable: true })
                        )}
                      </td>
                    )
                  })}
                </tr>
              )
            })}
          </tbody>
        </table>
        {/*
          Pagination can be built however you'd like.
          This is just a very basic UI implementation:
        */}
        <div className="pagination">
          <button onClick={() => gotoPage(0)} disabled={!canPreviousPage}>
            {'<<'}
          </button>{' '}
          <button onClick={() => previousPage()} disabled={!canPreviousPage}>
            {'<'}
          </button>{' '}
          <button onClick={() => nextPage()} disabled={!canNextPage}>
            {'>'}
          </button>{' '}
          <button onClick={() => gotoPage(pageCount - 1)} disabled={!canNextPage}>
            {'>>'}
          </button>{' '}
          <span>
            Page{' '}
            <strong>
              {pageIndex + 1} of {pageOptions.length}
            </strong>{' '}
          </span>
          <span>
            | Go to page:{' '}
            <input
              type="number"
              defaultValue={pageIndex + 1}
              onChange={e => {
                const page = e.target.value ? Number(e.target.value) - 1 : 0
                gotoPage(page)
              }}
              style={{ width: '100px' }}
            />
          </span>{' '}
          <select
            value={pageSize}
            onChange={e => {
              setPageSize(Number(e.target.value))
            }}
          >
            {[10, 20, 30, 40, 50].map(pageSize => (
              <option key={pageSize} value={pageSize}>
                Show {pageSize}
              </option>
            ))}
          </select>
        </div>
        <pre>
          <code>
            {JSON.stringify(
              {
                pageIndex,
                pageSize,
                pageCount,
                canNextPage,
                canPreviousPage,
                sortBy,
                groupBy,
                expanded: expanded,
                filters,
                selectedRowIds: selectedRowIds,
              },
              null,
              2
            )}
          </code>
        </pre>
      </>
    )
  }

  // Define a custom filter filter function!
  function filterGreaterThan(rows, id, filterValue) {
    return rows.filter(row => {
      const rowValue = row.values[id]
      return rowValue >= filterValue
    })
  }

  // This is an autoRemove method on the filter function that
  // when given the new filter value and returns true, the filter
  // will be automatically removed. Normally this is just an undefined
  // check, but here, we want to remove the filter if it's not a number
  filterGreaterThan.autoRemove = val => typeof val !== 'number'

  // This is a custom aggregator that
  // takes in an array of leaf values and
  // returns the rounded median
  function roundedMedian(leafValues) {
    let min = leafValues[0] || 0
    let max = leafValues[0] || 0

    leafValues.forEach(value => {
      min = Math.min(min, value)
      max = Math.max(max, value)
    })

    return Math.round((min + max) / 2)
  }

  const IndeterminateCheckbox = React.forwardRef(
    ({ indeterminate, ...rest }, ref) => {
      const defaultRef = React.useRef()
      const resolvedRef = ref || defaultRef

      React.useEffect(() => {
        resolvedRef.current.indeterminate = indeterminate
      }, [resolvedRef, indeterminate])

      return (
        <>
          <input type="checkbox" ref={resolvedRef} {...rest} />
        </>
      )
    }
  )



  export default Table

 and this is the error when calling it in the page 

Type '{ columns: any; data: any; }' is missing the following properties from type '{ columns: any; data: any; updateMyData: any; skipReset: any; }': updateMyData, skipReset

If I can just set update and skip reset inside the component as it's called as i've done with the other inputs, then it will be perfect!

import table from "asdasdasd table"
...
<Table columns = {columns as any} data = {productList as any} />

Update based on answer

Using the new allow JS, i still am receiving a similar error as below,

Type '{ columns: MutableRefObject<any>; data: any; }' is missing the following properties from type '{ columns: any; data: any; updateMyData: any; skipReset: any; }': updateMyData, skipReset

This is the current index.tsx file where it is outputting.

....
import Table from "../components/Visualization/HekticTable";



function NumberRangeColumnFilter({
  column: { filterValue = [], preFilteredRows, setFilter, id },
}) {
  const [min, max] = React.useMemo(() => {
    let min = preFilteredRows.length ? preFilteredRows[0].values[id] : 0
    let max = preFilteredRows.length ? preFilteredRows[0].values[id] : 0
    preFilteredRows.forEach(row => {
      min = Math.min(row.values[id], min)
      max = Math.max(row.values[id], max)
    })
    return [min, max]
  }, [id, preFilteredRows])

  return (
    <div
      style={{
        display: 'flex',
      }}
    >
      <input
        value={filterValue[0] || ''}
        type="number"
        onChange={e => {
          const val = e.target.value
          setFilter((old = []) => [val ? parseInt(val, 10) : undefined, old[1]])
        }}
        placeholder={`Min (${min})`}
        style={{
          width: '70px',
          marginRight: '0.5rem',
        }}
      />
      to
      <input
        value={filterValue[1] || ''}
        type="number"
        onChange={e => {
          const val = e.target.value
          setFilter((old = []) => [old[0], val ? parseInt(val, 10) : undefined])
        }}
        placeholder={`Max (${max})`}
        style={{
          width: '70px',
          marginLeft: '0.5rem',
        }}
      />
    </div>
  )
}


// function Output({ searched, productList }) {
//   interface OutputProps {
//     searched?: string
//     productList?: Product[]
// }

// const Output: React.FC<OutputProps> = ({ searched, productList }) => {
//   // Typescript infers searched and productList typing here
//   if(!searched && !productList) {
//     return null;
//   }return (
//     <div>
//     <div>
//             <p></p>

//             {/* <Chart data={productList} /> */}
//           </div>
//           <table className="table-auto">
//             <thead>
//               <tr>
//                 <th className="px-4 py-2">Name</th>
//                 <th className="px-4 py-2">Price</th>
//                 <th className="px-4 py-2">Brand</th>
//               </tr>
//             </thead>
//             <tbody>
//               {productList.map((e, index) => (
//                 <tr key={index}>
//                   <td className="border px-4 py-2">{e.Product}</td>
//                   <td className="border px-4 py-2">{e.Price}</td>
//                   <td className="border px-4 py-2">{e.Brand}</td>
//                 </tr>
//               ))}
//             </tbody>
//           </table>
//           </div>
//   );
// }
// }

export default function index() {
  const searchRef = useRef<HTMLInputElement>(null);
  const [message, setMessage] = useState<any>(null);

  const [productList, setProductList] = useState<any>([]);
  const [searched, setSearched] = useState(false);
...
  const columns = useRef<any | null>(React.useMemo(
    () => [
      {
        Header: "Brand",
        accessor: "Brand"
      },
      {
        Header: "Price",
        accessor: "Price",
        Filter: NumberRangeColumnFilter,
        filter: 'between',
        aggregate: 'sum',
        Aggregated: ({ value }) => `${value} (total)`,
      }
      ,
      {
        Header: "Product",
        accessor: "Product"
      }
    ],

    []
  ));



  async function handleSearch() {
    console.log("searching...", searchRef.current?.value);
    setMessage("Searching...");
    var headers = {
      "Content-Type": "application/x-www-form-urlencoded",
      "auth-token": token,
    };

    fetch(
      `xxx` +
      encodeURIComponent(searchRef.current.value),
      {
        method: "GET",
        headers: headers,
      }
    )
      .then((response) => {
        setMessage("");
        if (response.status !== 200) {
          console.log("error", response);
          return [];
        } else {
          console.log("success", response);
          let productList = response.json()
          console.log("data", productList)
          return productList;
        }
      })
      .then((responseData) => {
        console.log("responseData", responseData);

        // setting resonseData to productList
        setProductList(responseData);
        setSearched(true)
      });
  }






  if (token == null) {
    return (
      <Layout>
        <div>
          <h1>You must be logged in to continue.</h1>
          <Link href="/account/login">
            <a className="no-underline border-b border-blue text-blue">Login</a>
          </Link>
        </div>
      </Layout>
    );
  }

  return (
    <Layout>
      <div className="container mx-auto pt-32">
        <h1>Unlimited....POWER!</h1>


        <div>
          <div className="bg-white shadow p-4 flex">
            <span className="w-auto flex justify-end items-center text-gray-500 p-2">
              <i className="material-icons text-3xl">search</i>
            </span>
            <input
              className="w-full rounded p-2"
              type="text"
              placeholder="Try 'Phone'"
              ref={searchRef}
            />
            <button
              onClick={handleSearch}
              className="bg-red-400 hover:bg-red-300 rounded text-white p-2 pl-4 pr-4"
            >
              <p className="font-semibold text-xs">Search</p>
            </button>
          </div>

          <p>{message}</p>

          <div>
            <p></p>

            <Chart data={productList} />
            <Table columns = {columns} data = {productList} />
            <button>Download Data Now</button>
          </div>

          </div>


        </div>

    </Layout>
  );
}

        233 | 
        234 |             <Chart data={productList} />
      > 235 |             <Table columns = {columns} data = {productList} />
            |              ^
        236 |             <button>Download Data Now</button>
        237 |           </div>
        238 |         
    Warning: React.createFactory() is deprecated and will be removed in a future major release. Consider using JSX or use React.createElement() directly instead.
    undefined
    undefined
7
  • So you are leaving it as it is, in a JS file, but get errors when consuming it from a JS file? Is that your situation? Commented May 22, 2020 at 4:02
  • component is in JS, the page is in TSX where the component is being used. Commented May 22, 2020 at 4:05
  • Either write <Table columns={columns} data={productList} skipReset={false} updateMyData={undefined} /> or just provide explicit default values for them in Table, rendering them optional: function Table({ columns, data, updateMyData = undefined, skipReset = undefined }) Commented May 22, 2020 at 4:13
  • 1
    Or you could do the terse but hackish <Table props={...{columns, data: productList} as any}/> Commented May 22, 2020 at 4:23
  • No problem. Cheers! Commented May 22, 2020 at 4:33

1 Answer 1

4

you need to add in the compiler options of your tsconfig.json file the option "allowJs": true, here you will find more info

{
    "compilerOptions": {
        "allowJs": true,
    },

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

1 Comment

@MichaelHolborn you have to allow js and to allow optional type declaration, otherwise you have to use .d.ts to map types or map them in js comments.

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.