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

Здравствуйте! У меня есть вопрос по типизации в TypeScript. У меня есть компонент Posts, который отображает список постов в PostList. Каждый пост состоит из трех полей: id, title и body. В состоянии по умолчанию я заранее определил несколько постов, в полях title и body находятся буквы для удобства просмотра при сортировке. 

В состоянии selectedSort я храню параметр, который определяет, по какому критерию происходит сортировка: title или body. Это значит, что сортировка ведется либо по заголовку, либо по содержимому поста, которые выступают в роли ключей в объекте post.

У меня возникла проблема с типизацией хука и функции sortPosts. Все мои попытки не приводили к успеху. Я использовал тип <string> в 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 – строка? Можете помочь с типизацией хуков и функции sortPosts? 

Вот код компонента: 
<pre><code class="typescript">export interface INewPost {
  title: string
  body: string
}

export const Posts = () =&gt; {
  const [selectedSort, setSelectedSort] = useState&lt;string&gt;('')

  const [posts, setPosts] = useState&lt;IPost[]&gt;([
    { 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) =&gt; {
    console.log(sort)
    setSelectedSort(sort)
    setPosts(
      [...posts].sort((a, b) =&gt; a[selectedSort].localeCompare(b[selectedSort]))
    )
  }

  const a...
  • 21 декабря 2024 г. 13:53
Ответы на вопрос 2
Причина, по которой вы получаете ошибку в TypeScript, связана с использованием переменной `selectedSort` в качестве ключа для индексации объектов типа `IPost`. Вы типизируете `selectedSort` как `string`, но TypeScript не гарантирует, что значение `selectedSort` будет одним из ключей интерфейса `IPost`. Давайте рассмотрим, как это можно исправить.

Во-первых, вам следует определить, какие ключи будут использоваться для сортировки. Вы можете использовать строковый литеральный тип, чтобы ограничить возможные значения `selectedSort` до `'title'` или `'body'`. Таким образом, вы сможете дать TypeScript понять, что `selectedSort` всегда будет одним из этих значений.

Вот как можно изменить ваш код:

```typescript
export interface IPost {
  id: number;
  title: string;
  body: string;
}

type SortKey = 'title' | 'body';

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

  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: SortKey) => {
    console.log(sort);
    setSelectedSort(sort);
    setPosts(
      [...posts].sort((a, b) => a[sort].localeCompare(b[sort]))
    );
  }

  // Остальной код компонента
};
```

### Изменения, которые я внёс:
1. Я добавил тип `SortKey`, который представляет собой объединение строковых литералов `'title'` и `'body'`. Теперь `selectedSort` и параметр `sort` функции `sortPosts` имеют этот тип.
  
2. В вызове `sort` внутри функции `sortPosts` используется тот же ключ, что и переданный в качестве параметра, что исключает возможность использования потенциально недопустимого значения.

Теперь TypeScript сможет проверить, что значением `selectedSort` и параметра `sort` могут быть только `'title'` или `'body'`, и не будет выдавать ошибку при индексации объекта `IPost`.
keyof IPost
Похожие вопросы