javascript - 采用 Javascript 组件的打字稿;如何解决错误
问题描述
我正在尝试在用打字稿编写的页面中使用用 javascript 编写的组件。
我不想在 tsx 中重写组件,有没有办法让 tsx 避免查看此组件,或者将组件的类型设置为所有输入的任何类型?
在此先感谢,这是下面的组件和我正在使用的示例的代码框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
如果我可以在组件内部设置更新并跳过重置,就像我对其他输入所做的那样,那将是完美的!
import table from "asdasdasd table"
...
<Table columns = {columns as any} data = {productList as any} />
根据答案更新
使用新的允许 JS,我仍然收到如下类似的错误,
Type '{ columns: MutableRefObject<any>; data: any; }' is missing the following properties from type '{ columns: any; data: any; updateMyData: any; skipReset: any; }': updateMyData, skipReset
这是输出的当前 index.tsx 文件。
....
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
解决方案
您需要在 tsconfig.json 文件的编译器选项中添加选项"allowJs": true
,在这里您可以找到更多信息
{
"compilerOptions": {
"allowJs": true,
},
}
推荐阅读
- c# - 从 C# 调试 C++ 静态库
- python - 将包含项目和计数的字典转换为项目列表
- azure-devops - 如何设置我的 Az DevOps 管道以驻留在不同的分支中?
- ruby-on-rails - 如何使用引用直通表的范围编写`has_many:through`?
- mongodb - mongosql 未在 Red Hat 8 上启动:“加载共享库时出错:libssl.so.10:无法打开共享对象文件:没有这样的文件或目录”
- flutter - Flutter:如何使整行可点击
- gps - Onboard SDK 能否提供原始伪距和载波相位测量和星历数据?
- git - 尝试在 GitHub 上分叉任何 repo 会引发“你已经分叉了..再次分叉到另一个帐户”对话框
- angular-reactive-forms - 表单提交成功后出现验证错误
- c++ - 如何使 msvc 矢量化浮点加法?