Машинописный текст говорит, что собственность не существует

Я новичок в общем/типовом криптировании.

Контекст: я пытаюсь передать данные со страниц (page.tsx) в общие компоненты (header.tsx). Данные извлекаются из функции реагирования-запроса.

Проблема: в файле header.tsx, компоненте Checkbox, Typescrypt говорит:

Свойство каждый не существует в типе T

Параметр «запись» неявно имеет тип «любой».

И я совершенно не понимаю, почему это тот случай, когда передаваемые данные гарантируют, что они являются массивом и существуют.

Что я подумал: TypeScript также уведомляет, что запись имеет любой тип, но я думал, что, поскольку компонент не распознает тип свойства «данных», если бы я мог каким-то образом получить тип для «данных», prop в headers.tsx, TypeScript сможет его вывести.

Я прикрепил ссылку на детскую площадку .

типы.ts

export type ProductRow = {
    id: string;
    name?: string;
    sku?: string;
};

страница.tsx

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>
  )
}

заголовок.tsx

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>
  )
};

Какая строка выдает вам эту ошибку?

Dai 23.08.2024 04:01

это потому, что у вас есть data.priceRecords = {, у которого есть подраздел priceRecords? то есть data.priceRecords.priceRecords = [] но data.priceRecords = {}

dave 23.08.2024 04:03

@dave Привет, я допустил опечатку в образце данных, у него нет подраздела.

Jamie Nguyen 23.08.2024 04:05

@Dai В header.tsx checked = {priceRecords.every(record => selectedRows.map(row => row.id).includes(record.id))} отображается сообщение об ошибке.

Jamie Nguyen 23.08.2024 04:06

Я предполагаю, что ничто не ограничивает ваш универсальный T-тип массивом, поэтому он не обязательно может иметь метод Every.

Nick Parsons 23.08.2024 04:08

Пожалуйста, отредактируйте так, чтобы ваш код представлял собой минимально воспроизводимый пример, который мы можем просто скопировать и вставить в наши IDE, чтобы увидеть то, что вы видите. Это означает, что вам следует удалить все, что не имеет прямого отношения к вашей проблеме, особенно если это вызывает несвязанные ошибки у других (например, импорт личных вещей, таких как ProductRow и Checkbox}).

jcalz 23.08.2024 04:35

@jcalz Спасибо за совет, я добавил еще несколько, чтобы прояснить использование ProductRow, так как думаю, что это может быть полезно.

Jamie Nguyen 23.08.2024 04:46

Похоже, это не имеет прямого отношения. Я вижу кучу ошибок и вы спрашиваете только об одной из них, верно? Пожалуйста, отредактируйте , чтобы создать минимально воспроизводимый пример, в котором присутствуют только те проблемы, о которых вы спрашиваете, чтобы любое предложение можно было проверить.

jcalz 23.08.2024 04:50

Было бы лучше, если бы вы просто скопировали и вставили код как есть. data.priceRecords[0].date имеет незаконное значение, и я подозреваю, что вы намеревались использовать String. Во-вторых, вы должны использовать разделители-запятые для отделения каждого поля от других в литерале объекта JavaScript и/или TypeScript. В-третьих, data не объявляется перед присвоением. Приведенный выше пример наполнен ошибками. Пожалуйста, сделайте это минимально воспроизводимым примером .

Chukwujiobi Canon 23.08.2024 04:56

Ссылка на игровую площадку повреждена, и если у вас есть способ отобразить ее на игровой площадке, отредактируйте , чтобы предоставить нам только этот код в виде открытого текста вместо показа многофайлового кода. Цель минимально воспроизводимого примера — дать людям возможность быстро копировать и вставлять в свои собственные IDE, чтобы увидеть то, что видите вы (ссылка на игровую площадку — хорошее дополнение, но ее недостаточно).

jcalz 23.08.2024 05:22

@jcalz Спасибо за совет, я добавил полезную ссылку на игровую площадку и отредактировал код, чтобы он был минимально воспроизводимым фрагментом кода.

Jamie Nguyen 23.08.2024 05:47

Итак, должен ли T быть типом data или типом элементов data? Должен ли T иметь id недвижимость? Что-то вроде этот подход может быть тем, что вы ищете, но вам нужно сообщить нам, каково ваше намерение с T, здесь.

jcalz 23.08.2024 05:49

@jcalz, я попробовал это <T extends { id: string }>, и это сработало, но я хочу посмотреть, можно ли вывести типы из данных, которые я передаю из реквизита, без необходимости их объявления

Jamie Nguyen 23.08.2024 05:54

Хм, я тебя не понимаю, извини. Строка data.every(record => selectedRows.map(row => row.id).includes(record.id)) имеет смысл только в том случае, если data имеет метод every, что предположительно означает, что это массив, и если каждый элемент этого массива имеет свойство record. Это означает, что вам нужно решить, что означает T. Если T действительно является типом data, то, вероятно, вы хотите ограничить его {id: string}[] в Header, как показано здесь. Но вам нужно сообщить нам о своих намерениях. Я не уверен, как можно ожидать, что это будет выведено из чего-либо.

jcalz 23.08.2024 05:54

@jcalz, например, если я хочу добавить еще поля, такие как sku или name, тогда мне также придется включить их вот так <T extends { id: string, sku: string, name: string }> тогда это как бы противоречит цели, заключающейся в том, чтобы typecrypt вывел это из данных, которые я передаю

Jamie Nguyen 23.08.2024 05:56

Внутри Header откуда он узнает, какие данные вы передаете? Разве это не зависит от того, как называется Header? TypeScript не выводит параметры функции из набора всех сайтов вызовов. Да, если вы хотите, чтобы тело реализации Header знало о sku или name, вам нужно упомянуть эти свойства в сигнатуре вызова, точно так же, как если вы хотите, чтобы function foo(x: unknown) {x.toUpperCase()} работало, вам нужно будет написать x: string вместо x.unknown. TypeScript не может этого сделать.

jcalz 23.08.2024 06:00

(см. предыдущий комментарий) В любом случае, мне пора идти спать. Если вы хотите увидеть ответ с ограничениями на T, я могу его написать, иначе, если вы ищете какой-то вывод, которого я не понимаю, возможно, придет кто-то еще.

jcalz 23.08.2024 06:00

@jcalz Спасибо за помощь!!

Jamie Nguyen 23.08.2024 06:02
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
0
18
53
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Эта конкретная концепция обсуждалась в руководстве в разделе Общие ограничения. [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.

Другие вопросы по теме