TL;DL
interface IHoge {
A: string,
B: number,
C: boolean
}
const Hoge: IHoge = {
A: "",
B: 0,
C: true
}
const h = (p: keyof IHoge) => {
return p in Hoge ? Hoge[p] : undefined;
}
type IHogeValues = NonNullable<ReturnType<typeof h>>;
// IHogeValues = string | number | boolean
追記 2020/06/25
コメントで教えていただきました。@nagtkk さん、ありがとうございます😭
インターフェースから
interface IHoge {
A: "a",
B: "b",
C: "c"
}
type IHogeValues = IHoge[keyof IHoge];
オブジェクトから
const Hoge = <const>{
A: "a",
B: "b",
C: "c"
};
type IHogeValues = (typeof Hoge)[keyof typeof Hoge];
なぜつくるのか
こんなことをしたかった
const Hoge = {
A: "a",
B: "b",
C: "c"
}
function Say(t: "a" | "b" | "c") {
console.log(t);
}
Say(Hoge.A); // -> a
Say("b"); // -> b
Say("d"); // error
ライブラリが提供する列挙型だけ受け付ける関数をつくりたかった
しかし
このやり方の場合確かに問題なく動作するが、列挙型の中身が増減するたびに該当箇所を書き換える必要が出てくる → 追記漏れが怖い😭
interface から union をつくる
完成コード
interface IHoge {
A: "a",
B: "b",
C: "c"
}
const Hoge: IHoge = {
A: "a",
B: "b",
C: "c"
}
const h = (p: keyof IHoge) => {
return p in Hoge ? Hoge[p] : undefined;
}
type IHogeValues = NonNullable<ReturnType<typeof h>>;
// IHogeValues = "a" | "b" | "c"
function Say(t: IHogeValues) {
console.log(t);
}
Say(Hoge.A); // -> a
Say("b"); // -> b
Say("d"); // error
解説
ReturnType
ReturnType
は与えた関数の情報を元に、その関数が返す値の型情報を取得します。
そのため、上記コード内に出てきた関数h
では、IHoge
内のキー名を受け取る引数p
をインデックスとしHoge
から対応する値を返し、存在しない場合はundefined
を返します。
❗️p は動的に決まるため仕様上return Hoge[p]
とは書けません。(keyof IHogeしているのに。。。)
そのため関数h
が返す値は"a" | "b" | "c" | undefined
になります。
NonNullable
NonNullable
は受け取ったTypeからundefined
とnull
を除いた値を返します。
そのためReturnType
から受け取った"a" | "b" | "c" | undefined
を"a" | "b" | "c"
にすることが出来ます。
あとがき
これで追記漏れなどもなくなるので安心ですね!