(完全に自分用のメモです。)
TypeScript+React+Redux を使うときに気に入って使っているtypesafe-actions。 意外なところでハマりポイントがありました。
Reducer で Action の型情報が見れない
よくある Reducer。
export const weatherReducer = (
state: WeatherState = initialState,
action: Action
): WeatherState => {
switch (action.type) {
case getType(actions.weatherSetAction):
return Object.assign({}, state, { weather: new Weather(action.payload) });
case getType(actions.weatherErrorAction):
console.error(action.payload.message);
return state;
default:
return state;
}
};
ところがaction.payload
の型情報が正しく取れていません。なぜだろう。。。
エラーの内容はこちら。
TS2339:
Property 'payload' does not exist on type
'{ type: string; payload: { lat: number; lng: number; }; } |
{ type: string; payload: Response; } |
{ type: "@@weather/ERROR"; payload: Error; } |
{ type: "@@map/READY"; }'.
Property 'payload' does not exist on type '{ type: "@@map/READY"; }'.
Action の型定義は一見良さそうなので、最初は何が原因かわかりませんでした。
(追記: 2019/01/23) というか型定義良くない。
type: string
になっている。Blog 用にエラーを整形したら気づいた。エラーメッセージもっと冷静に読まないと。
ActionType に動的な文字列を使ってはいけない
原因はこれでした。ActionType をPREFIX
を使ったテンプレート文字列で定義している部分。
// constants.ts
const PREFIX = "@MyApp";
export const MAP_READY = `${PREFIX}/READY`;
export const WEATHER_GET = `${PREFIX}/GET`;
export const WEATHER_SET = `${PREFIX}/SET`;
export const WEATHER_ERROR = `${PREFIX}/ERROR`;
公式ドキュメントにも書いてありました。
PRO-TIP: string constants limitation in TypeScript ...
TypeScript には文字列の定義に関する制限事項があってだね。テンプレート文字列などで動的に文字列を組み立てた場合、型情報が全部string
になってしまって、reducer の case の中の型情報が壊れるよ。(超訳)
// Example file: './constants.ts'
// WARNING: Incorrect usage
export const ADD = prefix + 'ADD'; // => string
export const ADD = `${prefix}/ADD`; // => string
export default {
ADD: '@prefix/ADD', // => string
}
// Correct usage
export const ADD = '@prefix/ADD'; // => '@prefix/ADD'
export const TOGGLE = '@prefix/TOGGLE'; // => '@prefix/TOGGLE'
(公式ドキュメントから抜粋)
という訳で、正しいやり方は普通に文字列だけで ActionType 定義すればいいだけでした。
// constants.ts
export const MAP_READY = "@MyApp/READY";
export const WEATHER_GET = "@MyApp/GET";
export const WEATHER_SET = "@MyApp/SET";
export const WEATHER_ERROR = "@MyApp/ERROR";
なるほど、TypeScript の制限。 勉強になりました。