[{"data":1,"prerenderedAt":305},["ShallowReactive",2],{"article-typescript-union-status":3,"related-articles":205},{"id":4,"title":5,"body":6,"categoryLabel":191,"categorySlug":192,"description":193,"extension":194,"featured":195,"meta":196,"navigation":72,"path":197,"publishedAt":198,"readingMinutes":101,"seo":199,"stem":200,"tags":201,"__hash__":204},"blog\u002Fblog\u002Ftypescript-union-status.md","用联合类型描述请求状态",{"type":7,"value":8,"toc":187},"minimark",[9,18,22,174,177,180,183],[10,11,12,13,17],"p",{},"如果用任意字符串表示状态，",[14,15,16],"code",{},"'sucess'"," 这样的拼写错误可能一直留到运行时。联合类型可以把可用值限制在一个明确集合中。",[19,20,21],"h2",{"id":21},"声明有限的状态值",[23,24,29],"pre",{"className":25,"code":26,"language":27,"meta":28,"style":28},"language-ts shiki shiki-themes github-light-high-contrast","type RequestState = 'idle' | 'loading' | 'success' | 'error'\n\nfunction getMessage(state: RequestState) {\n  if (state === 'loading') return '正在加载'\n  if (state === 'error') return '加载失败'\n  if (state === 'success') return '加载完成'\n  return '等待开始'\n}\n","ts","",[14,30,31,67,74,99,122,141,159,168],{"__ignoreMap":28},[32,33,36,40,44,47,51,54,57,59,62,64],"span",{"class":34,"line":35},"line",1,[32,37,39],{"class":38},"sTJeM","type",[32,41,43],{"class":42},"sV4o_"," RequestState",[32,45,46],{"class":38}," =",[32,48,50],{"class":49},"sYEJz"," 'idle'",[32,52,53],{"class":38}," |",[32,55,56],{"class":49}," 'loading'",[32,58,53],{"class":38},[32,60,61],{"class":49}," 'success'",[32,63,53],{"class":38},[32,65,66],{"class":49}," 'error'\n",[32,68,70],{"class":34,"line":69},2,[32,71,73],{"emptyLinePlaceholder":72},true,"\n",[32,75,77,80,84,88,91,94,96],{"class":34,"line":76},3,[32,78,79],{"class":38},"function",[32,81,83],{"class":82},"s3dhs"," getMessage",[32,85,87],{"class":86},"sigWx","(",[32,89,90],{"class":42},"state",[32,92,93],{"class":38},":",[32,95,43],{"class":42},[32,97,98],{"class":86},") {\n",[32,100,102,105,108,111,113,116,119],{"class":34,"line":101},4,[32,103,104],{"class":38},"  if",[32,106,107],{"class":86}," (state ",[32,109,110],{"class":38},"===",[32,112,56],{"class":49},[32,114,115],{"class":86},") ",[32,117,118],{"class":38},"return",[32,120,121],{"class":49}," '正在加载'\n",[32,123,125,127,129,131,134,136,138],{"class":34,"line":124},5,[32,126,104],{"class":38},[32,128,107],{"class":86},[32,130,110],{"class":38},[32,132,133],{"class":49}," 'error'",[32,135,115],{"class":86},[32,137,118],{"class":38},[32,139,140],{"class":49}," '加载失败'\n",[32,142,144,146,148,150,152,154,156],{"class":34,"line":143},6,[32,145,104],{"class":38},[32,147,107],{"class":86},[32,149,110],{"class":38},[32,151,61],{"class":49},[32,153,115],{"class":86},[32,155,118],{"class":38},[32,157,158],{"class":49}," '加载完成'\n",[32,160,162,165],{"class":34,"line":161},7,[32,163,164],{"class":38},"  return",[32,166,167],{"class":49}," '等待开始'\n",[32,169,171],{"class":34,"line":170},8,[32,172,173],{"class":86},"}\n",[10,175,176],{},"编辑器会提示允许的值。判断完所有状态后，类型检查也能帮助发现遗漏分支。",[19,178,179],{"id":179},"需要携带数据时",[10,181,182],{},"可以把状态和对应数据组合成判别联合类型，例如成功状态带数据、错误状态带错误信息。这样每种状态能携带恰当的信息，而不是把所有字段都写成可选。",[184,185,186],"style",{},"html pre.shiki code .sTJeM, html code.shiki .sTJeM{--shiki-default:#A0111F}html pre.shiki code .sV4o_, html code.shiki .sV4o_{--shiki-default:#702C00}html pre.shiki code .sYEJz, html code.shiki .sYEJz{--shiki-default:#032563}html pre.shiki code .s3dhs, html code.shiki .s3dhs{--shiki-default:#622CBC}html pre.shiki code .sigWx, html code.shiki .sigWx{--shiki-default:#0E1116}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":28,"searchDepth":69,"depth":69,"links":188},[189,190],{"id":21,"depth":69,"text":21},{"id":179,"depth":69,"text":179},"TypeScript","typescript","把加载、成功和失败等状态限制在明确的集合里，减少拼写错误和遗漏分支。","md",false,{},"\u002Fblog\u002Ftypescript-union-status","2026-08-04",{"title":5,"description":193},"blog\u002Ftypescript-union-status",[191,202,203],"联合类型","学习笔记","ezNGJVnZTsXQR2DDmDCe91oBfMZkUQxVsllIAkghu8M",[206,218,227,237,245,253,261,269,277,285,293,295],{"id":207,"path":208,"title":209,"description":210,"categorySlug":211,"categoryLabel":212,"publishedAt":213,"readingMinutes":101,"featured":72,"tags":214},"blog\u002Fblog\u002Fcss-grid-cards.md","\u002Fblog\u002Fcss-grid-cards","用 CSS Grid 排列自适应卡片","使用 repeat、minmax 和 auto-fit，让卡片列数随容器宽度自然变化。","styles","页面与样式","2026-09-10",[215,216,217],"CSS","Grid","页面布局",{"id":219,"path":220,"title":221,"description":222,"categorySlug":211,"categoryLabel":212,"publishedAt":223,"readingMinutes":124,"featured":195,"tags":224},"blog\u002Fblog\u002Fmobile-layout-checklist.md","\u002Fblog\u002Fmobile-layout-checklist","做移动端页面适配时的检查笔记","从内容宽度、触控区域、文字换行和键盘焦点几个方面检查窄屏体验。","2026-08-20",[215,225,226],"响应式","可访问性",{"id":228,"path":229,"title":230,"description":231,"categorySlug":232,"categoryLabel":233,"publishedAt":234,"readingMinutes":143,"featured":72,"tags":235},"blog\u002Fblog\u002Freact-controlled-form.md","\u002Fblog\u002Freact-controlled-form","React 受控表单：从一个输入框开始","用一个简单的姓名输入框，理解 value、onChange 与 React state 之间的关系。","react","React","2026-09-18",[233,236,203],"表单",{"id":238,"path":239,"title":240,"description":241,"categorySlug":232,"categoryLabel":233,"publishedAt":242,"readingMinutes":101,"featured":195,"tags":243},"blog\u002Fblog\u002Freact-list-keys.md","\u002Fblog\u002Freact-list-keys","React 列表渲染里的 key 有什么用","通过稳定的 key 帮助 React 识别列表项目，避免用数组下标造成的状态错位。","2026-08-12",[233,244,203],"列表",{"id":246,"path":247,"title":248,"description":249,"categorySlug":232,"categoryLabel":233,"publishedAt":250,"readingMinutes":124,"featured":195,"tags":251},"blog\u002Fblog\u002Freact-props-and-state.md","\u002Fblog\u002Freact-props-and-state","React 组件里的 Props 和 State","用一个简单的信息卡片区分外部传入的数据和组件内部会变化的数据。","2026-08-28",[233,252,203],"组件",{"id":254,"path":255,"title":256,"description":257,"categorySlug":232,"categoryLabel":233,"publishedAt":258,"readingMinutes":124,"featured":195,"tags":259},"blog\u002Fblog\u002Freact-small-components.md","\u002Fblog\u002Freact-small-components","把重复的界面拆成小组件","从重复的按钮和卡片开始，认识组件拆分的几个简单信号。","2026-07-16",[233,252,260],"练习记录",{"id":262,"path":263,"title":264,"description":265,"categorySlug":232,"categoryLabel":233,"publishedAt":266,"readingMinutes":143,"featured":195,"tags":267},"blog\u002Fblog\u002Freact-use-effect-notes.md","\u002Fblog\u002Freact-use-effect-notes","React useEffect 的依赖和清理","整理 useEffect 何时运行，以及订阅和定时器为什么需要清理。","2026-06-30",[233,268,203],"useEffect",{"id":270,"path":271,"title":272,"description":273,"categorySlug":232,"categoryLabel":233,"publishedAt":274,"readingMinutes":101,"featured":195,"tags":275},"blog\u002Fblog\u002Freact-use-state-practice.md","\u002Fblog\u002Freact-use-state-practice","用 useState 管理一个简单的计数交互","从加一和重置两个操作，熟悉 React state 的声明与更新方式。","2026-07-30",[233,276,260],"useState",{"id":278,"path":279,"title":280,"description":281,"categorySlug":192,"categoryLabel":191,"publishedAt":282,"readingMinutes":124,"featured":72,"tags":283},"blog\u002Fblog\u002Ftypescript-component-props.md","\u002Fblog\u002Ftypescript-component-props","给 React 组件的 Props 添加 TypeScript 类型","用必填属性和可选属性描述组件接口，让使用方式在编辑器里更清晰。","2026-09-02",[191,233,284],"类型",{"id":286,"path":287,"title":288,"description":289,"categorySlug":192,"categoryLabel":191,"publishedAt":290,"readingMinutes":124,"featured":72,"tags":291},"blog\u002Fblog\u002Ftypescript-narrowing.md","\u002Fblog\u002Ftypescript-narrowing","TypeScript 类型收窄入门","使用 typeof 和条件判断缩小联合类型的范围，让后续代码更安全。","2026-09-25",[191,292,203],"类型收窄",{"id":4,"path":197,"title":5,"description":193,"categorySlug":192,"categoryLabel":191,"publishedAt":198,"readingMinutes":101,"featured":195,"tags":294},[191,202,203],{"id":296,"path":297,"title":298,"description":299,"categorySlug":300,"categoryLabel":301,"publishedAt":302,"readingMinutes":101,"featured":195,"tags":303},"blog\u002Fblog\u002Fvite-react-project.md","\u002Fblog\u002Fvite-react-project","用 Vite 创建一个 React + TypeScript 小项目","记录从初始化模板到启动开发服务器的基础步骤，以及几个值得先检查的文件。","tools","开发工具","2026-06-12",[304,233,191],"Vite",1792674168712]