TypeScriptで書いているとたまにこの時はどうするんだっけ?ってが止まることがありますよね
今回は私が普段の実装している時に??って手が止まった時のことをまとめてみました
1. Object.keys使ったループ処理
Object.keysはstring[]を返却するためstring型では列挙型のindexとは型が不一致なためエラーになります
const obj = {
aaa: '0',
bbb: '1',
ccc: '2'
}
// error 型 'string' のパラメーターを持つインデックス シグネチャが型に見つかりませんでした。
Object.keys(obj).map((key) => Number(obj[key]))
解決策
どこかで型を明示的に変えてあげれば解決します。
① 宣言しているオブジェクトの型を{ [key: string]: string }で定義する
上記の宣言だと列挙型になってしまうので列挙型ではなくインデックスシグネチャの型定義をしてあげれば参照できるようになります。
const obj: { [key: string]: string } = {
aaa: '0',
bbb: '1',
ccc: '2'
}
② Object.keysで取得した値をアサーションする
(Object.keys(obj) as Array<keyof typeof obj>).map((key) => Number(obj[key]))
// or
Object.keys(obj).map((key) => Number(obj[(key as keyof typeof obj)]));
何をやっているかというとobjをtypeofでいったん
{
aaa: string,
bbb: string,
ccc: string
}
このようなtypeにしてからkeyofで
aaa | bbb | ccc
keyだけの型定義にして結果をObject.keysの戻り値もしくはmapの第一引数のkeyにアサーションしています。
ちなみにObject.keysはstring[]を返却するため以下のようにアサーションするのはできません。
Object.keys(obj).map((key: keyof typeof obj) => Number(obj[key]));
また、アサーションをObject.keysするたびに行うのはスマートではないのでwrapperしてkeyのunion typeが返ってくるような関数を作成するのも手です。
const wrapObjectKeys = <T>(obj: T) => Object.keys(obj) as Array<keyof T>;
wrapObjectKeys(obj).map((key) => Number(obj[key]));
2.ライブラリの型定義にないフィールドを参照したい
稀によくあるのですがライブラリの関数の戻り値の型をconsole.logなどでみると型定義には存在しないフィールドがいて自分がその値を参照したいけどそのまま参照するとerrorになってしまいます。
export type Event {
value: string;
}
interfact Props {
onClick: (event: Event) => void
}
export const Component: React.FC<ITextBoxProps> = (props: Props ) => {
return <div {...props} />
}
こんな型定義のコンポーネントを提供しているライブラリがあるとして
import { Component, Event } from 'Component'
const onClickEvent = (event: Event) => {
console.log(event)
}
return (
<Component onClick={onClickEvent} />
)
これでコンソールログで値を見ると { value: string, beforeValue: string }
となっていてbeforeValueを参照したいのに型定義上には存在しないため以下のようにeventからbeforeValueにアクセスするとエラーになります。
console.log(event.beforeValue)
解決策
渡す引数にoptionalの値を&でつないで型を拡張する
import { Component } from 'Component'
const onClickEvent = (event: Event & { beforeValue?: string }) => {
// アクセスできる!
console.log(event.beforeValue)
}
return (
<Component onClick={onClickEvent} />
)
optionalにしないとライブラリが提供している型定義と合致しないエラーが出てしまうのでoptionalにするのは必須です。