メモ書きです。
https://github.com/Microsoft/TypeScript/issues/14889
参考にしたIssue
例えば、画像をアップするところをやっていて、次のようなコンポーネントを扱っているとします。
(適当にそれっぽい雰囲気にしています)
import * as React from 'react'
import { compose, pure } from 'recompose'
import Icon, { IconType } from '../../atoms/Icon/Icon'
import * as styles from './ImageUploader.css'
interface ImageUploaderProps {
onChangeImage: (data: any) => void
}
const ImageUploader: React.FunctionComponent<ImageUploaderProps> = ({ onChangeImage }) => {
return (
<div className={styles.container}>
<label className={styles.buttonLabel}>
<span>
<Icon iconType={IconType.IMAGE_UPLOAD_ICON} />
アップロード
</span>
<input
hidden
id={'file_image'}
type="file"
accept="image/png,image/jpg,image/bmp"
onChange={e =>
onChangeImage(targetImage: e.target.files[0])
}
/>
</label>
</div>
)
}
export default compose<ImageUploaderProps, ImageUploaderProps>(pure)(ImageUploader)
inputに画像をアップする際、そこからFileオブジェクトを抜き出すときは files
に配列で格納されているのでこれをindex指定で取り出す必要があります。
nullかもしれないオブジェクトに対する操作は認められない、という感じですね。では、nullじゃないことを保証すればよいわけです。ここでは2つ変更します。
interface変更
まずinterfaceが雑に any
を使ってしまっているのでこれを直しましょう。
変更前
interface ImageUploaderProps {
onChangeImage: (data: any) => void
}
変更後
interface ImageUploaderProps {
onChangeImage: (object: { targetImage: File | null }) => void
}
onChange内で、nullの場合に関する切り分けをする
配列にアクセスする際に、nullでないことを保証します。
変更前
<input
hidden
id={'file_image'}
type="file"
accept="image/png,image/jpg,image/bmp"
onChange={e =>
onChangeImage(targetImage: e.target.files[0])
}
/>
変更後
<input
hidden
id={'file_image'}
type="file"
accept="image/png,image/jpg,image/bmp"
onChange={e =>
onChangeImage(targetImage: e.target.files !== null ? e.target.files[0] : null)
}
/>
参考にしたIssueでは、guard文で早めに抜けていました。
https://github.com/Microsoft/TypeScript/issues/14889#issuecomment-289613908
しかし、onChangeの関数内で同様に抜けようとするよりも、三項演算子でnullを回避するほうが見やすいかと思ってこのように書いています。
nullが裏のsagaなどに行ってしまうので、そこでの制御はどちらにせよ必要ですね...
まとめ
最終的には以下のようになります。
import * as React from 'react'
import { compose, pure } from 'recompose'
import Icon, { IconType } from '../../atoms/Icon/Icon'
import * as styles from './ImageUploader.css'
interface ImageUploaderProps {
onChangeImage: (object: { targetImage: File | null }) => void
}
const ImageUploader: React.FunctionComponent<ImageUploaderProps> = ({ onChangeImage }) => {
return (
<div className={styles.container}>
<label className={styles.buttonLabel}>
<span>
<Icon iconType={IconType.IMAGE_UPLOAD_ICON} />
アップロード
</span>
<input
hidden
id={'file_image'}
type="file"
accept="image/png,image/jpg,image/bmp"
onChange={e =>
onChangeImage(targetImage: e.target.files !== null ? e.target.files[0] : null )
}
/>
</label>
</div>
)
}
export default compose<ImageUploaderProps, ImageUploaderProps>(pure)(ImageUploader)