🧐

Typescript や分割代入を使った複雑な引数を解読する

2024/06/25に公開

はじめに

分割代入はJavaScriptの機能で、オブジェクトのプロパティを個別の変数として取り出すことができる機能です。例えば

const user = { name: 'Bob', age: 20 };
// これを name = 'Bob', age = 20 という変数として取り出したい場合以下のようにして取ることができる

const name = user.name
const age = user.age

// これを分割代入を使うことで、以下のように変数を定義できる
const { name, age } = user;
console.log(name); // 'Bob'
console.log(age); // 20

これ位であればわかりやすいのですが、 Reactのpropsや、typescriptの型情報、別名での変数名定義などが組み合わさった複雑な構造をしていると、中々理解が難しいです。

例えば以下のようなコード。脳がバグります。

export function TodoItem({
  todoList: {
    hoge: { foo: todoList },
  },
}: {
  todoList: { hoge: { foo: TodoItemType[] } };
}) {
  return (
    <div>
      {todoList.map((todo) => {
        return (
          <div key={todo.id}>
            <input type="checkbox" checked={todo.completed} />
            {todo.task}
          </div>
        );
      })}
    </div>
  );
}

なので、一つずつ分解して整理しながら上記を例に紐解いてみます。(return 文より上について書きますので下は無視でOKです。)

コンポーネントの構造

まず、コンポーネントの引数の中ですが、構造的には以下のようになっています。

functuon  関数名( 引数の値 :)

これに沿って、分解すると以下のようになります。

export function TodoItem(
// ここから
{
  todoList: {
    hoge: { foo: todoList },
  },
}
// ここまでが引数部分で
 :{
  todoList: { hoge: { foo: TodoItemType[] } };
}) {
// ここまでが型情報になる。

型の部分

型の方はまだわかりやすいです。

// 型の形。引数は以下と同じ構造を持っているということを示している。
todoList:{
 hoge: {
   foo: TodoItemType[]
    }
   }
// 型の中身は以下のようなイメージ。
todoList.hoge.foo:TodoItemType[]

// 補足: TodoItemType[] は例えば以下のようなオブジェクトの配列であることを示している。このオブジェクトを配列で持っている
type TodoItemType = {id:number,task:string, isChecked: boolean}
 

引数の部分

引数の方ですが、少しややこしいです。

// 引数を一つずつ分割代入する形で、かつpropsとして引数を受け取る形
function TodoItem(props:Props){
  const { todoList } = props
  const { hoge } = todoList
  const { foo } = hoge
  }
  
// 次にコンポーネントの中でまとめてに分割代入をすると
function TodoItem(props:Props){
  const { todoList: { hoge : { foo } } = props
  }

// さらにこの foo を foo ではなく  todoList という名前に変更して取り出したい場合
function TodoItem(props:Props){
  const { todoList: { hoge : { foo : todoList } } = props
  }

// これを 引数の中で受け取る形に変更するとこうなる
function TodoItem({ todoList: { hoge : { foo : todoList } }:Props){
  }

つまり、todoList オブジェクトの中の、hoge オブジェクトの中の、foo の値を todoListという名前で定義された変数として取り出している、ということになります。

分割代入は今回のような引数に限らず色々な場所で使われるのですが、

  • の後に { } があれば分割代入されていて
  • の後に{ } ではなく値が来てたら、それは取り出した変数に名前をつけている

と覚えるようにすると良さそうです。

ここに、今Props 型になっている箇所に先ほどの型情報をくっつけると、最初の形が完成します。

export function TodoItem({
  todoList: {
    hoge: { foo: todoList },
  },
}: {
  todoList: { hoge: { foo: TodoItemType[] } };
})

まとめ

このような感じで、一つずつ理解のために分解して整理してみることで理解が深まりました。

そもそも、分割代入をここまで掘ることは中々ないんじゃないかと思いますし、型も直接書かずに type とかで定義した方が見やすくなるので、こういったわかりにくい書き方は避けた方が良さそうです。

分割代入の構造は、自分はまだまだ見慣れていないので混乱するのですが、見える人には見えているんだろうなと思いました。

Discussion