Problem
次の関数changeValueByKey
ようにObjectのkeyによってvalueを変更する関数を型安全に作りたい!
type State = {
text: string;
isActive: boolean;
};
let state: State = {
text: "",
isActive: false
}
const changeText = (value: string) => {
// OK
state['text'] = value;
}
const changeIsActive = (value: boolean) => {
// OK
state['isActive'] = value;
}
const changeValueByKey = (key: 'text' | 'isActive', value: string | boolean) => {
// Type Error
state[key] = value;
}
Solution
- こちらのStack Overflowを参考にしました
type State = {
text: string;
isActive: boolean;
};
let state: State = {
text: "",
isActive: false
}
const changeValueByKey = <K extends keyof State>(key: K, value: State[K]) => {
state[key] = value;
}
// OK
changeValueByKey('text', 'test')
changeValueByKey('isActive', true)
// Type Error
changeValueByKey('text', true)
changeValueByKey('isActive', 'test')
- 解説
-
keyof
を使うことでプロパティ名のtypeを参照することができます -
keyof State
でStateのプロパティ名を参照し、それをextends
継承したtypeK
をGenericsとして用意します - 第一引数
key
はプロパティ名なのでそのままK
を使用し、第二引数value
はStateのプロパティK
のvalueの型なのでState[K]
とします - これでkeyは
text | isActive
となり、keyを指定した時にvalueの型が決まるようになります - keyに
text
を指定した時にvalueはState['text']
なのでstring
に、isActive
を指定した時にvalueはState['isActive']
なのでboolean
しか渡せないようになりました
-
- 今すぐ動作を確認してみたい方はこちら
TypeScriptに対応したEditorならサジェストも出ます
Demo
- Reactの
useState
にObjectを入れて実際に使ってみた時のサンプルはこちら
See the Pen Demo: change object value type by key by mikan3rd (@mikan3rd) on CodePen.
↑ Code SandboxはQiitaに埋め込みできないようなのでCodePenを埋め込んであります
(ただしCodePenではType Errorなどは出ません・・・)