Задать вопрос
@Kermet123

Как типизировать хук useState, если в качестве возвращаемым значений у нас ключи объекта?

Все доброго времени суток.
Есть компонент Posts, который отрисовывает список постов в компоненте PostList.
Структура поста примитивная - id, title, body.
В состоянии, в значении по умолчанию, сразу прописано несколько постов. В значении title и body прописаны буквы, это сделано для наглядности, при сортировке.
В состоянии selectedSort храним параметр для выбора сортировки, тип строка. Это title или body. Т.е. сортировка либо по заголовку, либо по содержимому поста (они же являются ключами в объекте post)
Вопрос: как типизировать хук и функцию sortPosts?
Все мои попытки не увенчались успехом. Самый очевидный на мой взгляд вариант:
useState('')
не работает и выдает ошибку:
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'IPost'.
No index signature with a parameter of type 'string' was found on type 'IPost'
Почему тут говориться, что элемент selectedSort имеет неявный тип any, тогда как я указал в useState(), что тип - строка.
Код компонента:
export interface INewPost {
  title: string
  body: string
}

export const Posts = () => {
  const [selectedSort, setSelectedSort] = useState<string>('')

  const [posts, setPosts] = useState<IPost[]>([
    { id: 1, title: 'b', body: 'b' },
    { id: 2, title: 'd', body: 'd' },
    { id: 3, title: 'a', body: 'c' },
    { id: 4, title: 'c', body: 'a' },
  ])

  const sortPosts = (sort: string) => {
    console.log(sort)
    setSelectedSort(sort)
    setPosts(
      [...posts].sort((a, b) => a[selectedSort].localeCompare(b[selectedSort]))
    )
  }

  const addPost = (post: INewPost) => {
    const newPost = {
      id: Date.now(),
      ...post,
    }
    setPosts([...posts, newPost])
  }

  const deletePost = (id: number) => {
    setPosts([...posts.filter((post) => post.id !== id)])
  }

  return (
    <div className={s.posts}>
      <h1>Post list</h1>
      <PostForm create={addPost} />
      <PostList posts={posts} sortPosts={sortPosts} remove={deletePost} />
    </div>
  )
}


Интерфейс поста (который в состоянии):
export interface IPost {
  id: number
  title: string
  body: string
}


Интерфейс INewPost, это для нового поста, при создании. Отличается от IPost только отсутствием id.
  • Вопрос задан
  • 63 просмотра
Подписаться 1 Простой Комментировать
Пригласить эксперта
Ответы на вопрос 3
Aetae
@Aetae Куратор тега TypeScript
Тлен
keyof IPost
Ответ написан
Alexandroppolus
@Alexandroppolus
кодир
1) Тебе надо взять только те ключи, значения по которым строки. Из коробки такого type-util нет, пишем свой.
2) Функция sortPosts не нужна. Не надо сортировать набор, когда выбираем способ сортировки. Для этого используем отдельное вычисляемое значение - сортированный набор.
3) в PostList тоже надо бы прокидывать текущий selectedSort, и использовать там тип SortKeys (см. ниже)

type GetStrKeys<T> = keyof {[K in keyof T as T[K] extends string ? K : never]: T[K]};

type SortKeys = GetStrKeys<IPost>;

.......

const [selectedSort, setSelectedSort] = useState<SortKeys>(); // SortKeys | undefined;

const sortedPosts = useMemo(() => selectedSort
    ? [...posts].sort((a, b) => a[selectedSort].localeCompare(b[selectedSort]))
    : posts,
  [posts, selectedSort]
);

.....

<PostList posts={sortedPosts} sortPosts={setSelectedSort} remove={deletePost} />
Ответ написан
Комментировать
@studentRrontend
Или перепиши функцию с явными проверками типов:
const sortPosts = (sort: string) => {
setSelectedSort(sort);
setPosts(
[...posts].sort((a, b) => {
const aValue = a[sort as keyof IPost];
const bValue = b[sort as keyof IPost];

if (typeof aValue === 'string' && typeof bValue === 'string') {
return aValue.localeCompare(bValue);
} else if (typeof aValue === 'number' && typeof bValue === 'number') {
return aValue - bValue;
} else return 0;
})
);
};
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы