Глубокий ключ вложенного объекта в машинописном тексте

Я хочу создать собственную таблицу. Опция в ячейке и нижнем колонтитуле функции createColumnHelper должна возвращать правильный ключ/тип объекта вместо любого.

Например, у меня есть вложенный объект в моих ДАННЫХ. Или, если это не объект, он также должен возвращать правильный тип.

Прикрепил скриншоты того, как я вижу в IDE

Помощник.ts

import { ReactElement } from "react";

export type Column<T> = {
  header: string | (() => ReactElement);
  accessor: (row: T) => any;
  cell?:(prop: any) => ReactElement | string;
  footer?: (prop: any) => ReactElement | string;
};

export const createColumnHelper = <T>() => {
  return {
    accessor: (
      accessor: keyof T | ((row: T) => any),
      column: {
        header: string | (() => ReactElement);
        cell?: (prop: any) => ReactElement | string;
        footer?: (prop: any) => ReactElement | string;
      }
    ) => {
      return {
        header: column.header,
        accessor: (row: T) => (typeof accessor === "function" ? accessor(row) : row[accessor]),
        cell: column.cell,
        footer: column.footer,
      } as Column<T>;
    }
  };
};

ТаблицаКомпоненты.tsx

import React from "react";
import { createColumnHelper } from "./columnHelper";

type Person = {
  firstName: string
  lastName: string
  age: number
  visits: number
  status: string
  progress: {
    ok?: number;
    no?: string
  }
}

const DATA: Person[] = [
  {
    firstName: 'tanner',
    lastName: 'linsley',
    age: 24,
    visits: 100,
    status: 'In Relationship',
    progress: {
      ok: 50
    },
  },
  {
    firstName: 'tandy',
    lastName: 'miller',
    age: 40,
    visits: 40,
    status: 'Single',
    progress: {
      no: 'bad'
    },
  },
]

const columnHelper = createColumnHelper<Person>();

const columns = [
  columnHelper.accessor(
    'firstName', {
    header: 'First Name',
    cell: val => val,
  }),
  columnHelper.accessor(
    'progress', {
      header: 'Progress',
      cell: prop => prop.ok,
    }),
  columnHelper.accessor(row => row, {
    header: () => <span>Age</span>,
    cell: prop => <span>{prop.age}</span>,
  }),
];

const TableComponent: React.FC = () => {
  return (
    <table>
      <thead>
      <tr>
        {columns.map((column, index) => (
          <th key = {index}>
            {typeof column.header === "function" ? column.header() : column.header}
          </th>
        ))}
      </tr>
      </thead>
      <tbody>
      {DATA.map((row, rowIndex) => (
        <tr key = {rowIndex}>
          {columns.map((column, colIndex) => (
            <td key = {colIndex}>
              {column.cell ? column.cell(column.accessor(row)) : column.accessor(row)}
            </td>
          ))}
        </tr>
      ))}
      </tbody>
      <tfoot>
      <tr>
        {columns.map((column, index) => (
          <td key = {index}>
            {column.footer ? column.footer({ column }) : null}
          </td>
        ))}
      </tr>
      </tfoot>
    </table>
  );
};

export default TableComponent;

Но вы используете type Column::cell: (prop: any) => ••• в объявлении. Вы хотите напечатать его по-другому, поэтому придайте ему тот тип, который вам нужен. Если я не дошел до вашей точки зрения, я расширю ее.

Chukwujiobi Canon 23.06.2024 19:38

У вас там много any, и если вы начнете давать ему правильные типы, то TS расстроится из-за чего-то. Вероятно, вам нужно Column<T, V>, где V — тип вывода accessor и ввода cell/footer. Разделяя разницу между фактической безопасностью типов и всеми any, я полагаю, вы можете написать эту ссылку на игровую площадку, но это просто переместит any в следующую часть вашего кода. Соответствует ли это вашим потребностям? Если да, то я напишу ответ; если нет, то что мне не хватает?

jcalz 23.06.2024 20:06

@jcalz, да, похоже, это то, что мне нужно. Тип вывода средства доступа и ввода ячейки/нижнего колонтитула. Спасибо

IncognitoUser 24.06.2024 10:23
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
3
71
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Поскольку вас волнует тип ввода cell/footer и тип вывода accessor, вам следует рассмотреть возможность добавления общего параметра типа 🔁 в Column для этого типа:

type Column<T, V> = {
  header: string | (() => ReactElement);
  accessor: (row: T) => V;
  cell?: (prop: V) => ReactElement | string;
  footer?: (prop: V) => ReactElement | string;
};

Тогда значение, возвращаемое createColumnHelper<T>(), должно иметь метод accessor, который либо принимает значение типа K extends keyof T и выдает Column<T, T[K]>, либо принимает значение типа (row: T) => V для некоторых V и выдает Column<T, V>. Это выглядит так:

const createColumnHelper = <T,>() => {
  // call signatures
  function accessor<K extends keyof T>(
    accessor: K, column: Omit<Column<T, T[K]>, "accessor">
  ): Column<T, T[K]>;
  function accessor<V>(
    accessor: (row: T) => V, column: Omit<Column<T, V>, "accessor">
  ): Column<T, V>;

  // implementation
  function accessor(
    accessor: keyof T | ((row: T) => any), column: Omit<Column<T, any>, "accessor">
  ): Column<T, any> {
    return {
      header: column.header,
      accessor: (row: T) => (typeof accessor === "function" ? accessor(row) : row[accessor]),
      cell: column.cell,
      footer: column.footer,
    };
  }
  return { accessor };
};

Обратите внимание, что я использую Пропустить, чтобы легко описать Column, у которого отсутствует свойство accessor. Это не обязательно, но позволяет сэкономить время на написании {header: ⋯, cell?: ⋯, footer?: ⋯}. Это устраняет проблему, с которой вы столкнулись:

const columns = [
  columnHelper.accessor(
    'firstName', {
    header: 'First Name',
    cell: val => val,
  }),
  columnHelper.accessor(
    'progress', {
    header: 'Progress',
    cell: prop => String(prop.ok), 
  }),
  columnHelper.accessor(row => row, {
    header: () => <span>Age</span>,
    cell: prop => <span>{prop.age}</span>,
  }),
];

(и обратите внимание, что мне пришлось исправить ошибку, поскольку prop.ok — это не string или ReactElement.)


Это отвечает на заданный вопрос, но если вы это сделаете, у вас возникнут проблемы с column.cell ? column.cell(column.accessor(row)) : column.accessor(row) и тому подобным. Единственная причина, по которой раньше это работало, заключается в том, что column.cell() принимает any. Теперь это не так. И TS не может проверить это как действительное из-за нескольких высказываний column типа объединения. Это ms/TS#30581 более или менее, и исправления для него выходят за рамки этого вопроса.

Самый простой вариант — потребовать, чтобы columnHelper.accessor(a, o) входные данные выглядели как Omit<Column<T, T[K]>, "accessor"> или Omit<Column<T, V>, "accessor">, но возвращали Column<T, any>:

const createColumnHelper = <T,>() => {
  // call signatures
  function accessor<K extends keyof T>(
    accessor: K, column: Omit<Column<T, T[K]>, "accessor">
  ): Column<T, any>;
  function accessor<V>(
    accessor: (row: T) => V, column: Omit<Column<T, V>, "accessor">
  ): Column<T, any>;

  // implementation
  function accessor(
    accessor: keyof T | ((row: T) => any), column: Omit<Column<T, any>, "accessor">
  ): Column<T, any> {
    return {
      header: column.header,
      accessor: (row: T) => (typeof accessor === "function" ? accessor(row) : row[accessor]),
      cell: column.cell,
      footer: column.footer,
    };
  }
  return { accessor };
};

Это облегчит сопоставление, но проблема any в выводе все еще сохраняется:

columns[0].cell?.(1) // no error

Вероятно, это должно привести к ошибке «number не может быть присвоено {ok?: number, no?: string}». Но это не так. Ну ладно, вы не спрашивали об этом, и исправление этого потребовало бы еще большего рефакторинга.

Детская площадка, ссылка на код

Спасибо! Можно ли везде убрать какие-то типы? если в Person будут все строки? { ok?: string, no?: string }

IncognitoUser 25.06.2024 15:55

Проблема any не имеет ничего общего со строками. Это связано с column.cell(column.accessor(row)) и ms/TS#30581. Чтобы справиться с этим, потребуется целый ряд рефакторингов, но здесь это выходит за рамки.

jcalz 25.06.2024 15:57

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

Nextjs useSearchParams не обновляется правильно
Main.js содержит html при развертывании, что приводит к пустой странице
Как визуализировать JSX, возвращаемый асинхронной функцией, вызванной изнутри Array.Map() в React?
Реакция: сброс группы переключателей пользовательского интерфейса материала в форме Formik не работает
Как создать универсальный крючок для извлечения данных в TypeScript для API с различными структурами ответов?
Можно ли надежно буферизовать исходящие POST-запросы из автономного React PWA?
Обертка с помощью React.StrictMode в Next.js
Как я могу создать переводчик, который конвертирует язык моего динамического сайта с английского на французский?
Использование NPM для создания приложения React: СЕРЬЕЗНОЕ ИЗМЕНЕНИЕ: webpack <5 используется для включения полифилов для основных модулей node.js по умолчанию. Это уже не так
Как смоделировать функцию внутри компонента реагирования с помощью Jest?