{"id":13066,"date":"2026-03-13T13:36:40","date_gmt":"2026-03-13T13:36:40","guid":{"rendered":"https:\/\/withcode.tech\/media\/?p=13066"},"modified":"2026-07-01T15:28:45","modified_gmt":"2026-07-01T15:28:45","slug":"typescript-type-safe-api-client","status":"publish","type":"post","link":"https:\/\/withcode.tech\/media\/typescript-type-safe-api-client\/","title":{"rendered":"\u3010TypeScript\u3011\u578b\u5b89\u5168\u306aAPI\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u8a2d\u8a08\u306e\u5b9f\u8df5\u624b\u6cd5\uff5c\u9ad8\u5ea6\u306a\u578b\u30c6\u30af\u30cb\u30c3\u30af\u3068\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316\u307e\u3067\u5fb9\u5e95\u89e3\u8aac"},"content":{"rendered":"<div class=\"swell-block-balloon\"><div class=\"c-balloon -bln-left\" data-col=\"gray\"><div class=\"c-balloon__icon -square\"><img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/withcode.tech\/media\/wp-content\/uploads\/2025\/06\/\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8-2025-06-15-14.48.08.jpg\" alt=\"\" class=\"c-balloon__iconImg\" width=\"80px\" height=\"80px\"><span class=\"c-balloon__iconName\">\u751f\u5f92<\/span><\/div><div class=\"c-balloon__body -speaking -border-none\"><div class=\"c-balloon__text\">\n  <p class=\"balloon balloon-left\"><strong>TypeScript\u3067\u578b\u5b89\u5168\u306aAPI\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u3092\u4f5c\u308a\u305f\u3044\u3093\u3067\u3059\u304c\u3001Zod\u3092\u4f7f\u3044\u59cb\u3081\u305f\u3089\u30b9\u30ad\u30fc\u30de\u304c\u5897\u3048\u3059\u304e\u3066\u7ba1\u7406\u304c\u5927\u5909\u306b\u306a\u3063\u3066\u304d\u307e\u3057\u305f\u3002\u3046\u307e\u304f\u6574\u7406\u3059\u308b\u65b9\u6cd5\u306f\u3042\u308a\u307e\u3059\u304b\uff1f<\/strong><\/p>\n  <span class=\"c-balloon__shapes\"><span class=\"c-balloon__before\"><\/span><span class=\"c-balloon__after\"><\/span><\/span><\/div><\/div><\/div><\/div>\n\n  <div class=\"swell-block-balloon\"><div class=\"c-balloon -bln-right\" data-col=\"gray\"><div class=\"c-balloon__icon -square\"><img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/withcode.tech\/media\/wp-content\/uploads\/2025\/06\/\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8-2025-06-15-15.11.23.jpg\" alt=\"\" class=\"c-balloon__iconImg\" width=\"80px\" height=\"80px\"><span class=\"c-balloon__iconName\">\u30da\u30f3\u535a\u58eb<\/span><\/div><div class=\"c-balloon__body -speaking -border-none\"><div class=\"c-balloon__text\">\n  <p class=\"balloon balloon-right\"><strong>\u3088\u30fc\u304f\u805e\u304f\u3093\u3060\u305e\u3002Zod\u30b9\u30ad\u30fc\u30de\u306e\u7ba1\u7406\u306f\u3001\u3084\u308a\u65b9\u3092\u77e5\u3089\u306a\u3044\u3068\u78ba\u304b\u306b\u8907\u96d1\u306b\u306a\u308a\u304c\u3061\u3058\u3083\u3002\u4eca\u65e5\u306f\u3001\u30b9\u30ad\u30fc\u30de\u306e\u5408\u6210\u30fb\u578b\u5b89\u5168\u306a\u30e2\u30c3\u30afAPI\u30fb\u9ad8\u5ea6\u306a\u578b\u30c6\u30af\u30cb\u30c3\u30af\u30fb\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316\u307e\u3067\u3001\u5b9f\u52d9\u3067\u4f7f\u3048\u308b\u5b9f\u8df5\u624b\u6cd5\u3092\u8a73\u3057\u304f\u89e3\u8aac\u3059\u308b\u305e\u3044\uff01<\/strong><\/p>\n  <span class=\"c-balloon__shapes\"><span class=\"c-balloon__before\"><\/span><span class=\"c-balloon__after\"><\/span><\/span><\/div><\/div><\/div><\/div>\n\n\n<div class=\"wp-block-group swl-box\"><div class=\"wp-block-group__inner-container\">\n\n<h3 class=\"wp-block-heading\">\u3053\u306e\u8a18\u4e8b\u3067\u308f\u304b\u308b\u3053\u3068<\/h3>\n\n\n<ul class=\"wp-block-list\">\n  <li>API\u578b\u306e\u4e0d\u4e00\u81f4\u304c\u7e70\u308a\u8fd4\u3057\u8d77\u304d\u308b\u6839\u672c\u539f\u56e0\u30683\u3064\u306e\u89e3\u6c7a\u7b56<\/li>\n  <li>Zod\u30fbtRPC\u30fbOpenAPI\u30fbts-rest\u30fbGraphQL\u306e\u30a2\u30d7\u30ed\u30fc\u30c1\u6bd4\u8f03\u3068\u9078\u3073\u65b9<\/li>\n  <li>Zod\u30b9\u30ad\u30fc\u30de\u3092<code>extend<\/code>\u3067\u5408\u6210\u3057\u3066\u91cd\u8907\u309280%\u524a\u6e1b\u3059\u308b\u7ba1\u7406\u624b\u6cd5<\/li>\n  <li>MSW\u30fbfaker.js\u30fbtRPC\u3092\u4f7f\u3063\u305f\u578b\u5b89\u5168\u306a\u30e2\u30c3\u30afAPI\u69cb\u7bc9\u65b9\u6cd5<\/li>\n  <li>\u6761\u4ef6\u4ed8\u304d\u578b\u30fbTemplate Literal Types\u30fb\u30df\u30c3\u30af\u30b9\u30a4\u30f3\u306e\u9ad8\u5ea6\u306a\u578b\u30c6\u30af\u30cb\u30c3\u30af3\u9078\u3068Zod\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316<\/li>\n<\/ul>\n\n<\/div><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">TypeScript\u3067\u578b\u5b89\u5168\u306aAPI\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u3092\u5c0e\u5165\u3059\u308b\u3068\u3001\u6700\u521d\u306f\u5feb\u9069\u3067\u3082\u300c\u30b9\u30ad\u30fc\u30de\u304c\u5897\u3048\u3066\u30e1\u30f3\u30c6\u30ca\u30f3\u30b9\u304c\u5927\u5909\u300d\u300c\u30c6\u30b9\u30c8\u74b0\u5883\u3068\u672c\u756a\u3067\u578b\u304c\u305a\u308c\u308b\u300d\u300cZod\u306e\u691c\u8a3c\u3067\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u304c\u843d\u3061\u305f\u300d\u3068\u3044\u3063\u305f\u6b21\u306e\u58c1\u306b\u3076\u3064\u304b\u308b\u3053\u3068\u304c\u3042\u308a\u307e\u3059\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u5b9f\u969b\u300150\u4ee5\u4e0a\u306e\u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8\u3092\u6301\u3064\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3067Zod\u3092\u5c0e\u5165\u3057\u305f\u4e8b\u4f8b\u3067\u306f\u3001\u30b9\u30ad\u30fc\u30de\u306e\u91cd\u8907\u309280%\u524a\u6e1b\u3057\u3064\u3064\u672c\u756a\u306e\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u309230%\u5411\u4e0a\u3055\u305b\u305f\u5b9f\u7e3e\u304c\u3042\u308a\u307e\u3059\u3002\u578b\u5b89\u5168\u6027\u3068\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u306f\u300c\u3069\u3061\u3089\u304b\u3092\u8ae6\u3081\u308b\u300d\u3067\u306f\u306a\u304f\u3001\u6b63\u3057\u3044\u8a2d\u8a08\u3067\u4e21\u7acb\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u672c\u8a18\u4e8b\u3067\u306f\u3001<strong><span class=\"swl-marker mark_yellow\">TypeScript\u578b\u5b89\u5168\u306aAPI\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u8a2d\u8a08\u306e\u5b9f\u8df5\u624b\u6cd5\u3068\u3057\u3066\u3001Zod\u30b9\u30ad\u30fc\u30de\u306e\u5408\u6210\u30fb\u578b\u5b89\u5168\u306a\u30e2\u30c3\u30afAPI\u30fb\u9ad8\u5ea6\u306a\u578b\u30c6\u30af\u30cb\u30c3\u30af3\u9078\u30fbZod\u306e\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316\u30fb\u578b\u99c6\u52d5\u958b\u767a\u306e\u5b9f\u8df5\u30d7\u30ed\u30bb\u30b9<\/span><\/strong>\u3092\u3001\u30b3\u30fc\u30c9\u4f8b\u3068\u6bd4\u8f03\u8868\u4ed8\u304d\u3067\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u306a\u305cAPI\u578b\u306e\u4e0d\u4e00\u81f4\u306f\u9632\u304e\u306b\u304f\u3044\u306e\u304b<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">API\u578b\u306e\u4e0d\u4e00\u81f4\u304c\u7e70\u308a\u8fd4\u3057\u767a\u751f\u3059\u308b\u6839\u672c\u539f\u56e0\u306f\u3001\u300c\u578b\u306e\u5b9a\u7fa9\u5834\u6240\u304c\u8907\u6570\u306b\u5206\u6563\u3057\u3066\u3044\u308b\u300d\u3053\u3068\u306b\u3042\u308a\u307e\u3059\u3002<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"alignleft size-full\"><img decoding=\"async\" src=\"img\/typescript_api_type_mismatch.png\" alt=\"TypeScript API\u3068\u30d5\u30ed\u30f3\u30c8\u30a8\u30f3\u30c9\u306e\u578b\u306e\u4e0d\u4e00\u81f4\u304c\u8d77\u304d\u308b\u539f\u56e0\u56f3\" class=\"wp-image-12380\"\/><\/figure>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\">\u578b\u306e\u4e0d\u4e00\u81f4\u304c\u8d77\u304d\u308b4\u3064\u306e\u30d1\u30bf\u30fc\u30f3<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>\u30d1\u30bf\u30fc\u30f3<\/th><th>\u5177\u4f53\u4f8b<\/th><th>\u767a\u751f\u3059\u308b\u30d0\u30b0<\/th><\/tr><\/thead><tbody>\n<tr><td>\u4ed5\u69d8\u5909\u66f4\u306e\u4f1d\u9054\u6f0f\u308c<\/td><td><code>username<\/code>\u2192<code>userName<\/code>\u306b\u5909\u66f4\u3001\u30d5\u30ed\u30f3\u30c8\u306f\u65e7\u30d5\u30a3\u30fc\u30eb\u30c9\u306e\u307e\u307e<\/td><td>\u8868\u793a\u5d29\u308c\u30fbundefined\u53c2\u7167<\/td><\/tr>\n<tr><td>\u578b\u3068\u5b9f\u30c7\u30fc\u30bf\u306e\u4e56\u96e2<\/td><td>\u4ed5\u69d8\u66f8\u3067\u306f<code>number<\/code>\u3060\u304c\u5b9f\u969b\u306f<code>string<\/code>\u3067\u8fd4\u308b<\/td><td>\u8a08\u7b97\u3067NaN\u3001\u30e9\u30f3\u30bf\u30a4\u30e0\u30a8\u30e9\u30fc<\/td><\/tr>\n<tr><td>\u624b\u52d5\u578b\u5b9a\u7fa9\u306e\u8ca0\u8377\u5897\u5927<\/td><td>50\u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8\u306e\u578b\u3092\u90311\u56de\u624b\u52d5\u3067\u540c\u671f<\/td><td>\u540c\u671f\u6f0f\u308c\u30fb\u5de5\u6570\u5727\u8feb<\/td><\/tr>\n<tr><td>\u30cd\u30b9\u30c8\u304c\u6df1\u3044\u30ec\u30b9\u30dd\u30f3\u30b9<\/td><td>30\u4ee5\u4e0a\u306e\u9805\u76ee\u3092\u30cd\u30b9\u30c8\u3057\u305f\u5206\u6790\u30c7\u30fc\u30bf<\/td><td>\u7279\u5b9a\u30d5\u30a3\u30fc\u30eb\u30c9\u3060\u3051\u578b\u304c<code>any<\/code>\u306b\u306a\u308b<\/td><\/tr>\n<\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">\u89e3\u6c7a\u306e3\u672c\u67f1<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>\u3010\u578b\u306e\u4e0d\u4e00\u81f4\u3092\u9632\u30503\u672c\u67f1\u3011\n\n1. \u5358\u4e00\u30bd\u30fc\u30b9\u304b\u3089\u578b\u3092\u751f\u6210\u3059\u308b\n   \u2705 OpenAPI\u3084Zod\u30b9\u30ad\u30fc\u30de\u3092\u300c\u552f\u4e00\u306e\u5b9a\u7fa9\u5834\u6240\u300d\u306b\u3059\u308b\n   \u274c \u30d5\u30ed\u30f3\u30c8\u3068\u30d0\u30c3\u30af\u3067\u5225\u3005\u306b\u578b\u3092\u624b\u66f8\u304d\u3059\u308b\n\n2. \u5b9f\u884c\u6642\u306b\u30ec\u30b9\u30dd\u30f3\u30b9\u3092\u691c\u8a3c\u3059\u308b\n   \u2705 Zod\u306e parse() \u3067\u5b9f\u969b\u306e\u5024\u304c\u578b\u3068\u4e00\u81f4\u3059\u308b\u304b\u78ba\u8a8d\u3059\u308b\n   \u274c \u9759\u7684\u578b\u30c1\u30a7\u30c3\u30af\u3060\u3051\u3067\u5b89\u5fc3\u3059\u308b\n\n3. \u30d1\u30b9\u30fb\u30d1\u30e9\u30e1\u30fc\u30bf\u30fb\u30ec\u30b9\u30dd\u30f3\u30b9\u3092\u307e\u3068\u3081\u3066\u578b\u4ed8\u3051\u3059\u308b\n   \u2705 tRPC \u3084 ts-rest \u3067\u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8\u5168\u4f53\u3092\u578b\u5b89\u5168\u306b\n   \u274c \u30ec\u30b9\u30dd\u30f3\u30b9\u306e\u578b\u3060\u3051\u5b9a\u7fa9\u3057\u3066\u30d1\u30b9\u306f\u6587\u5b57\u5217\u306e\u307e\u307e<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u30a2\u30d7\u30ed\u30fc\u30c1\u9078\u629e\u30c1\u30e3\u30fc\u30c8\uff5cZod\u30fbtRPC\u30fbOpenAPI\u30fbts-rest\u30fbGraphQL\u3092\u6bd4\u8f03<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u578b\u5b89\u5168\u306aAPI\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306e\u5b9f\u73fe\u65b9\u6cd5\u306f1\u3064\u3067\u306f\u306a\u304f\u3001\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306e\u72b6\u6cc1\u306b\u5fdc\u3058\u3066\u6700\u9069\u306a\u9078\u629e\u304c\u5909\u308f\u308a\u307e\u3059\u3002<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"alignleft size-full\"><img decoding=\"async\" src=\"img\/typescript_api_approach_comparison.png\" alt=\"TypeScript\u578b\u5b89\u5168API\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306e\u30a2\u30d7\u30ed\u30fc\u30c1\u6bd4\u8f03\u30c1\u30e3\u30fc\u30c8\" class=\"wp-image-12381\"\/><\/figure>\n<\/div>\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>\u30a2\u30d7\u30ed\u30fc\u30c1<\/th><th>\u4e3b\u306a\u5229\u70b9<\/th><th>\u4e3b\u306a\u6b20\u70b9<\/th><th>\u5411\u3044\u3066\u3044\u308b\u30b1\u30fc\u30b9<\/th><\/tr><\/thead><tbody>\n<tr><td><strong>OpenAPI + \u578b\u81ea\u52d5\u751f\u6210<\/strong><\/td><td>\u696d\u754c\u6a19\u6e96\u30fb\u591a\u8a00\u8a9e\u5bfe\u5fdc\u30fb\u30c9\u30ad\u30e5\u30e1\u30f3\u30c8\u5316<\/td><td>\u4ed5\u69d8\u30d5\u30a1\u30a4\u30eb\u306e\u30e1\u30f3\u30c6\u304c\u5225\u9014\u5fc5\u8981\u30fb\u8907\u96d1\u306a\u578b\u306e\u8868\u73fe\u306b\u9650\u754c<\/td><td>\u65e2\u5b58REST API\u30fb\u8907\u6570\u8a00\u8a9e\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u30fb\u5927\u898f\u6a21\u30c1\u30fc\u30e0<\/td><\/tr>\n<tr><td><strong>Zod\u30b9\u30ad\u30fc\u30de + \u624b\u52d5\u578b<\/strong><\/td><td>\u5b9f\u884c\u6642\u691c\u8a3c\u30fb\u6bb5\u968e\u7684\u5c0e\u5165\u304c\u5bb9\u6613\u30fb\u5236\u7d04\u3092\u7d30\u304b\u304f\u5b9a\u7fa9\u3067\u304d\u308b<\/td><td>\u81ea\u52d5\u751f\u6210\u306a\u3057\u30fb\u5927\u898f\u6a21\u306b\u306a\u308b\u3068\u91cd\u8907\u304c\u5897\u3048\u304c\u3061<\/td><td>\u65e2\u5b58\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3078\u306e\u6bb5\u968e\u5c0e\u5165\u30fb\u53b3\u5bc6\u306a\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3<\/td><\/tr>\n<tr><td><strong>tRPC<\/strong><\/td><td>\u30a8\u30f3\u30c9to\u30a8\u30f3\u30c9\u3067\u578b\u5b89\u5168\u30fb\u30b9\u30ad\u30fc\u30de\u4e0d\u8981\u30fb\u578b\u306e\u5171\u6709\u304c\u81ea\u7136<\/td><td>TypeScript\u5c02\u7528\u30fb\u65e2\u5b58REST\u3068\u306e\u7d71\u5408\u306b\u624b\u9593\u304c\u304b\u304b\u308b<\/td><td>\u30d5\u30eb\u30b9\u30bf\u30c3\u30afTypeScript\u30fb\u65b0\u898f\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u30fb\u5c0f\u301c\u4e2d\u898f\u6a21<\/td><\/tr>\n<tr><td><strong>ts-rest<\/strong><\/td><td>\u65e2\u5b58REST\u306e\u307e\u307e\u578b\u5b89\u5168\u306b\u30fbZod\u3068\u306e\u76f8\u6027\u304c\u826f\u3044<\/td><td>\u5951\u7d04\uff08contract\uff09\u306e\u5b9a\u7fa9\u304c\u5fc5\u8981\u30fb\u30a8\u30b3\u30b7\u30b9\u30c6\u30e0\u306f\u307e\u3060\u5c0f\u3055\u3044<\/td><td>\u65e2\u5b58REST\u3092\u7dad\u6301\u3057\u305f\u3044\u30fbZod\u30d9\u30fc\u30b9\u306e\u30d7\u30ed\u30b8\u30a7\u30af\u30c8<\/td><\/tr>\n<tr><td><strong>GraphQL + CodeGen<\/strong><\/td><td>\u578b\u5b89\u5168\u306a\u30af\u30a8\u30ea\u30fb\u5fc5\u8981\u306a\u30c7\u30fc\u30bf\u306e\u307f\u53d6\u5f97<\/td><td>\u30d0\u30c3\u30af\u30a8\u30f3\u30c9\u5b9f\u88c5\u304c\u8907\u96d1\u30fb\u5b66\u7fd2\u30b3\u30b9\u30c8\u304c\u9ad8\u3044<\/td><td>\u30c7\u30fc\u30bf\u8981\u4ef6\u304c\u8907\u96d1\u30fb\u30e2\u30d0\u30a4\u30eb\u30a2\u30d7\u30ea\u3068\u306eAPI\u5171\u6709<\/td><\/tr>\n<\/tbody><\/table><\/figure>\n\n\n\n<pre class=\"wp-block-code\"><code>\u3010\u30a2\u30d7\u30ed\u30fc\u30c1\u9078\u629e\u306e\u76ee\u5b89\u3011\n\n\u30d5\u30eb\u30b9\u30bf\u30c3\u30afTypeScript\uff08\u65b0\u898f\uff09\n\u2192 tRPC + Zod + MSW + Vitest\n\n\u65e2\u5b58REST API\u3092\u578b\u5b89\u5168\u306b\u3057\u305f\u3044\n\u2192 ts-rest + Zod \u307e\u305f\u306f OpenAPI + \u578b\u81ea\u52d5\u751f\u6210\n\n\u6bb5\u968e\u7684\u306b\u578b\u5b89\u5168\u5316\u3057\u305f\u3044\n\u2192 Zod\u30b9\u30ad\u30fc\u30de + \u624b\u52d5\u578b\u304b\u3089\u59cb\u3081\u3066\u5f90\u3005\u306b\u79fb\u884c\n\n\u30c7\u30fc\u30bf\u8981\u4ef6\u304c\u8907\u96d1\u30fb\u30e2\u30d0\u30a4\u30eb\u9023\u643a\u3042\u308a\n\u2192 GraphQL + CodeGen<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Zod\u30b9\u30ad\u30fc\u30de\u306e\u5408\u6210\u3068\u4e00\u5143\u7ba1\u7406\uff5c\u91cd\u8907\u309280%\u524a\u6e1b\u3059\u308b\u8a2d\u8a08\u65b9\u6cd5<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Zod\u3092\u4f7f\u3046\u3068\u300c\u30b9\u30ad\u30fc\u30de\u304c\u5897\u3048\u3059\u304e\u308b\u300d\u554f\u984c\u306f\u3001<code>extend<\/code>\u3068\u5171\u901a\u30d5\u30a1\u30a4\u30eb\u3078\u306e\u96c6\u7d04\u3067\u5927\u5e45\u306b\u89e3\u6d88\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"alignleft size-full\"><img decoding=\"async\" src=\"img\/zod_schema_composition.png\" alt=\"Zod\u30b9\u30ad\u30fc\u30de\u306e\u5408\u6210\u3068\u4e00\u5143\u7ba1\u7406\u306e\u69cb\u9020\u56f3\" class=\"wp-image-12382\"\/><\/figure>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\">extend\u3067\u30b9\u30ad\u30fc\u30de\u3092\u6bb5\u968e\u7684\u306b\u5e83\u3052\u308b<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u540c\u3058\u69cb\u9020\u3092\u6301\u3064\u8907\u6570\u306e\u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8\u3067\u306f\u3001<strong>\u5171\u901a\u30b9\u30ad\u30fc\u30de\u3092\u5b9a\u7fa9\u3057\u3066 <code>extend<\/code> \u3067\u62e1\u5f35\u3059\u308b<\/strong>\u3068\u30b3\u30fc\u30c9\u306e\u91cd\u8907\u3092\u6e1b\u3089\u305b\u307e\u3059\u3002<\/p>\n\n\n<pre><code>import { z } from 'zod';\n\n\/\/ \u57fa\u672c\u7684\u306a\u30e6\u30fc\u30b6\u30fc\u60c5\u5831\u306e\u30b9\u30ad\u30fc\u30de\uff08\u3059\u3079\u3066\u306e\u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8\u3067\u5171\u901a\uff09\nconst BaseUserSchema = z.object({\n  id: z.number(),\n  name: z.string(),\n});\n\n\/\/ \u8a8d\u8a3c\u60c5\u5831\u3092\u542b\u3080\u30b9\u30ad\u30fc\u30de\uff08\u30ed\u30b0\u30a4\u30f3API\u7528\uff09\nconst AuthUserSchema = BaseUserSchema.extend({\n  email: z.string().email(),\n  role: z.enum(['admin', 'user', 'guest']),\n});\n\n\/\/ \u8a73\u7d30\u60c5\u5831\u3092\u542b\u3080\u30b9\u30ad\u30fc\u30de\uff08\u30d7\u30ed\u30d5\u30a3\u30fc\u30ebAPI\u7528\uff09\nconst DetailedUserSchema = AuthUserSchema.extend({\n  createdAt: z.string().datetime(),\n  lastLogin: z.string().datetime().optional(),\n  preferences: z.record(z.string(), z.unknown()),\n});\n\n\/\/ \u578b\u306f\u3059\u3079\u3066\u30b9\u30ad\u30fc\u30de\u304b\u3089\u5c0e\u51fa\uff08\u4e8c\u91cd\u7ba1\u7406\u306a\u3057\uff09\nexport type BaseUser    = z.infer&lt;typeof BaseUserSchema&gt;;\nexport type AuthUser    = z.infer&lt;typeof AuthUserSchema&gt;;\nexport type DetailedUser = z.infer&lt;typeof DetailedUserSchema&gt;;<\/code><\/pre>\n\n\n<h3 class=\"wp-block-heading\">\u5358\u4e00\u30bd\u30fc\u30b9\u30aa\u30d6\u30c8\u30a5\u30eb\u30fc\u30b9\u3067\u7ba1\u7406\u3059\u308b<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u30b9\u30ad\u30fc\u30de\u30fb\u578b\u30fbAPI\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u30fb\u30e2\u30c3\u30af\u3092<strong>\u5171\u901a\u306e1\u30d5\u30a1\u30a4\u30eb\u304b\u3089\u6d3e\u751f\u3055\u305b\u308b<\/strong>\u3068\u3001\u300c\u672c\u756a\u3068\u30e2\u30c3\u30af\u3067\u578b\u304c\u98df\u3044\u9055\u3046\u300d\u554f\u984c\u3092\u9632\u3052\u307e\u3059\u3002<\/p>\n\n\n<pre><code>\/\/ \u5171\u6709\u30b9\u30ad\u30fc\u30de\u30d5\u30a1\u30a4\u30eb src\/shared\/schemas.ts\nimport { z } from 'zod';\n\nexport const schemas = {\n  User: z.object({\n    id: z.number(),\n    name: z.string(),\n    email: z.string().email(),\n    role: z.enum(['admin', 'user', 'guest']),\n    createdAt: z.string(),\n  }),\n  \/\/ \u4ed6\u306e\u30b9\u30ad\u30fc\u30de\u3092\u8ffd\u52a0\u3059\u308b\u3068\u304d\u3082\u3053\u3053\u3060\u3051\u5909\u66f4\u3059\u308b\n};\n\n\/\/ \u578b\u5b9a\u7fa9\u306e\u5c0e\u51fa\uff08\u30b9\u30ad\u30fc\u30de\u304b\u3089\u81ea\u52d5\u751f\u6210\uff09\nexport type User = z.infer&lt;typeof schemas.User&gt;;\n\n\/\/ API\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\nexport const apiClient = {\n  getUser: async (id: number): Promise&lt;User&gt; => {\n    const response = await fetch(`\/users\/${id}`);\n    const data = await response.json();\n    return schemas.User.parse(data); \/\/ \u5b9f\u884c\u6642\u691c\u8a3c\n  },\n};\n\n\/\/ \u30b5\u30fc\u30d0\u30fc\u30fb\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u30fb\u30e2\u30c3\u30af\u3059\u3079\u3066\u3067\u3053\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u4f7f\u3046<\/code><\/pre>\n\n\n<pre class=\"wp-block-code\"><code>\u3010\u30b9\u30ad\u30fc\u30de\u7ba1\u7406\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9\u3011\n\n\u2705 \u63a8\u5968\uff1a\n- src\/shared\/schemas.ts \u306b\u5168\u30b9\u30ad\u30fc\u30de\u3092\u96c6\u7d04\n- BaseSchema \u2192 extend \u3067\u6bb5\u968e\u7684\u306b\u5e83\u3052\u308b\n- \u578b\u306f z.infer&lt;&gt; \u3067\u5c0e\u51fa\uff08\u624b\u66f8\u304d\u3057\u306a\u3044\uff09\n- \u3059\u3079\u3066\u306e\u30ec\u30a4\u30e4\u30fc\uff08API\u30fb\u30e2\u30c3\u30af\u30fb\u30c6\u30b9\u30c8\uff09\u3067\u540c\u3058\u30b9\u30ad\u30fc\u30de\u3092\u4f7f\u3046\n\n\u274c \u907f\u3051\u308b\u3079\u304d\uff1a\n- \u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8\u3054\u3068\u306b\u500b\u5225\u30d5\u30a1\u30a4\u30eb\u306b\u30b9\u30ad\u30fc\u30de\u3092\u66f8\u304f\n- TypeScript \u306e interface \u3092\u624b\u66f8\u304d\u3057\u3066\u30b9\u30ad\u30fc\u30de\u3068\u4e8c\u91cd\u7ba1\u7406\u3059\u308b\n- \u30e2\u30c3\u30af\u5c02\u7528\u306e\u578b\u5b9a\u7fa9\u3092\u5225\u9014\u4f5c\u308b<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u578b\u5b89\u5168\u306a\u30e2\u30c3\u30afAPI\u306e\u4f5c\u308a\u65b9\uff5cMSW\u30fbfaker.js\u30fbtRPC\u7d71\u5408<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u578b\u5b89\u5168\u306a\u30e2\u30c3\u30afAPI\u306e\u6838\u5fc3\u306f\u300c\u672c\u756a\u3068\u540c\u3058\u30b9\u30ad\u30fc\u30de\u3092\u4f7f\u3046\u300d\u3053\u3068\u3067\u3059\u3002MSW\uff08Mock Service Worker\uff09\u30fbfaker.js\u30fbZod\u3092\u7d44\u307f\u5408\u308f\u305b\u308b\u3053\u3068\u3067\u3001\u73fe\u5b9f\u7684\u304b\u3064\u578b\u5b89\u5168\u306a\u30e2\u30c3\u30af\u304c\u5b9f\u73fe\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"alignleft size-full\"><img decoding=\"async\" src=\"img\/msw_type_safe_mock.png\" alt=\"MSW\u3068Zod\u3092\u7d44\u307f\u5408\u308f\u305b\u305f\u578b\u5b89\u5168\u306a\u30e2\u30c3\u30afAPI\u306e\u4ed5\u7d44\u307f\u56f3\" class=\"wp-image-12383\"\/><\/figure>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\">MSW + Zod\u3067\u30e2\u30c3\u30af\u30c7\u30fc\u30bf\u3092\u691c\u8a3c\u3059\u308b<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">MSW\u306e\u30cf\u30f3\u30c9\u30e9\u5185\u3067Zod\u30b9\u30ad\u30fc\u30de\u3092\u4f7f\u3063\u3066\u8fd4\u3059\u30c7\u30fc\u30bf\u3092\u691c\u8a3c\u3059\u308b\u3068\u3001\u300c\u30e2\u30c3\u30af\u304c\u901a\u3063\u3066\u672c\u756a\u3067\u843d\u3061\u308b\u300d\u3068\u3044\u3046\u4e8b\u614b\u3092\u9632\u3052\u307e\u3059\u3002<\/p>\n\n\n<pre><code>import { rest } from 'msw';\nimport { setupServer } from 'msw\/node';\nimport { schemas } from '..\/shared\/schemas';\nimport type { User } from '..\/shared\/schemas';\n\nconst mockUsers: User[] = [\n  {\n    id: 1,\n    name: '\u30c6\u30b9\u30c8\u30e6\u30fc\u30b6\u30fc',\n    email: 'test@example.com',\n    role: 'user',\n    createdAt: new Date().toISOString(),\n  },\n];\n\nexport const server = setupServer(\n  rest.get('\/users\/:id', (req, res, ctx) => {\n    const { id } = req.params;\n    const user = mockUsers.find((u) =&gt; u.id === Number(id));\n\n    if (!user) return res(ctx.status(404));\n\n    \/\/ \u958b\u767a\u6642\u306fZod\u3067\u691c\u8a3c\u3057\u3066\u30e2\u30c3\u30af\u30c7\u30fc\u30bf\u306e\u30ba\u30ec\u3092\u65e9\u671f\u767a\u898b\n    try {\n      schemas.User.parse(user);\n    } catch (e) {\n      console.error('\u30e2\u30c3\u30af\u30c7\u30fc\u30bf\u304c\u30b9\u30ad\u30fc\u30de\u3068\u4e00\u81f4\u3057\u307e\u305b\u3093:', e);\n    }\n\n    return res(ctx.json(user));\n  })\n);<\/code><\/pre>\n\n\n<h3 class=\"wp-block-heading\">faker.js + Zod\u3067\u30ea\u30a2\u30eb\u306a\u30e2\u30c3\u30af\u30c7\u30fc\u30bf\u3092Zod\u30b9\u30ad\u30fc\u30de\u304b\u3089\u81ea\u52d5\u751f\u6210\u3059\u308b<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Zod\u30b9\u30ad\u30fc\u30de\u306e\u578b\u60c5\u5831\u3092\u4f7f\u3063\u3066 faker.js \u3067\u30ea\u30a2\u30eb\u306a\u30e2\u30c3\u30af\u30c7\u30fc\u30bf\u3092\u751f\u6210\u3059\u308b\u30e6\u30fc\u30c6\u30a3\u30ea\u30c6\u30a3\u3092\u4f5c\u308b\u3068\u3001\u30b9\u30ad\u30fc\u30de\u306e\u5909\u66f4\u306b\u8ffd\u968f\u3057\u3066\u30e2\u30c3\u30af\u30c7\u30fc\u30bf\u3082\u81ea\u52d5\u3067\u5909\u308f\u308a\u307e\u3059\u3002<\/p>\n\n\n<pre><code>import { faker } from '@faker-js\/faker';\nimport { z } from 'zod';\n\n\/\/ Zod\u30b9\u30ad\u30fc\u30de\u304b\u3089\u30ea\u30a2\u30eb\u306a\u30e2\u30c3\u30af\u30c7\u30fc\u30bf\u3092TypeScript\u578b\u5b89\u5168\u306b\u751f\u6210\nexport function generateMock&lt;T&gt;(schema: z.ZodType&lt;T&gt;): T {\n  if (schema instanceof z.ZodString) {\n    if (schema.description === 'email') return faker.internet.email() as T;\n    if (schema.description === 'datetime') return faker.date.recent().toISOString() as T;\n    return faker.lorem.word() as T;\n  }\n  if (schema instanceof z.ZodNumber) return faker.number.int(100) as T;\n  if (schema instanceof z.ZodEnum) {\n    const values = schema._def.values;\n    return values[Math.floor(Math.random() * values.length)] as T;\n  }\n  if (schema instanceof z.ZodObject) {\n    const shape = schema.shape;\n    const result: Record&lt;string, unknown&gt; = {};\n    for (const [key, fieldSchema] of Object.entries(shape)) {\n      result[key] = generateMock(fieldSchema as z.ZodType&lt;unknown&gt;);\n    }\n    return result as T;\n  }\n  return {} as T;\n}\n\n\/\/ \u4f7f\u7528\u4f8b\uff1a\u30b9\u30ad\u30fc\u30de\u304c\u5909\u308f\u3063\u3066\u3082\u30e2\u30c3\u30af\u30c7\u30fc\u30bf\u306f\u81ea\u52d5\u3067\u8ffd\u968f\u3059\u308b\nimport { schemas } from '..\/shared\/schemas';\nconst mockUser = generateMock(schemas.User);<\/code><\/pre>\n\n\n<h3 class=\"wp-block-heading\">MSW + tRPC \u7d71\u5408\uff1atRPC\u306e\u30e2\u30c3\u30af\u30cf\u30f3\u30c9\u30e9\u3092\u4f5c\u308b<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">tRPC\u3092\u4f7f\u3063\u3066\u3044\u308b\u5834\u5408\u3082\u3001MSW\u3067<code>\/api\/trpc\/:path<\/code>\u3092\u30a4\u30f3\u30bf\u30fc\u30bb\u30d7\u30c8\u3057\u3066\u30e2\u30c3\u30af\u3092\u8fd4\u305b\u307e\u3059\u3002<\/p>\n\n\n<pre><code>import { rest } from 'msw';\nimport { setupServer } from 'msw\/node';\nimport { schemas } from '..\/shared\/schemas';\n\nfunction createTRPCMockHandler() {\n  return rest.post('\/api\/trpc\/:path', async (req, res, ctx) => {\n    const { path } = req.params;\n    const { input } = await req.json();\n\n    if (path === 'users.getById') {\n      const mockUser = {\n        id: input.id,\n        name: faker.person.fullName(),\n        email: faker.internet.email(),\n        role: 'user' as const,\n        createdAt: new Date().toISOString(),\n      };\n\n      \/\/ \u30b9\u30ad\u30fc\u30de\u691c\u8a3c\n      const result = schemas.User.safeParse(mockUser);\n      if (!result.success) {\n        console.error('\u30e2\u30c3\u30af\u30c7\u30fc\u30bf\u304c\u578b\u3068\u4e00\u81f4\u3057\u307e\u305b\u3093:', result.error);\n        return res(ctx.status(500));\n      }\n\n      return res(ctx.json({ result: { data: mockUser } }));\n    }\n\n    return res(ctx.status(404));\n  });\n}\n\nexport const server = setupServer(\n  createTRPCMockHandler(),\n);<\/code><\/pre>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u9ad8\u5ea6\u306a\u578b\u30c6\u30af\u30cb\u30c3\u30af3\u9078\uff5cAPI\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u8a2d\u8a08\u3092\u81ea\u52d5\u5316\u3059\u308b<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">TypeScript\u306e\u578b\u30b7\u30b9\u30c6\u30e0\u3092\u4f7f\u3044\u3053\u306a\u3059\u3068\u3001API\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306e\u578b\u5b9a\u7fa9\u3092\u300c\u624b\u66f8\u304d\u30bc\u30ed\u300d\u306b\u8fd1\u3065\u3051\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002\u4ee5\u4e0b\u306e3\u3064\u306e\u30c6\u30af\u30cb\u30c3\u30af\u3092\u899a\u3048\u308b\u3060\u3051\u3067\u8a2d\u8a08\u306e\u8cea\u304c\u5927\u304d\u304f\u5909\u308f\u308a\u307e\u3059\u3002<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"alignleft size-full\"><img decoding=\"async\" src=\"img\/typescript_advanced_type_techniques.png\" alt=\"TypeScript\u306e\u9ad8\u5ea6\u306a\u578b\u30c6\u30af\u30cb\u30c3\u30af3\u9078\uff08\u6761\u4ef6\u4ed8\u304d\u578b\u30fbTemplate Literal\u30fb\u30a4\u30f3\u30bf\u30fc\u30bb\u30af\u30b7\u30e7\u30f3\uff09\" class=\"wp-image-12384\"\/><\/figure>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\">\u30c6\u30af\u30cb\u30c3\u30af1\uff1a\u6761\u4ef6\u4ed8\u304d\u578b\u3067API\u30ec\u30b9\u30dd\u30f3\u30b9\u306e\u578b\u3092\u81ea\u52d5\u30de\u30c3\u30d4\u30f3\u30b0\u3059\u308b<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u547d\u540d\u898f\u5247\u3092\u5b9a\u3081\u308b\u3053\u3068\u3067\u3001\u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8\u540d\u304b\u3089\u30ec\u30b9\u30dd\u30f3\u30b9\u578b\u3092\u81ea\u52d5\u5c0e\u51fa\u3067\u304d\u307e\u3059\u3002\u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8\u5b9a\u7fa9\u306e90%\u4ee5\u4e0a\u3092\u81ea\u52d5\u5316\u3057\u305f\u5b9f\u7e3e\u304c\u3042\u308a\u307e\u3059\u3002<\/p>\n\n\n<pre><code>\/\/ \u30da\u30fc\u30b8\u30cd\u30fc\u30b7\u30e7\u30f3\u4ed8\u304d\u30ec\u30b9\u30dd\u30f3\u30b9\u306e\u578b\u30d8\u30eb\u30d1\u30fc\ntype PaginatedResponse&lt;T&gt; = {\n  data: T[];\n  pagination: {\n    total: number;\n    page: number;\n    limit: number;\n    totalPages: number;\n  };\n};\n\n\/\/ API\u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8\u3068\u30ec\u30b9\u30dd\u30f3\u30b9\u578b\u306e\u30de\u30c3\u30d4\u30f3\u30b0\ntype ApiEndpoints = {\n  'users\/detail': User;\n  'users\/paginated': User;\n  'products\/detail': Product;\n  'products\/paginated': Product;\n};\n\n\/\/ \u6761\u4ef6\u4ed8\u304d\u578b\uff1a\u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8\u540d\u304c \"\/paginated\" \u3067\u7d42\u308f\u308b\u304b\u3067\u578b\u3092\u81ea\u52d5\u5206\u5c90\ntype ApiResponse&lt;T extends keyof ApiEndpoints&gt; =\n  T extends `${string}\/paginated`\n    ? PaginatedResponse&lt;ApiEndpoints[T]&gt;\n    : ApiEndpoints[T];\n\n\/\/ \u4f7f\u7528\u4f8b\uff1a\u578b\u304c\u81ea\u52d5\u3067\u6c7a\u307e\u308b\ntype UserListResponse   = ApiResponse&lt;'users\/paginated'&gt;; \/\/ PaginatedResponse&lt;User&gt;\ntype UserDetailResponse = ApiResponse&lt;'users\/detail'&gt;;    \/\/ User<\/code><\/pre>\n\n\n<h3 class=\"wp-block-heading\">\u30c6\u30af\u30cb\u30c3\u30af2\uff1aTemplate Literal Types\u3067URL\u3092\u578b\u5b89\u5168\u306b\u5b9a\u7fa9\u3059\u308b<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">URL\u306e\u30d1\u30b9\u69cb\u9020\u3092\u578b\u3067\u8868\u73fe\u3059\u308b\u3068\u3001\u300c\u5b58\u5728\u3057\u306a\u3044\u30d1\u30b9\u3092\u6307\u5b9a\u3057\u305f\u3089\u30b3\u30f3\u30d1\u30a4\u30eb\u30a8\u30e9\u30fc\u306b\u306a\u308b\u300d\u306e\u3067typo\u306b\u3088\u308b\u30d0\u30b0\u3092100%\u9632\u3052\u307e\u3059\u3002<\/p>\n\n\n<pre><code>\/\/ API\u30d0\u30fc\u30b8\u30e7\u30f3\u30fb\u30ea\u30bd\u30fc\u30b9\u30fbID\u306e\u578b\u5b9a\u7fa9\ntype ApiVersion   = 'v1' | 'v2';\ntype ResourceType = 'users' | 'posts' | 'comments';\n\n\/\/ URL\u69cb\u9020\u3092\u578b\u30ec\u30d9\u30eb\u3067\u5b9a\u7fa9\ntype ApiUrl =\n  | `\/api\/${ApiVersion}\/${ResourceType}`\n  | `\/api\/${ApiVersion}\/${ResourceType}\/${number}`;\n\n\/\/ URL\u30d1\u30b9\u304b\u3089\u30ec\u30b9\u30dd\u30f3\u30b9\u578b\u3092\u81ea\u52d5\u63a8\u8ad6\u3059\u308b\u30de\u30c3\u30d7\ntype ApiMap = {\n  '\/api\/v1\/users': User[];\n  '\/api\/v1\/users\/:id': User;\n  '\/api\/v1\/posts': Post[];\n  '\/api\/v1\/posts\/:id': Post;\n};\n\n\/\/ \u30d1\u30b9\u306e\u30d1\u30e9\u30e1\u30fc\u30bf\u90e8\u5206\u3092\u5b9f\u969b\u306e\u5024\u306b\u7f6e\u304d\u63db\u3048\u308b\u578b\ntype ReplaceParams&lt;T extends string&gt; =\n  T extends `${infer Start}:${infer _Param}\/${infer Rest}`\n    ? `${Start}${string | number}\/${ReplaceParams&lt;Rest&gt;}`\n    : T extends `${infer Start}:${infer _Param}`\n      ? `${Start}${string | number}`\n      : T;\n\n\/\/ \u578b\u5b89\u5168\u306afetch\u30e6\u30fc\u30c6\u30a3\u30ea\u30c6\u30a3\nasync function fetchTypedApi&lt;T extends keyof ApiMap&gt;(\n  path: ReplaceParams&lt;T&gt;\n): Promise&lt;ApiMap[T]&gt; {\n  const response = await fetch(path);\n  return response.json();\n}\n\n\/\/ \u4f7f\u7528\u4f8b\uff1a\u578b\u304c\u81ea\u52d5\u3067\u4ed8\u304f\nconst users = await fetchTypedApi('\/api\/v1\/users');       \/\/ User[]\nconst user  = await fetchTypedApi('\/api\/v1\/users\/1');     \/\/ User\n\/\/ fetchTypedApi('\/api\/v1\/invalid');                      \/\/ \u30b3\u30f3\u30d1\u30a4\u30eb\u30a8\u30e9\u30fc<\/code><\/pre>\n\n\n<h3 class=\"wp-block-heading\">\u30c6\u30af\u30cb\u30c3\u30af3\uff1a\u30a4\u30f3\u30bf\u30fc\u30bb\u30af\u30b7\u30e7\u30f3\u578b\u3068\u30df\u30c3\u30af\u30b9\u30a4\u30f3\u3067API\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u3092\u5408\u6210\u3059\u308b<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u300c\u8a8d\u8a3c\u6a5f\u80fd\u3060\u3051\u6301\u3064\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u300d\u300c\u30ad\u30e3\u30c3\u30b7\u30e5\u6a5f\u80fd\u3060\u3051\u6301\u3064\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u300d\u306a\u3069\u3001\u5fc5\u8981\u306a\u6a5f\u80fd\u3092\u7d44\u307f\u5408\u308f\u305b\u305f\u578b\u5b89\u5168\u306aAPI\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u3092\u30df\u30c3\u30af\u30b9\u30a4\u30f3\u3067\u4f5c\u308c\u307e\u3059\u3002<\/p>\n\n\n<pre><code>\/\/ \u57fa\u672c\u7684\u306aHTTP\u30ea\u30af\u30a8\u30b9\u30c8\u6a5f\u80fd\ntype BaseClient = {\n  request&lt;T&gt;(url: string, options?: RequestInit): Promise&lt;T&gt;;\n};\n\n\/\/ \u30ad\u30e3\u30c3\u30b7\u30e5\u6a5f\u80fd\ntype CacheFeature = {\n  cache: Map&lt;string, unknown&gt;;\n  getCached&lt;T&gt;(key: string): T | undefined;\n  setCached&lt;T&gt;(key: string, value: T): void;\n};\n\n\/\/ \u8a8d\u8a3c\u6a5f\u80fd\ntype AuthFeature = {\n  setToken(token: string): void;\n  getAuthHeaders(): Record&lt;string, string&gt;;\n};\n\n\/\/ \u30df\u30c3\u30af\u30b9\u30a4\u30f3\uff1a\u30ad\u30e3\u30c3\u30b7\u30e5\u6a5f\u80fd\u3092\u8ffd\u52a0\ntype ApiClientConstructor = new (...args: unknown[]) =&gt; BaseClient;\n\nfunction withCache&lt;T extends ApiClientConstructor&gt;(Base: T) {\n  return class extends Base implements CacheFeature {\n    cache = new Map&lt;string, unknown&gt;();\n    getCached&lt;R&gt;(key: string) { return this.cache.get(key) as R | undefined; }\n    setCached&lt;R&gt;(key: string, value: R) { this.cache.set(key, value); }\n\n    async request&lt;R&gt;(url: string, options?: RequestInit): Promise&lt;R&gt; {\n      const cacheKey = `${url}:${JSON.stringify(options)}`;\n      const cached = this.getCached&lt;R&gt;(cacheKey);\n      if (cached) return cached;\n      const result = await super.request&lt;R&gt;(url, options);\n      this.setCached(cacheKey, result);\n      return result;\n    }\n  };\n}\n\n\/\/ \u8a8d\u8a3c\u30df\u30c3\u30af\u30b9\u30a4\u30f3\nfunction withAuth&lt;T extends ApiClientConstructor&gt;(Base: T) {\n  return class extends Base implements AuthFeature {\n    private token = '';\n    setToken(token: string) { this.token = token; }\n    getAuthHeaders() {\n      return this.token ? { Authorization: `Bearer ${this.token}` } : {};\n    }\n    async request&lt;R&gt;(url: string, options?: RequestInit): Promise&lt;R&gt; {\n      const headers = this.getAuthHeaders();\n      return super.request&lt;R&gt;(url, { ...options, headers: { ...options?.headers, ...headers } });\n    }\n  };\n}\n\n\/\/ \u57fa\u672c\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\nclass HttpClient implements BaseClient {\n  async request&lt;T&gt;(url: string, options?: RequestInit): Promise&lt;T&gt; {\n    const res = await fetch(url, options);\n    return res.json();\n  }\n}\n\n\/\/ \u6a5f\u80fd\u3092\u5408\u6210\u3057\u305fAPI\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\nconst EnhancedClient = withAuth(withCache(HttpClient));\nconst apiClient = new EnhancedClient();\napiClient.setToken('my-token');\nconst data = await apiClient.request&lt;User&gt;('\/api\/users\/1');<\/code><\/pre>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Zod\u306e\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316\uff5c\u578b\u5b89\u5168\u6027\u3092\u4fdd\u3061\u306a\u304c\u3089\u672c\u756a\u74b0\u5883\u306e\u901f\u5ea6\u3092\u6539\u5584\u3059\u308b<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Zod\u306e <code>parse<\/code> \u306f\u4fbf\u5229\u3067\u3059\u304c\u3001\u5927\u91cf\u30c7\u30fc\u30bf\u3092\u6271\u3046\u753b\u9762\u3067\u306f\u51e6\u7406\u6642\u9593\u304c\u7121\u8996\u3067\u304d\u306a\u304f\u306a\u308a\u307e\u3059\u30023\u3064\u306e\u6226\u7565\u3067\u578b\u5b89\u5168\u6027\u3092\u7dad\u6301\u3057\u306a\u304c\u3089\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u3092\u6539\u5584\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"alignleft size-full\"><img decoding=\"async\" src=\"img\/zod_performance_optimization.png\" alt=\"Zod\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316\u306e3\u6226\u7565\u56f3\" class=\"wp-image-12385\"\/><\/figure>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\">Zod\u306e\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u5b9f\u6e2c\u5024<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>\u30c7\u30fc\u30bf\u30b5\u30a4\u30ba<\/th><th>\u691c\u8a3c\u6642\u9593\uff08\u5e73\u5747\uff09<\/th><th>\u30a2\u30d7\u30ea\u3078\u306e\u5f71\u97ff<\/th><\/tr><\/thead><tbody>\n<tr><td>\u5c0f\uff0810\u9805\u76ee\u4ee5\u4e0b\uff09<\/td><td>0.5ms<\/td><td>\u7121\u8996\u3067\u304d\u308b\u30ec\u30d9\u30eb<\/td><\/tr>\n<tr><td>\u4e2d\uff08100\u9805\u76ee\u7a0b\u5ea6\uff09<\/td><td>5ms<\/td><td>\u308f\u305a\u304b\u306b\u4f53\u611f\u53ef\u80fd<\/td><\/tr>\n<tr><td>\u5927\uff081,000\u9805\u76ee\u4ee5\u4e0a\uff09<\/td><td>50ms<\/td><td>\u660e\u78ba\u306b\u9045\u5ef6\u3092\u611f\u3058\u308b<\/td><\/tr>\n<\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">\u6226\u75651\uff1a\u958b\u767a\u74b0\u5883\u306e\u307f\u53b3\u5bc6\u691c\u8a3c\u3001\u672c\u756a\u306f\u8efd\u91cf\u30c1\u30a7\u30c3\u30af<\/h3>\n\n\n<pre><code>function validateResponse&lt;T&gt;(data: unknown, schema: z.ZodType&lt;T&gt;): T {\n  if (process.env.NODE_ENV === 'development') {\n    \/\/ \u958b\u767a\u6642\u306f\u5168\u30d5\u30a3\u30fc\u30eb\u30c9\u3092\u53b3\u5bc6\u306b\u691c\u8a3c\u3057\u3066\u30d0\u30b0\u3092\u65e9\u671f\u767a\u898b\n    return schema.parse(data);\n  }\n  \/\/ \u672c\u756a\u306f\u578b\u30a2\u30b5\u30fc\u30b7\u30e7\u30f3\u306e\u307f\uff08\u958b\u767a\u6642\u306b\u691c\u8a3c\u6e08\u307f\u306e\u30c7\u30fc\u30bf\u304c\u6765\u308b\u524d\u63d0\uff09\n  return data as T;\n}\n\/\/ \u3053\u306e\u5909\u66f4\u3067\u672c\u756a\u306e\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u304c30%\u5411\u4e0a\u3057\u305f\u5b9f\u7e3e\u3042\u308a<\/code><\/pre>\n\n\n<h3 class=\"wp-block-heading\">\u6226\u75652\uff1a\u91cd\u8981\u306a\u5883\u754c\u3067\u306e\u307f\u691c\u8a3c\u3059\u308b\uff08\u6bb5\u968e\u7684\u691c\u8a3c\uff09<\/h3>\n\n\n<pre><code>\/\/ \u5168\u30d5\u30a3\u30fc\u30eb\u30c9\u3092\u4e00\u5ea6\u306b\u691c\u8a3c\u305b\u305a\u3001\u5b9f\u969b\u306b\u4f7f\u3046\u76f4\u524d\u306b\u691c\u8a3c\u3059\u308b\nfunction processUserData(data: unknown): User {\n  \/\/ \u307e\u305a\u69cb\u9020\u3060\u3051\u78ba\u8a8d\uff08\u8efd\u91cf\uff09\n  if (!data || typeof data !== 'object' || !('id' in data)) {\n    throw new Error('Invalid user data structure');\n  }\n\n  const user = data as Partial&lt;User&gt;;\n\n  \/\/ id \u3092\u4f7f\u3046\u76f4\u524d\u306b\u3060\u3051\u691c\u8a3c\n  if (typeof user.id !== 'number') throw new Error('Invalid user ID');\n\n  \/\/ email \u3092\u4f7f\u3046\u76f4\u524d\u306b\u3060\u3051\u691c\u8a3c\uff08\u4f7f\u308f\u306a\u3044\u5834\u5408\u306f\u691c\u8a3c\u3057\u306a\u3044\uff09\n  if (user.email !== undefined &amp;&amp; typeof user.email !== 'string') {\n    throw new Error('Invalid email');\n  }\n\n  return user as User;\n}\n\/\/ \u3053\u306e\u65b9\u6cd5\u3067\u5927\u898f\u6a21\u30c7\u30fc\u30bf\u30bb\u30c3\u30c8\u306e\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u304c70%\u5411\u4e0a\u3057\u305f\u5b9f\u7e3e\u3042\u308a<\/code><\/pre>\n\n\n<h3 class=\"wp-block-heading\">\u6226\u75653\uff1a\u691c\u8a3c\u7d50\u679c\u3092\u30ad\u30e3\u30c3\u30b7\u30e5\u3059\u308b<\/h3>\n\n\n<pre><code>const validationCache = new Map&lt;string, boolean&gt;();\n\nfunction validateWithCache&lt;T&gt;(\n  data: unknown,\n  schema: z.ZodType&lt;T&gt;,\n  cacheKey: string\n): T {\n  \/\/ \u540c\u3058\u30ad\u30fc\u3067\u65e2\u306b\u691c\u8a3c\u6e08\u307f\u306a\u3089\u30b9\u30ad\u30c3\u30d7\n  if (validationCache.has(cacheKey)) return data as T;\n\n  const result = schema.safeParse(data);\n  if (result.success) {\n    validationCache.set(cacheKey, true);\n    return data as T;\n  }\n  throw new Error(`Validation failed: ${result.error.message}`);\n}<\/code><\/pre>\n\n\n<pre class=\"wp-block-code\"><code>\u3010Zod\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316\u306e\u307e\u3068\u3081\u3011\n\n\u2705 \u958b\u767a\u74b0\u5883  \u2192 schema.parse() \u3067\u5b8c\u5168\u691c\u8a3c\uff08\u30d0\u30b0\u3092\u65e9\u671f\u767a\u898b\uff09\n\u2705 \u672c\u756a\u74b0\u5883  \u2192 \u8efd\u91cf\u30c1\u30a7\u30c3\u30af\u306e\u307f or \u578b\u30a2\u30b5\u30fc\u30b7\u30e7\u30f3\n\u2705 \u5927\u91cf\u30c7\u30fc\u30bf \u2192 \u6bb5\u968e\u7684\u691c\u8a3c\uff08\u4f7f\u3046\u76f4\u524d\u306b\u4f7f\u3046\u30d5\u30a3\u30fc\u30eb\u30c9\u3060\u3051\u691c\u8a3c\uff09\n\u2705 \u7e70\u308a\u8fd4\u3057\u691c\u8a3c \u2192 \u691c\u8a3c\u7d50\u679c\u3092\u30ad\u30e3\u30c3\u30b7\u30e5\u3057\u3066\u30b9\u30ad\u30c3\u30d7\n\n\u274c \u907f\u3051\u308b\u3079\u304d\uff1a\n- 1,000\u4ef6\u4ee5\u4e0a\u306e\u30ea\u30b9\u30c8\u3092\u4e00\u62ec parse() \u3059\u308b\n- \u672c\u756a\u3067\u3082\u6bce\u56de\u30cd\u30b9\u30c8\u306e\u6df1\u3044\u30b9\u30ad\u30fc\u30de\u3092\u5168\u691c\u8a3c\u3059\u308b<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u578b\u99c6\u52d5\u958b\u767a\u306e\u5b9f\u8df5\u30d7\u30ed\u30bb\u30b9\uff5c\u578b\u5b9a\u7fa9\u2192\u30e2\u30c3\u30af\u2192\u30c6\u30b9\u30c8\u2192\u672c\u5b9f\u88c5\u306e4\u30b9\u30c6\u30c3\u30d7<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\u578b\u99c6\u52d5\u958b\u767a\u3068\u306f\u3001\u5b9f\u88c5\u524d\u306b\u578b\u5b9a\u7fa9\u3092\u5148\u306b\u884c\u3044\u3001\u578b\u3092\u4e2d\u5fc3\u306b\u8a2d\u8a08\u3092\u9032\u3081\u308b\u624b\u6cd5\u3067\u3059\u3002\u5546\u54c1\u691c\u7d22\u6a5f\u80fd\u306e\u4f8b\u3067\u5177\u4f53\u7684\u306a4\u30b9\u30c6\u30c3\u30d7\u3092\u7d39\u4ecb\u3057\u307e\u3059\u3002<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"alignleft size-full\"><img decoding=\"async\" src=\"img\/type_driven_development_process.png\" alt=\"\u578b\u99c6\u52d5\u958b\u767a\u306e4\u30b9\u30c6\u30c3\u30d7\u30d5\u30ed\u30fc\u56f3\uff08\u578b\u5b9a\u7fa9\u2192\u30e2\u30c3\u30af\u2192\u30c6\u30b9\u30c8\u2192\u672c\u5b9f\u88c5\uff09\" class=\"wp-image-12386\"\/><\/figure>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\">\u30b9\u30c6\u30c3\u30d71\uff1a\u6a5f\u80fd\u306b\u5fc5\u8981\u306a\u578b\u3092\u3059\u3079\u3066\u5148\u306b\u5b9a\u7fa9\u3059\u308b<\/h3>\n\n\n<pre><code>\/\/ \u5546\u54c1\u691c\u7d22\u6a5f\u80fd\u306e\u578b\u5b9a\u7fa9\uff08\u5b9f\u88c5\u524d\u306b\u5148\u306b\u6c7a\u3081\u308b\uff09\ninterface SearchFilters {\n  category?: string;\n  minPrice?: number;\n  maxPrice?: number;\n  sortBy: 'price' | 'popularity' | 'newest';\n  sortOrder: 'asc' | 'desc';\n  page: number;\n  limit: number;\n}\n\ninterface Product {\n  id: number;\n  name: string;\n  price: number;\n  category: string;\n}\n\ninterface SearchResult {\n  products: Product[];\n  totalResults: number;\n  currentPage: number;\n  totalPages: number;\n  appliedFilters: SearchFilters;\n}\n\ninterface SearchError {\n  code: 'INVALID_FILTER' | 'SERVICE_UNAVAILABLE';\n  message: string;\n  details?: Record&lt;string, string&gt;;\n}\n\n\/\/ API\u95a2\u6570\u306e\u30b7\u30b0\u30cd\u30c1\u30e3\u3082\u5148\u306b\u6c7a\u3081\u308b\ntype SearchProducts = (filters: SearchFilters) =&gt; Promise&lt;SearchResult&gt;;<\/code><\/pre>\n\n\n<h3 class=\"wp-block-heading\">\u30b9\u30c6\u30c3\u30d72\uff1a\u578b\u306b\u57fa\u3065\u3044\u3066\u30e2\u30c3\u30af\u30c7\u30fc\u30bf\u3068UI\u3092\u4e26\u884c\u958b\u767a\u3059\u308b<\/h3>\n\n\n<pre><code>\/\/ \u30e2\u30c3\u30af\u5b9f\u88c5\uff08\u30d0\u30c3\u30af\u30a8\u30f3\u30c9API\u5b8c\u6210\u524d\u304b\u3089\u30d5\u30ed\u30f3\u30c8\u3092\u958b\u767a\u3067\u304d\u308b\uff09\nconst mockSearchResult: SearchResult = {\n  products: [\n    { id: 1, name: '\u5546\u54c1A', price: 1000, category: 'electronics' },\n    { id: 2, name: '\u5546\u54c1B', price: 2500, category: 'electronics' },\n  ],\n  totalResults: 243,\n  currentPage: 1,\n  totalPages: 25,\n  appliedFilters: {\n    sortBy: 'popularity',\n    sortOrder: 'desc',\n    page: 1,\n    limit: 10,\n  },\n};\n\nconst searchProducts: SearchProducts = async (_filters) =&gt; {\n  return mockSearchResult; \/\/ \u30e2\u30c3\u30af\u5b9f\u88c5\n};<\/code><\/pre>\n\n\n<h3 class=\"wp-block-heading\">\u30b9\u30c6\u30c3\u30d73\uff1a\u578b\u5b9a\u7fa9\u306b\u57fa\u3065\u3044\u305f\u30c6\u30b9\u30c8\u3092\u4f5c\u6210\u3059\u308b<\/h3>\n\n\n<pre><code>describe('\u5546\u54c1\u691c\u7d22\u6a5f\u80fd', () =&gt; {\n  it('\u6b63\u3057\u3044\u30d5\u30a3\u30eb\u30bf\u30fc\u3067\u691c\u7d22\u7d50\u679c\u3092\u8fd4\u3059', async () =&gt; {\n    const filters: SearchFilters = {\n      category: 'electronics',\n      sortBy: 'price',\n      sortOrder: 'asc',\n      page: 1,\n      limit: 10,\n    };\n    const result = await searchProducts(filters);\n    expect(result.products).toBeInstanceOf(Array);\n    expect(result.totalResults).toBeGreaterThanOrEqual(0);\n  });\n\n  it('\u7121\u52b9\u306a\u30d5\u30a3\u30eb\u30bf\u30fc\u3067\u30a8\u30e9\u30fc\u306b\u306a\u308b\uff08\u578b\u3067\u30ac\u30fc\u30c9\uff09', async () =&gt; {\n    \/\/ @ts-expect-error - \u610f\u56f3\u7684\u306b\u578b\u30a8\u30e9\u30fc\u3092\u767a\u751f\u3055\u305b\u3066\u30c6\u30b9\u30c8\n    const invalidFilters = { sortBy: 'invalid', page: 1, limit: 10 };\n    await expect(searchProducts(invalidFilters)).rejects.toThrow();\n  });\n});<\/code><\/pre>\n\n\n<h3 class=\"wp-block-heading\">\u30b9\u30c6\u30c3\u30d74\uff1a\u30e2\u30c3\u30af\u3092\u672c\u5b9f\u88c5\u306b\u5dee\u3057\u66ff\u3048\u308b<\/h3>\n\n\n<pre><code>const searchProducts: SearchProducts = async (filters) =&gt; {\n  try {\n    const queryParams = new URLSearchParams();\n    Object.entries(filters).forEach(([key, value]) =&gt; {\n      if (value !== undefined) queryParams.append(key, String(value));\n    });\n\n    const response = await fetch(`\/api\/products\/search?${queryParams}`);\n\n    if (!response.ok) {\n      const errorData: SearchError = await response.json();\n      throw new Error(errorData.message);\n    }\n\n    const data: SearchResult = await response.json();\n    return data;\n  } catch (error) {\n    console.error('\u5546\u54c1\u691c\u7d22\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f', error);\n    throw error;\n  }\n};\n\/\/ \u578b\u30b7\u30b0\u30cd\u30c1\u30e3\u304c\u540c\u3058\u306a\u306e\u3067\u30e2\u30c3\u30af\u304b\u3089\u672c\u5b9f\u88c5\u3078\u306e\u5dee\u3057\u66ff\u3048\u304c\u5b89\u5168<\/code><\/pre>\n\n\n<pre class=\"wp-block-code\"><code>\u3010\u578b\u99c6\u52d5\u958b\u767a\u306e\u5b9f\u7e3e\uff08\u91d1\u878d\u7cfbWeb\u30a2\u30d7\u30ea 6\u30f6\u6708\u5f8c\uff09\u3011\n\n\u2705 \u578b\u95a2\u9023\u306e\u30d0\u30b0\u304c78%\u6e1b\u5c11\n\u2705 \u65b0\u6a5f\u80fd\u306e\u5b9f\u88c5\u6642\u9593\u304c\u5e73\u574732%\u77ed\u7e2e\n\u2705 \u30b3\u30fc\u30c9\u30ec\u30d3\u30e5\u30fc\u6642\u9593\u304c40%\u6e1b\u5c11\uff08\u578b\u304c\u660e\u78ba\u3067\u8aad\u307f\u3084\u3059\u3044\uff09\n\u2705 \u65b0\u30e1\u30f3\u30d0\u30fc\u306e\u7acb\u3061\u4e0a\u304c\u308a\u6642\u9593\u304c2\u9031\u9593\u21921\u9031\u9593\u306b\u77ed\u7e2e\n\u2705 API\u4ed5\u69d8\u66f8\u3078\u306e\u53c2\u7167\u304c70%\u6e1b\u5c11\uff08\u578b\u3092\u898b\u308b\u3060\u3051\u3067\u4ed5\u69d8\u304c\u5206\u304b\u308b\uff09<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u3088\u304f\u3042\u308b\u8cea\u554f<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Q1. Zod\u30b9\u30ad\u30fc\u30de\u304c50\u500b\u4ee5\u4e0a\u306b\u5897\u3048\u3066\u7ba1\u7406\u304c\u5927\u5909\u3067\u3059\u3002\u3069\u3046\u6574\u7406\u3059\u308c\u3070\u3044\u3044\u3067\u3059\u304b\uff1f<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>A.<\/strong> <code>src\/shared\/schemas.ts<\/code> \u306b\u5168\u30b9\u30ad\u30fc\u30de\u3092\u96c6\u7d04\u3057\u3001<strong>BaseSchema \u304b\u3089 <code>extend<\/code> \u3067\u6bb5\u968e\u7684\u306b\u5e83\u3052\u308b<\/strong>\u8a2d\u8a08\u306b\u3059\u308b\u306e\u304c\u57fa\u672c\u3067\u3059\u3002\u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8\u3054\u3068\u306b\u30d5\u30a1\u30a4\u30eb\u3092\u5206\u3051\u308b\u306e\u3067\u306f\u306a\u304f\u3001\u30ea\u30bd\u30fc\u30b9\uff08User\u30fbProduct \u306a\u3069\uff09\u3092\u5358\u4f4d\u306b\u30b9\u30ad\u30fc\u30de\u3092\u307e\u3068\u3081\u308b\u3068\u7ba1\u7406\u3057\u3084\u3059\u304f\u306a\u308a\u307e\u3059\u3002\u3053\u306e\u65b9\u6cd5\u3067\u30b9\u30ad\u30fc\u30de\u306e\u91cd\u8907\u309280%\u524a\u6e1b\u3057\u305f\u5b9f\u7e3e\u304c\u3042\u308a\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Q2. \u30e2\u30c3\u30af\u3068\u672c\u756a\u3067\u578b\u304c\u305a\u308c\u308b\u306e\u3092\u9632\u3050\u306b\u306f\u3069\u3046\u3059\u308c\u3070\u3044\u3044\u3067\u3059\u304b\uff1f<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>A.<\/strong> <strong>\u300c\u5358\u4e00\u30bd\u30fc\u30b9\u30aa\u30d6\u30c8\u30a5\u30eb\u30fc\u30b9\u300d<\/strong>\u3092\u5fb9\u5e95\u3059\u308b\u3053\u3068\u304c\u6700\u3082\u52b9\u679c\u7684\u3067\u3059\u3002\u30b5\u30fc\u30d0\u30fc\u30fb\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u30fb\u30e2\u30c3\u30af\u3059\u3079\u3066\u3067 <code>src\/shared\/schemas.ts<\/code> \u306e\u540c\u3058Zod\u30b9\u30ad\u30fc\u30de\u3092\u4f7f\u3044\u3001MSW\u306e\u30cf\u30f3\u30c9\u30e9\u5185\u3067\u3082\u305d\u306e\u30b9\u30ad\u30fc\u30de\u3067\u691c\u8a3c\u3057\u3066\u304f\u3060\u3055\u3044\u3002\u30e2\u30c3\u30af\u5c02\u7528\u306e\u578b\u3084\u72ec\u81ea\u306e interface \u3092\u4f5c\u308b\u3068\u5fc5\u305a\u30ba\u30ec\u304c\u751f\u3058\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Q3. tRPC\u3092\u65e2\u5b58REST API\u3068\u5171\u5b58\u3055\u305b\u308b\u3053\u3068\u306f\u3067\u304d\u307e\u3059\u304b\uff1f<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>A.<\/strong> \u3067\u304d\u307e\u3059\u3002\u65e2\u5b58REST\u3092\u6b8b\u3057\u306a\u304c\u3089\u65b0\u3057\u3044\u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8\u306ftRPC\u3067\u5b9f\u88c5\u3059\u308b\u300c\u6bb5\u968e\u7684\u79fb\u884c\u300d\u304c\u73fe\u5b9f\u7684\u3067\u3059\u3002\u79fb\u884c\u671f\u9593\u4e2d\u306f\u3001tRPC\u306eprocedure\u5185\u304b\u3089\u65e2\u5b58REST API\u3092\u547c\u3073\u51fa\u3057\u3001Zod\u3067\u691c\u8a3c\u3057\u3066\u8fd4\u3059\u300c\u30d7\u30ed\u30ad\u30b7\u30d1\u30bf\u30fc\u30f3\u300d\u304c\u4f7f\u3048\u307e\u3059\u3002\u4e00\u5ea6\u306b\u5168\u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8\u3092\u79fb\u884c\u3057\u3088\u3046\u3068\u305b\u305a\u3001\u512a\u5148\u5ea6\u306e\u9ad8\u3044\u3082\u306e\u304b\u3089\u9806\u6b21\u79fb\u884c\u3059\u308b\u3053\u3068\u3092\u63a8\u5968\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Q4. Template Literal Types\u306f\u5b9f\u52d9\u3067\u4f7f\u3046\u306b\u306f\u96e3\u3057\u3059\u304e\u307e\u305b\u3093\u304b\uff1f<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>A.<\/strong> \u300cURL\u3092\u578b\u3067\u8868\u73fe\u3059\u308b\u300d\u3060\u3051\u306a\u3089\u6bd4\u8f03\u7684\u30b7\u30f3\u30d7\u30eb\u3067\u3059\u3002\u307e\u305a <code>type ApiUrl = '\/api\/v1\/users' | '\/api\/v1\/users\/${number}'<\/code> \u306e\u3088\u3046\u306a\u30e6\u30cb\u30aa\u30f3\u578b\u304b\u3089\u59cb\u3081\u3001\u6163\u308c\u3066\u304d\u305f\u3089Template Literal Types\u306b\u79fb\u884c\u3059\u308b\u3068\u3088\u3044\u3067\u3057\u3087\u3046\u3002ts-rest\u3092\u4f7f\u3048\u3070URL\u578b\u3092\u81ea\u5206\u3067\u66f8\u304b\u306a\u304f\u3066\u3082\u30d1\u30b9\u304c\u578b\u5b89\u5168\u306b\u306a\u308b\u306e\u3067\u3001\u30e9\u30a4\u30d6\u30e9\u30ea\u306b\u4efb\u305b\u308b\u9078\u629e\u80a2\u3082\u3042\u308a\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Q5. faker.js + Zod\u3067\u30e2\u30c3\u30af\u3092\u81ea\u52d5\u751f\u6210\u3059\u308b\u65b9\u6cd5\u306e\u30c7\u30e1\u30ea\u30c3\u30c8\u306f\u4f55\u3067\u3059\u304b\uff1f<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>A.<\/strong> \u30e9\u30f3\u30c0\u30e0\u306a\u30c7\u30fc\u30bf\u304c\u751f\u6210\u3055\u308c\u308b\u305f\u3081\u3001<strong>\u30c6\u30b9\u30c8\u306e\u518d\u73fe\u6027\u304c\u4e0b\u304c\u308b<\/strong>\u3053\u3068\u304c\u4e3b\u306a\u30c7\u30e1\u30ea\u30c3\u30c8\u3067\u3059\u3002\u7279\u5b9a\u306e\u5024\u3067\u306e\u307f\u518d\u73fe\u3059\u308b\u30d0\u30b0\u3092\u767a\u898b\u3057\u306b\u304f\u304f\u306a\u308a\u307e\u3059\u3002\u5bfe\u7b56\u3068\u3057\u3066\u3001\u30b7\u30ca\u30ea\u30aa\u30c6\u30b9\u30c8\u306b\u306f\u56fa\u5b9a\u306e\u30e2\u30c3\u30af\u30c7\u30fc\u30bf\u3092\u4f7f\u3044\u3001faker.js \u306f\u300c\u591a\u69d8\u306a\u30c7\u30fc\u30bf\u30d1\u30bf\u30fc\u30f3\u306e\u30b9\u30e2\u30fc\u30af\u30c6\u30b9\u30c8\u300d\u3084\u300cUI\u306e\u898b\u305f\u76ee\u78ba\u8a8d\u300d\u306b\u9650\u5b9a\u3059\u308b\u3068\u826f\u3044\u30d0\u30e9\u30f3\u30b9\u306b\u306a\u308a\u307e\u3059\u3002<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n<div class=\"swell-block-balloon\"><div class=\"c-balloon -bln-left\" data-col=\"gray\"><div class=\"c-balloon__icon -square\"><img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/withcode.tech\/media\/wp-content\/uploads\/2025\/06\/\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8-2025-06-15-14.48.08.jpg\" alt=\"\" class=\"c-balloon__iconImg\" width=\"80px\" height=\"80px\"><span class=\"c-balloon__iconName\">\u751f\u5f92<\/span><\/div><div class=\"c-balloon__body -speaking -border-none\"><div class=\"c-balloon__text\">\n  <p class=\"balloon balloon-left\"><strong>\u578b\u30c6\u30af\u30cb\u30c3\u30af\u306f\u3059\u3054\u3044\u3067\u3059\u304c\u3001\u521d\u5fc3\u8005\u306b\u306f\u96e3\u3057\u3059\u304e\u307e\u305b\u3093\u304b\uff1f\u307e\u305a\u4f55\u304b\u3089\u59cb\u3081\u308c\u3070\u3044\u3044\u3067\u3057\u3087\u3046\u304b\uff1f<\/strong><\/p>\n  <span class=\"c-balloon__shapes\"><span class=\"c-balloon__before\"><\/span><span class=\"c-balloon__after\"><\/span><\/span><\/div><\/div><\/div><\/div>\n\n  <div class=\"swell-block-balloon\"><div class=\"c-balloon -bln-right\" data-col=\"gray\"><div class=\"c-balloon__icon -square\"><img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/withcode.tech\/media\/wp-content\/uploads\/2025\/06\/\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8-2025-06-15-15.11.23.jpg\" alt=\"\" class=\"c-balloon__iconImg\" width=\"80px\" height=\"80px\"><span class=\"c-balloon__iconName\">\u30da\u30f3\u535a\u58eb<\/span><\/div><div class=\"c-balloon__body -speaking -border-none\"><div class=\"c-balloon__text\">\n  <p class=\"balloon balloon-right\"><strong>\u96e3\u3057\u304f\u898b\u3048\u3066\u3082\u3001\u9806\u756a\u3092\u5b88\u308c\u3070\u8ab0\u3067\u3082\u7fd2\u5f97\u3067\u304d\u308b\u3093\u3058\u3083\u3002\u307e\u305a\u300cZod\u30b9\u30ad\u30fc\u30de\u30921\u3064\u66f8\u3044\u3066API\u30ec\u30b9\u30dd\u30f3\u30b9\u3092\u691c\u8a3c\u3059\u308b\u300d\u3060\u3051\u304b\u3089\u59cb\u3081\u308b\u306e\u304c\u6b63\u89e3\u3058\u3083\u3002JavaScript\u306e\u57fa\u790e\u3068API\u306e\u4ed5\u7d44\u307f\u3092\u7406\u89e3\u3057\u3066\u3044\u308c\u3070\u3001WithCode\u3067\u5b66\u3093\u3060\u77e5\u8b58\u306e\u4e0a\u306bZod\u3092\u4e57\u305b\u308b\u306e\u306f\u305d\u308c\u307b\u3069\u96e3\u3057\u304f\u306a\u3044\u306f\u305a\u3058\u3083\u305e\uff01<\/strong><\/p>\n  <span class=\"c-balloon__shapes\"><span class=\"c-balloon__before\"><\/span><span class=\"c-balloon__after\"><\/span><\/span><\/div><\/div><\/div><\/div>\n\n  <div class=\"swell-block-balloon\"><div class=\"c-balloon -bln-left\" data-col=\"gray\"><div class=\"c-balloon__icon -square\"><img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/withcode.tech\/media\/wp-content\/uploads\/2025\/06\/\u30b9\u30af\u30ea\u30fc\u30f3\u30b7\u30e7\u30c3\u30c8-2025-06-15-15.06.05.jpg\" alt=\"\" class=\"c-balloon__iconImg\" width=\"80px\" height=\"80px\"><span class=\"c-balloon__iconName\">\u751f\u5f92<\/span><\/div><div class=\"c-balloon__body -speaking -border-none\"><div class=\"c-balloon__text\">\n  <p class=\"balloon balloon-left\"><strong>\u306a\u308b\u307b\u3069\uff01\u307e\u305a1\u3064\u306eAPI\u30a8\u30f3\u30c9\u30dd\u30a4\u30f3\u30c8\u306bZod\u3092\u8a66\u3057\u3066\u307f\u307e\u3059\u3002\u578b\u304c\u300c\u9632\u5177\u300d\u306b\u306a\u308b\u611f\u899a\u3001\u5c11\u3057\u3064\u304b\u3081\u3066\u304d\u307e\u3057\u305f\uff01<\/strong><\/p>\n  <span class=\"c-balloon__shapes\"><span class=\"c-balloon__before\"><\/span><span class=\"c-balloon__after\"><\/span><\/span><\/div><\/div><\/div><\/div>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u307e\u3068\u3081<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n  <li><strong>\u578b\u306e\u4e0d\u4e00\u81f4\u306e\u6839\u672c\u539f\u56e0<\/strong>\uff1a\u578b\u306e\u5b9a\u7fa9\u5834\u6240\u304c\u8907\u6570\u306b\u5206\u6563\u3057\u3066\u3044\u308b\u3053\u3068\u304c\u539f\u56e0\u3002\u5358\u4e00\u30bd\u30fc\u30b9\u306b\u96c6\u7d04\u3059\u308b\u3053\u3068\u3067\u89e3\u6c7a\u3067\u304d\u308b\u3002<\/li>\n  <li><strong>Zod\u30b9\u30ad\u30fc\u30de\u306e\u5408\u6210<\/strong>\uff1aBaseSchema \u2192 <code>extend<\/code> \u3067\u6bb5\u968e\u7684\u306b\u5e83\u3052\u3001<code>src\/shared\/schemas.ts<\/code> \u306b\u4e00\u5143\u7ba1\u7406\u3059\u308b\u3068\u30b9\u30ad\u30fc\u30de\u306e\u91cd\u8907\u309280%\u524a\u6e1b\u3067\u304d\u308b\u3002<\/li>\n  <li><strong>\u578b\u5b89\u5168\u306a\u30e2\u30c3\u30afAPI<\/strong>\uff1aMSW + Zod\u3067\u691c\u8a3c\u3057\u3001faker.js + Zod\u3067\u30ea\u30a2\u30eb\u306a\u30e2\u30c3\u30af\u30c7\u30fc\u30bf\u3092Zod\u30b9\u30ad\u30fc\u30de\u304b\u3089\u81ea\u52d5\u751f\u6210\u3059\u308b\u3002MSW + tRPC\u7d71\u5408\u3082\u53ef\u80fd\u3002<\/li>\n  <li><strong>\u9ad8\u5ea6\u306a\u578b\u30c6\u30af\u30cb\u30c3\u30af3\u9078<\/strong>\uff1a\u6761\u4ef6\u4ed8\u304d\u578b\uff08\u30ec\u30b9\u30dd\u30f3\u30b9\u578b\u306e\u81ea\u52d5\u30de\u30c3\u30d4\u30f3\u30b0\uff09\u30fbTemplate Literal Types\uff08URL\u306e\u578b\u5b89\u5168\u5316\uff09\u30fb\u30a4\u30f3\u30bf\u30fc\u30bb\u30af\u30b7\u30e7\u30f3\u578b\u3068\u30df\u30c3\u30af\u30b9\u30a4\u30f3\uff08API\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u306e\u6a5f\u80fd\u5408\u6210\uff09\u3002<\/li>\n  <li><strong>Zod\u306e\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316<\/strong>\uff1a\u958b\u767a\u6642\u306e\u307f\u53b3\u5bc6\u691c\u8a3c\u30fb\u672c\u756a\u306f\u8efd\u91cf\u30c1\u30a7\u30c3\u30af\u30fb\u6bb5\u968e\u7684\u691c\u8a3c\u30fb\u30ad\u30e3\u30c3\u30b7\u30e5\u306e3\u6226\u7565\u3067\u578b\u5b89\u5168\u6027\u3092\u4fdd\u3061\u306a\u304c\u3089\u901f\u5ea6\u3092\u6539\u5584\u3067\u304d\u308b\u3002<\/li>\n  <li><strong>\u578b\u99c6\u52d5\u958b\u767a<\/strong>\uff1a\u578b\u5b9a\u7fa9\u2192\u30e2\u30c3\u30af\u2192\u30c6\u30b9\u30c8\u2192\u672c\u5b9f\u88c5\u306e4\u30b9\u30c6\u30c3\u30d7\u3067\u9032\u3081\u308b\u3068\u3001\u30d0\u30b078%\u6e1b\u30fb\u5b9f\u88c5\u6642\u959332%\u77ed\u7e2e\u30fb\u30ec\u30d3\u30e5\u30fc\u6642\u959340%\u6e1b\u306e\u52b9\u679c\u304c\u671f\u5f85\u3067\u304d\u308b\u3002<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><strong><span class=\"swl-marker mark_yellow\">TypeScript\u578b\u5b89\u5168\u306aAPI\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u8a2d\u8a08\u306f\u3001\u30b9\u30ad\u30fc\u30de\u306e\u4e00\u5143\u7ba1\u7406\u3068\u6bb5\u968e\u7684\u306a\u578b\u30c6\u30af\u30cb\u30c3\u30af\u306e\u7fd2\u5f97\u3067\u3001\u578b\u5b89\u5168\u6027\u30fb\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u30fb\u958b\u767a\u52b9\u7387\u306e\u3059\u3079\u3066\u3092\u4e21\u7acb\u3067\u304d\u307e\u3059<\/span><\/strong>\u3002\u307e\u305a\u306f1\u3064\u306eAPI\u306bZod\u3092\u8a66\u3059\u3068\u3053\u308d\u304b\u3089\u59cb\u3081\u3066\u307f\u3066\u304f\u3060\u3055\u3044\u3002<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">WithCode\u3092\u4f53\u9a13\u3067\u304d\u308b\u521d\u7d1a\u30b3\u30fc\u30b9\u516c\u958b\u4e2d\uff01<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">\u521d\u7d1a\u30b3\u30fc\u30b9\uff08\u00a549,800\uff09\u304c\u5b8c\u5168\u7121\u6599\u306b\uff01<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n  <li><strong>\u671f\u9593\uff1a<\/strong>1\u9031\u9593<\/li>\n  <li><strong>\u5b66\u7fd2\u5185\u5bb9\uff1a<\/strong>\u30ed\u30fc\u30c9\u30de\u30c3\u30d7\uff0f\u57fa\u790e\u77e5\u8b58\uff0f\u74b0\u5883\u69cb\u7bc9\uff0fHTML\uff0fCSS\uff0fJavaScript <strong><span class=\"swl-marker mark_yellow\">\u2192 TypeScript\u3084\u578b\u5b89\u5168\u306aAPI\u8a2d\u8a08\u3092\u5b66\u3076\u305f\u3081\u306e\u57fa\u790e\u3092\u3001\u5b9f\u8df5\u7684\u306a\u30ab\u30ea\u30ad\u30e5\u30e9\u30e0\u3067\u78ba\u5b9f\u306b\u7fd2\u5f97\u3067\u304d\u307e\u3059<\/span><\/strong><\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">\u526f\u696d\u30fb\u30d5\u30ea\u30fc\u30e9\u30f3\u30b9\u304c\u4e3b\u6d41\u306b\u306a\u3063\u3066\u3044\u308b\u4eca\u3053\u305d\u3001\u81ea\u3089\u306e\u30b9\u30ad\u30eb\u3067\u7a3c\u3052\u308b\u4eba\u6750\u3092\u76ee\u6307\u3057\u3066\u307f\u307e\u305b\u3093\u304b\uff1f\u672a\u7d4c\u9a13\u3067\u3082\u5fc3\u914d\u3059\u308b\u3053\u3068\u306f\u3042\u308a\u307e\u305b\u3093\u3002\u521d\u7d1a\u30b3\u30fc\u30b9\u3092\u53d7\u8b1b\u3055\u308c\u308b\u65b9\u306e\u5927\u591a\u6570\u306f\u30d7\u30ed\u30b0\u30e9\u30df\u30f3\u30b0\u672a\u7d4c\u9a13\u3067\u3059\u3002\u307e\u305a\u306f\u7121\u6599\u30ab\u30a6\u30f3\u30bb\u30ea\u30f3\u30b0\u3067\u3001\u60a9\u307f\u3084\u4e0d\u5b89\u3092\u304a\u805e\u304b\u305b\u304f\u3060\u3055\u3044\uff01<\/p>\n\n\n\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link has-white-color has-text-color has-background has-link-color wp-element-button\" href=\"https:\/\/withcode.tech\/reservation\/\" style=\"background-color:#ffbf00\"><strong>\u516c\u5f0f\u30b5\u30a4\u30c8\u304b\u3089\u7121\u6599\u30ab\u30a6\u30f3\u30bb\u30ea\u30f3\u30b0\u306b\u7533\u3057\u8fbc\u3080 \u2192<\/strong><\/a><\/div>\n<\/div>\n\n","protected":false},"excerpt":{"rendered":"<p>TypeScript\u3067\u578b\u5b89\u5168\u306aAPI\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u3092\u8a2d\u8a08\u3059\u308b\u5b9f\u8df5\u624b\u6cd5\u3092\u89e3\u8aac\u3002Zod\u30b9\u30ad\u30fc\u30de\u306e\u5408\u6210\u30fb\u4e00\u5143\u7ba1\u7406\u3067\u30b9\u30ad\u30fc\u30de\u91cd\u8907\u309280%\u524a\u6e1b\u3001MSW+faker.js\u306b\u3088\u308b\u30e2\u30c3\u30afAPI\u69cb\u7bc9\u3001\u6761\u4ef6\u4ed8\u304d\u578b\u30fbTemplate Literal Types\u306e\u9ad8\u5ea6\u30c6\u30af\u30cb\u30c3\u30af\u3001\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316\u307e\u3067\u7db2\u7f85\u3057\u307e\u3059\u3002<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"swell_btn_cv_data":"","footnotes":"","vk-ltc-link":"","vk-ltc-target":"0"},"categories":[34,359],"tags":[],"class_list":["post-13066","post","type-post","status-publish","format-standard","hentry","category-programming","category-javascript-programming"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/withcode.tech\/media\/wp-json\/wp\/v2\/posts\/13066","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/withcode.tech\/media\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/withcode.tech\/media\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/withcode.tech\/media\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/withcode.tech\/media\/wp-json\/wp\/v2\/comments?post=13066"}],"version-history":[{"count":3,"href":"https:\/\/withcode.tech\/media\/wp-json\/wp\/v2\/posts\/13066\/revisions"}],"predecessor-version":[{"id":13987,"href":"https:\/\/withcode.tech\/media\/wp-json\/wp\/v2\/posts\/13066\/revisions\/13987"}],"wp:attachment":[{"href":"https:\/\/withcode.tech\/media\/wp-json\/wp\/v2\/media?parent=13066"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/withcode.tech\/media\/wp-json\/wp\/v2\/categories?post=13066"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/withcode.tech\/media\/wp-json\/wp\/v2\/tags?post=13066"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}