0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

extends keyofを使って汎用的なオブジェクト更新関数を作成する

Posted at

オブジェクトが入ったuseStateを更新したい

次のような型定義のオブジェクトがあったとする

type UserProfile = {
  name: string;
  email: string;
  age: number;
};

const [profile, setProfile] = useState<UserProfile>({
  name: '',
  email: '',
  age: 0,
});

更新関数を愚直に書いていく

これでも動きはしますが重複コードが多いです。フィールドが増えるたびに関数が増えていくのでメンテナンスも面倒くさそう。

// name用
const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  setProfile((prev) => ({ ...prev, name: e.target.value }));
};

// email用
const handleEmailChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  setProfile((prev) => ({ ...prev, email: e.target.value }));
};

// age用
const handleAgeChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  setProfile((prev) => ({ ...prev, age: Number(e.target.value) }));
};

共通の更新関数にまとめる

オブジェクト内のプロパティ名やプロパティの値の型が変更されたとしても、関数に変更は加えなくて良い保守性の高い関数になりました

  const handleProfileChange = <K extends keyof UserProfile>(
    field: K,
    value: UserProfile[K],
  ) => {
    setBlackboard((prev) => ({
      ...prev,
      [field]: value,
    }));
  };

  // 使用例
  <input
    type="text"
    value={profile.name}
    onChange={(e) => handleProfileChange('name', e.target.value)}
  />

解説

  • extends keyof Tでオブジェクトのプロパティを型にしている。それにextends で部分型制約をつけている
  • 型引数を使っているので、関数を使用するときに型を指定してやる必要がある。その指定する型に制約がついている
  • 制約というのは、UserProfile オブジェクトのプロパティ名のいずれかでなければならないというもの
  • オブジェクトのプロパティの型を取得することで、valueの型を定義することができる

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?