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