NK-dev
@NK-dev (N K)

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

Recoilのselector内でget関数でエラーが出る

解決したいこと

selectorで全タスク、完了タスク、未完了タスクの数を参照して、出力したい。selectorを管理しているファイル内のget関数でオーバーロードのエラーが出されてしまう。

発生している問題・エラー

(property) ReadOnlySelectorOptions<number>.get: (opts: {
    get: GetRecoilValue;
    getCallback: GetCallback;
}) => number | Promise<number> | RecoilValue<number> | Loadable<number> | WrappedValue<...>
この呼び出しに一致するオーバーロードはありません。
  2 中 1 のオーバーロード, '(options: ReadWriteSelectorOptions<number>): RecoilState<number>' により、次のエラーが発生しました。
    型 '({ get }: { get: GetRecoilValue; getCallback: GetCallback; }) => { totalNum: number; totalCompletedNum: number; totalUnCompletedNum: number; }' を型 '(opts: { get: GetRecoilValue; getCallback: GetCallback; }) => number | Promise<number> | RecoilValue<number> | Loadable<number> | WrappedValue<...>' に割り当てることはできません。

selector.tsでセレクターを管理しており、returnにおいてオブジェクトで複数の値を返すと上記エラーが出現してしまう。

該当するソースコード

selector.ts
import { selector } from 'recoil';
import { todoListState } from './atom';

export const todoListStatusState = selector<number>({
  key: 'todoListStatusState',
  get: ({ get }) => { //NG
    const todoList = get(todoListState);
    const totalNum = todoList.length;
    const totalCompletedNum = todoList.filter((item) => item.isComplete).length;
    const totalUnCompletedNum = totalNum - totalCompletedNum;
    return { totalNum, totalCompletedNum, totalUnCompletedNum };
  },
});
TodoListStatus.tsx
//こちらでもuseRecoilValueで参照しようとしてもselectorでエラーが出ているため、できない
import React from 'react';
import { useRecoilValue } from 'recoil';
import { todoListStatusState } from '../State/selector';

const TodoListStatus = () => {
  const { totalNum, totalCompletedNum, totalUnCompletedNum } =
    useRecoilValue(todoListStatusState);

  return (
    <div>
      <ul>
        <li>Todoの登録数: {totalNum}</li>
        <li>完了数: {totalCompletedNum}</li>
        <li>未完了数: {totalUnCompletedNum}</li>
      </ul>
    </div>
  );
};

export default TodoListStatus;
atom.ts
import { atom } from 'recoil';
import { DATA } from '../types/DataType';

export const todoListState = atom<Array<DATA>>({
  key: 'todoListState',
  default: [
    {
      id: 0,
      title: '送信設定',
      isComplete: false,
    },
  ],
});
DataType.ts
export type DATA = {
  id: number;
  title: string;
  isComplete: boolean;
};

export type ITEMPROPS = {
  todo: DATA;
};

自分で試したこと

今回はRecoilのドキュメントを参考にして、記述していました。
オーバーロードということだったので、TypeScriptでのオーバーロードについて調査した。
こちらの記事

関数の引数に複数の型を定義することだそうだ。

という文言を拝見して、設定した型の指定仕方に問題があるのかなと考えた。

selectorメソッドに型を指定するのに、リテラル型にすればいいのかなと推測。しかし、具体的な型が分からずanyにすることで一時退避はできたが、TypeScriptの強みを消してしまっているので、anyは避けたい(一時的に全てのエラーは無くなった)。

selector.ts
import { selector } from 'recoil';
import { todoListState } from './atom';

export const todoListStatusState = selector<number | any>({
  key: 'todoListStatusState',
  get: ({ get }) => {
    const todoList = get(todoListState);
    const totalNum = todoList.length;
    const totalCompletedNum = todoList.filter((item) => item.isComplete).length;
    const totalUnCompletedNum = totalNum - totalCompletedNum;
    return { totalNum, totalCompletedNum, totalUnCompletedNum };
  },
});
0

1Answer

return { totalNum, totalCompletedNum, totalUnCompletedNum };

で返されるのは{ totalNum:number, totalCompletedNum:number, totalUnCompletedNum:number }型になります.

詳しいことは「オブジェクト型」や「Generics」を調べるとご理解頂けるかと思います.

selectorは以下リンク先の通り定義されています.

1Like

Comments

  1. @NK-dev

    Questioner

    お忙しい中返答ありがとうございます。
    返される値の構造まで理解することはできました。しかし、記載いただいたRecoilのselectorに定義されている型定義を見たところ、返される値もTで返されることから、今回もnumberで返されると思いました。TodoListStatus.tsxで{ totalNum, totalCompletedNum, totalUnCompletedNum }を呼び出すとanyになってしまいます。これはどこを理解すればエラー解決に繋がるか教えていただけないでしょうか。
  2. Genericsの型指定はあくまでユーザに型を強制するもので,自動的にキャストしたりするものではありません.
    Tにnumberを指定するならgetは自分でnumberを返さなければなりません.
    基本的にJS/TSに複値返却の概念はなく,ObjectかArrayで代用する必要があります.

Your answer might help someone💌