技術メモ・実体験
TypeScriptを使っているのにanyだらけ、型エラーをasで無理やり回避、nullチェックを忘れてランタイムエラー。最初の自分はこんな状態でした。
実際のコードレビューで指摘を受けながら学んだ、「型安全なコードを書くために意識していること」をまとめます。
対象読者TypeScriptを使い始めたエンジニア、特にReact Nativeで「とりあえず動いてるけど型定義が雑」という人に向けて書きます。
キラ
キラ
TypeScriptはJavaScriptに型をつけるだけじゃなくて、「バグを事前に防ぐ設計」を強制してくれるツール。ちゃんと使えば強力。
Contents
- Pickで必要なpropsだけに絞る
- null/undefinedを安全に扱う(??演算子)
- JSON.parseに型アサーションをつける
- バリデーション関数をユーティリティとして分離する
- まとめ:型は「制約」ではなく「設計図」
① Pickで必要なpropsだけに絞る
コンポーネントのpropsを定義するとき、親コンポーネントの型をそのまま使い回すと、不要なpropsまで受け取れる状態になります。Pickを使って必要なpropsだけに絞ることで、コンポーネントの責務が明確になります。
// ❌ TextInputProps を全部受け取っている
// このコンポーネントが本当に必要なのは crmField と field だけなのに
const UrlInputFieldCmp: React.FC<TextInputProps> = (props) => {
// …
};
// ✅ Pick で必要なものだけに絞る
// このコンポーネントが受け取れるのは crmField と field だけ
const UrlInputFieldCmp: React.FC<Pick<TextInputProps, ‘crmField’ | ‘field’>> = (props) => {
const { crmField, field } = props;
return (
<TextInputFieldCmp
crmField={crmField}
field={field}
keyboardType=”url”
autoCapitalize=”none”
autoCorrect={false}
textContentType=”URL”
/>
);
};
? POINT
Pick<T, K>は型Tからキーを選んで新しい型を作るユーティリティ型です。コンポーネントの「使える範囲」を明示的に制限することで、誤った使い方をコンパイル時に防げます。
スカイ
スカイ
「このコンポーネントはこのpropsしか受け取らない」という意図がコードに表れるから、読む側も使う側もわかりやすくなるね。
② null/undefinedを安全に扱う(??演算子)
APIから取得したデータはnullやundefinedが含まれることがあります。それをそのままpropsに渡すと警告やランタイムエラーの原因になります。
// ❌ field.value が null/undefined の場合に警告が出る
// TextInput の value に null を渡すとコンポーネントが警告を出す
<TextInputFieldCmp
value={field.value}
/>
// ✅ ?? 演算子でフォールバックを設定する
// null または undefined の場合は空文字列にする
<TextInputFieldCmp
value={field.value ?? ”}
/>
? ??(Nullish Coalescing)演算子とは
a ?? bは「aがnullまたはundefinedの場合はbを返す、そうでなければaを返す」という演算子です。||と似ていますが、||は0や空文字列もfalsyとして扱うのに対し、??はnull/undefinedのみを対象にします。
// || との違い
const value1 = 0 || ‘default’; // ‘default’(0はfalsyなので)
const value2 = 0 ?? ‘default’; // 0(0はnull/undefinedではないので)
// フォームの値では ?? の方が正しい
// 0 という値を「入力なし」として扱いたくない場合は ?? を使う
キラ
キラ
フォームの入力値に || を使うと「0を入力したのに空扱いになる」というバグが起きる。数値フィールドでは特に注意。
③ JSON.parseに型アサーションをつける
APIからJSON.parseした結果は、TypeScriptではany型になります。そのまま使うと型安全性が失われます。
// ❌ JSON.parseの結果はany型
const parsed = JSON.parse(meetingMinutes.data);
// parsed.duration にアクセスできるが型チェックが効かない
setDuration(parsed.duration);
// ✅ 型アサーションで型を明示する
const parsed = JSON.parse(
meetingMinutes.data as string // data は string | null なので string に絞る
) as { duration?: number }; // パースした結果の型を明示
// duration は number | undefined として扱われる
if (parsed.duration) {
setDuration(parsed.duration); // 型チェックが効く
}
⚠️ 注意点
asによる型アサーションは「コンパイラへの約束」であり、実行時に型チェックは行われません。パースした結果が本当に期待する型であることを、ロジックで保証する必要があります。型アサーションを乱用すると型安全性が失われるので注意。
スカイ
スカイ
「asで型をつけたから安全」ではなく、「実際にその型になっているか」を確認するのがセット。型アサーションは道具であって保証ではない。
④ バリデーション関数をユーティリティとして分離する
コンポーネントの中にバリデーションロジックを書くと、テストが難しくなります。バリデーション関数をユーティリティとして分離することで、テストしやすく・再利用しやすいコードになります。
// src/util/textInputField.ts
// URL のバリデーション:scheme(https:// など)があるか
export const isValidUrl = (value: string): boolean => {
try {
const url = new URL(value);
return url.protocol === ‘https:’ || url.protocol === ‘http:’;
} catch {
return false;
}
};
// Email のバリデーション:@ を含むか
export const isValidEmail = (value: string): boolean => {
return value.includes(‘@’) && value.length > 3;
};
// Phone のバリデーション:数字・ハイフン・+ のみか
export const isValidPhone = (value: string): boolean => {
return /^[0-9+\-\s()]*$/.test(value);
};
// テストも書きやすくなる
// src/util/textInputField.test.ts
import { isValidUrl, isValidEmail, isValidPhone } from ‘./textInputField’;
describe(‘isValidUrl’, () => {
it(‘https:// から始まるURLはtrue’, () => {
expect(isValidUrl(‘https://example.com’)).toBe(true);
});
it(‘schemeがないURLはfalse’, () => {
expect(isValidUrl(‘example.com’)).toBe(false);
});
});
describe(‘isValidEmail’, () => {
it(‘@を含むメールアドレスはtrue’, () => {
expect(isValidEmail(‘test@example.com’)).toBe(true);
});
it(‘@を含まない文字列はfalse’, () => {
expect(isValidEmail(‘testexample.com’)).toBe(false);
});
});
? POINT
ユーティリティ関数として分離すると「純粋関数(同じ入力に対して常に同じ出力を返す関数)」になります。純粋関数はテストが簡単で、副作用がなく、再利用しやすい。TypeScriptとの相性も抜群です。キラ
キラ
コンポーネントにロジックを詰め込みすぎるとテストが書けなくなる。「ロジックはユーティリティへ、コンポーネントはUIだけ」が理想。
? この記事のまとめ
- Pickで必要なpropsだけに絞る:コンポーネントの責務が明確になる
- ??演算子でnull/undefinedを安全に扱う:||との違いに注意
- JSON.parseには型アサーションをつける:asは保証ではなく約束
- バリデーション関数はユーティリティとして分離:テストしやすく再利用しやすくなる
TypeScriptの型は「制約」ではなく「設計図」だと思っています。型を丁寧に書くことで、コードの意図が伝わりやすくなり、バグが減り、チーム開発がスムーズになります。
Recommended
ブログを始めるならConoHa WINGが最速
エンジニアの技術メモをブログで発信して収益化。月1,000円以下で始められます。
▶ ConoHa WINGを見てみる
※ アフィリエイトリンクです


コメント