Я новичок в общем/типовом криптировании.
Контекст: я пытаюсь передать данные со страниц (page.tsx) в общие компоненты (header.tsx). Данные извлекаются из функции реагирования-запроса.
Проблема: в файле header.tsx, компоненте Checkbox, Typescrypt говорит:
Свойство каждый не существует в типе T
Параметр «запись» неявно имеет тип «любой».
И я совершенно не понимаю, почему это тот случай, когда передаваемые данные гарантируют, что они являются массивом и существуют.
Что я подумал: TypeScript также уведомляет, что запись имеет любой тип, но я думал, что, поскольку компонент не распознает тип свойства «данных», если бы я мог каким-то образом получить тип для «данных», prop в headers.tsx, TypeScript сможет его вывести.
Я прикрепил ссылку на детскую площадку .
export type ProductRow = {
id: string;
name?: string;
sku?: string;
};
export const page = () => {
//data fetched from react-query useQuery
const data = [
{
brand: {
id: 'clywipo320000cayabq4iub7e',
name: 'Nero',
minMargin: 30,
maxMargin: 40,
},
id: '1',
name: "100mm Round Tile Insert Floor Waste 50mm Outlet Brushed Gold",
priceRecords: [
{
price: 120,
date: 'Fri Aug 23 2024 10:00:00 GMT+1000 (Australian Eastern Standard Time)'
}
],
sku: 'NRFW003GD'
},
{
brand: {
id: 'clywipo320000cayabq4iub7e',
name: 'Nero',
minMargin: 30,
maxMargin: 40,
},
id: '2',
name: "100mm Round Tile Insert Floor Waste 50mm Outlet Brushed Bronze",
priceRecords: [
{
price: 112,
date: 'Fri Aug 23 2024 10:00:00 GMT+1000 (Australian Eastern Standard Time)'
}
],
sku: 'NRFW003BZ'
}
]
const [selectedRows, setSelectedRows] = useState<ProductRow[]>([]);
return (
<div>
<Header data = {data} selectedRows = {selectedRows} />
{ data.map(record =>
<input
type = {`checkbox`}
checked = {selectedRows.map(row => row.id).includes(record.id)}
onClick = {() => setSelectedRows(prevValue => [...prevValue, { id: record.id, name: record.name, sku: record.sku }])} />
)}
</div>
)
}
type HeaderProps<T> = {
data: T,
selectedRows: ProductRow[]
};
export const Header = <T,>({ data, selectedRows }: HeaderProps<T>) => {
return (
<div className = {``}>
<div className = {`flex gap-3 items-start`}>
<input
type = {`checkbox`}
checked = {data.every(record => selectedRows.map(row => row.id).includes(record.id))}
/>
<p>SKU</p>
</div>
<p className = {`col-span-3`}>Product Name</p>
<p>Brand</p>
<p className = {`text-center`}>RRP</p>
<p className = {`text-center`}>Max Discount (%)</p>
<p className = {`text-center`}>Buildmat Price</p>
<p className = {`text-center`}>TheBlueSpace Price</p>
</div>
)
};
это потому, что у вас есть data.priceRecords = {
, у которого есть подраздел priceRecords
? то есть data.priceRecords.priceRecords = []
но data.priceRecords = {}
@dave Привет, я допустил опечатку в образце данных, у него нет подраздела.
@Dai В header.tsx checked = {priceRecords.every(record => selectedRows.map(row => row.id).includes(record.id))}
отображается сообщение об ошибке.
Я предполагаю, что ничто не ограничивает ваш универсальный T-тип массивом, поэтому он не обязательно может иметь метод Every.
Пожалуйста, отредактируйте так, чтобы ваш код представлял собой минимально воспроизводимый пример, который мы можем просто скопировать и вставить в наши IDE, чтобы увидеть то, что вы видите. Это означает, что вам следует удалить все, что не имеет прямого отношения к вашей проблеме, особенно если это вызывает несвязанные ошибки у других (например, импорт личных вещей, таких как ProductRow
и Checkbox}
).
@jcalz Спасибо за совет, я добавил еще несколько, чтобы прояснить использование ProductRow
, так как думаю, что это может быть полезно.
Похоже, это не имеет прямого отношения. Я вижу кучу ошибок и вы спрашиваете только об одной из них, верно? Пожалуйста, отредактируйте , чтобы создать минимально воспроизводимый пример, в котором присутствуют только те проблемы, о которых вы спрашиваете, чтобы любое предложение можно было проверить.
Было бы лучше, если бы вы просто скопировали и вставили код как есть. data.priceRecords[0].date
имеет незаконное значение, и я подозреваю, что вы намеревались использовать String
. Во-вторых, вы должны использовать разделители-запятые для отделения каждого поля от других в литерале объекта JavaScript и/или TypeScript. В-третьих, data
не объявляется перед присвоением. Приведенный выше пример наполнен ошибками. Пожалуйста, сделайте это минимально воспроизводимым примером .
Ссылка на игровую площадку повреждена, и если у вас есть способ отобразить ее на игровой площадке, отредактируйте , чтобы предоставить нам только этот код в виде открытого текста вместо показа многофайлового кода. Цель минимально воспроизводимого примера — дать людям возможность быстро копировать и вставлять в свои собственные IDE, чтобы увидеть то, что видите вы (ссылка на игровую площадку — хорошее дополнение, но ее недостаточно).
@jcalz Спасибо за совет, я добавил полезную ссылку на игровую площадку и отредактировал код, чтобы он был минимально воспроизводимым фрагментом кода.
Итак, должен ли T
быть типом data
или типом элементов data
? Должен ли T
иметь id
недвижимость? Что-то вроде этот подход может быть тем, что вы ищете, но вам нужно сообщить нам, каково ваше намерение с T
, здесь.
@jcalz, я попробовал это <T extends { id: string }>
, и это сработало, но я хочу посмотреть, можно ли вывести типы из данных, которые я передаю из реквизита, без необходимости их объявления
Хм, я тебя не понимаю, извини. Строка data.every(record => selectedRows.map(row => row.id).includes(record.id))
имеет смысл только в том случае, если data
имеет метод every
, что предположительно означает, что это массив, и если каждый элемент этого массива имеет свойство record
. Это означает, что вам нужно решить, что означает T
. Если T
действительно является типом data
, то, вероятно, вы хотите ограничить его {id: string}[]
в Header
, как показано здесь. Но вам нужно сообщить нам о своих намерениях. Я не уверен, как можно ожидать, что это будет выведено из чего-либо.
@jcalz, например, если я хочу добавить еще поля, такие как sku
или name
, тогда мне также придется включить их вот так <T extends { id: string, sku: string, name: string }>
тогда это как бы противоречит цели, заключающейся в том, чтобы typecrypt вывел это из данных, которые я передаю
Внутри Header
откуда он узнает, какие данные вы передаете? Разве это не зависит от того, как называется Header
? TypeScript не выводит параметры функции из набора всех сайтов вызовов. Да, если вы хотите, чтобы тело реализации Header
знало о sku
или name
, вам нужно упомянуть эти свойства в сигнатуре вызова, точно так же, как если вы хотите, чтобы function foo(x: unknown) {x.toUpperCase()}
работало, вам нужно будет написать x: string
вместо x.unknown
. TypeScript не может этого сделать.
(см. предыдущий комментарий) В любом случае, мне пора идти спать. Если вы хотите увидеть ответ с ограничениями на T
, я могу его написать, иначе, если вы ищете какой-то вывод, которого я не понимаю, возможно, придет кто-то еще.
@jcalz Спасибо за помощь!!
Эта конкретная концепция обсуждалась в руководстве в разделе Общие ограничения. [typescriptlang.org]
По сути, если вы хотите написать универсальную функцию, которая работает с набором типов, и у вас есть некоторые знания о том, какие возможности будет иметь этот набор типов, это совершенно нормально. Единственная проблема заключается в том, что компилятор не может доказать, что каждый тип имеет заданное свойство, поэтому он должен предупредить вас, что вы не можете сделать это предположение, что кажется разумным, потому что в вашем случае, что, если typeof data.priceRecords
не возвращает тип, который есть недвижимость every()
?
Концепция универсальных ограничений по сути заключается в том, что мы должны ограничить нашу универсальную процедуру для работы с любым типом T
, но для любого свойства, которое должно использоваться в T
, должно быть гарантировано существование этого свойства.
interface EveryWise {
every(callbackfn: (value, index: number, array) => boolean, thisArg?: any): boolean;
}
export const Header = <T extends EveryWise>({ data, selectedRows }: HeaderProps<T>) => { /* •••Rest of code••• */ }
Example of Generic Constraint. [1]
[1] The interface EveryWise
should be constrained even more with a type U
representing the type of value
but since the type of value
is left out, this is the strictest we can go. The reason for this mention is that record
is of type any
hence record.id
is used without type safety.
Какая строка выдает вам эту ошибку?