[{"data":1,"prerenderedAt":3422},["ShallowReactive",2],{"page-\u002Fvue\u002F17-typescript-with-vue":3},{"id":4,"title":5,"body":6,"description":3415,"extension":3416,"meta":3417,"navigation":90,"path":3418,"seo":3419,"stem":3420,"__hash__":3421},"content\u002Fvue\u002F17-typescript-with-vue.md","Vue 3 Engineering Reference — TypeScript with Vue",{"type":7,"value":8,"toc":3405},"minimark",[9,14,19,507,511,833,837,3401],[10,11,13],"h1",{"id":12},"_17-typescript-with-vue","17 — TypeScript with Vue",[15,16,18],"h2",{"id":17},"typed-props-defineprops-with-generics","Typed Props — defineProps with Generics",[20,21,24],"code-wrapper",{"filename":22,"language":23},"TypedProps.vue","vue",[25,26,30],"pre",{"className":27,"code":28,"language":23,"meta":29,"style":29},"language-vue shiki shiki-themes github-light github-dark","\u003Cscript setup lang=\"ts\">\nimport { type PropType } from 'vue'\n\n\u002F\u002F ── Generic type annotation (preferred for \u003Cscript setup>) ──\n\u002F\u002F Pure TypeScript syntax — no runtime prop validation, but full type safety.\nconst props = defineProps\u003C{\n  userId: number\n  role: 'admin' | 'user' | 'guest'  \u002F\u002F string literal union\n  tags?: string[]                     \u002F\u002F optional array\n  config?: { theme: string; lang: string }  \u002F\u002F optional object\n}>()\n\n\u002F\u002F ── withDefaults: provide defaults for optional props ──\n\u002F\u002F Required because TS interface defaults don't exist at runtime.\n\u002F\u002F The destructured values are typed correctly (role is the union, not string).\nconst { role = 'user', tags = [], config = { theme: 'dark', lang: 'en' } } = withDefaults(props, {\n  role: 'user',\n  tags: () => [],  \u002F\u002F factory for arrays\u002Fobjects (fresh per instance)\n  config: () => ({ theme: 'dark', lang: 'en' }),\n})\n\n\u002F\u002F ── Runtime validation (alternative: for prop validation at runtime) ──\n\u002F\u002F Use when you need runtime checks (not just compile-time TS types).\nconst props2 = defineProps({\n  userId: { type: Number, required: true },\n  role: {\n    type: String as PropType\u003C'admin' | 'user' | 'guest'>,  \u002F\u002F cast for literal unions\n    default: 'user',\n    validator: (v: string) => ['admin', 'user', 'guest'].includes(v),\n  },\n})\n\u003C\u002Fscript>\n","",[31,32,33,63,85,92,99,105,124,137,162,180,213,219,224,230,236,242,295,307,324,345,351,356,362,368,383,395,401,432,442,486,492,497],"code",{"__ignoreMap":29},[34,35,38,42,46,50,53,56,60],"span",{"class":36,"line":37},"line",1,[34,39,41],{"class":40},"ssxIu","\u003C",[34,43,45],{"class":44},"sk71V","script",[34,47,49],{"class":48},"sIsaT"," setup",[34,51,52],{"class":48}," lang",[34,54,55],{"class":40},"=",[34,57,59],{"class":58},"sJ6F3","\"ts\"",[34,61,62],{"class":40},">\n",[34,64,66,70,73,76,79,82],{"class":36,"line":65},2,[34,67,69],{"class":68},"svdQ7","import",[34,71,72],{"class":40}," { ",[34,74,75],{"class":68},"type",[34,77,78],{"class":40}," PropType } ",[34,80,81],{"class":68},"from",[34,83,84],{"class":58}," 'vue'\n",[34,86,88],{"class":36,"line":87},3,[34,89,91],{"emptyLinePlaceholder":90},true,"\n",[34,93,95],{"class":36,"line":94},4,[34,96,98],{"class":97},"sdCPZ","\u002F\u002F ── Generic type annotation (preferred for \u003Cscript setup>) ──\n",[34,100,102],{"class":36,"line":101},5,[34,103,104],{"class":97},"\u002F\u002F Pure TypeScript syntax — no runtime prop validation, but full type safety.\n",[34,106,108,111,115,118,121],{"class":36,"line":107},6,[34,109,110],{"class":68},"const",[34,112,114],{"class":113},"snvgF"," props",[34,116,117],{"class":68}," =",[34,119,120],{"class":48}," defineProps",[34,122,123],{"class":40},"\u003C{\n",[34,125,127,131,134],{"class":36,"line":126},7,[34,128,130],{"class":129},"sCrzJ","  userId",[34,132,133],{"class":68},":",[34,135,136],{"class":113}," number\n",[34,138,140,143,145,148,151,154,156,159],{"class":36,"line":139},8,[34,141,142],{"class":129},"  role",[34,144,133],{"class":68},[34,146,147],{"class":58}," 'admin'",[34,149,150],{"class":68}," |",[34,152,153],{"class":58}," 'user'",[34,155,150],{"class":68},[34,157,158],{"class":58}," 'guest'",[34,160,161],{"class":97},"  \u002F\u002F string literal union\n",[34,163,165,168,171,174,177],{"class":36,"line":164},9,[34,166,167],{"class":129},"  tags",[34,169,170],{"class":68},"?:",[34,172,173],{"class":113}," string",[34,175,176],{"class":40},"[]                     ",[34,178,179],{"class":97},"\u002F\u002F optional array\n",[34,181,183,186,188,190,193,195,197,200,203,205,207,210],{"class":36,"line":182},10,[34,184,185],{"class":129},"  config",[34,187,170],{"class":68},[34,189,72],{"class":40},[34,191,192],{"class":129},"theme",[34,194,133],{"class":68},[34,196,173],{"class":113},[34,198,199],{"class":40},"; ",[34,201,202],{"class":129},"lang",[34,204,133],{"class":68},[34,206,173],{"class":113},[34,208,209],{"class":40}," }  ",[34,211,212],{"class":97},"\u002F\u002F optional object\n",[34,214,216],{"class":36,"line":215},11,[34,217,218],{"class":40},"}>()\n",[34,220,222],{"class":36,"line":221},12,[34,223,91],{"emptyLinePlaceholder":90},[34,225,227],{"class":36,"line":226},13,[34,228,229],{"class":97},"\u002F\u002F ── withDefaults: provide defaults for optional props ──\n",[34,231,233],{"class":36,"line":232},14,[34,234,235],{"class":97},"\u002F\u002F Required because TS interface defaults don't exist at runtime.\n",[34,237,239],{"class":36,"line":238},15,[34,240,241],{"class":97},"\u002F\u002F The destructured values are typed correctly (role is the union, not string).\n",[34,243,245,247,249,252,254,256,259,262,264,267,270,272,275,278,281,284,287,289,292],{"class":36,"line":244},16,[34,246,110],{"class":68},[34,248,72],{"class":40},[34,250,251],{"class":113},"role",[34,253,117],{"class":68},[34,255,153],{"class":58},[34,257,258],{"class":40},", ",[34,260,261],{"class":113},"tags",[34,263,117],{"class":68},[34,265,266],{"class":40}," [], ",[34,268,269],{"class":113},"config",[34,271,117],{"class":68},[34,273,274],{"class":40}," { theme: ",[34,276,277],{"class":58},"'dark'",[34,279,280],{"class":40},", lang: ",[34,282,283],{"class":58},"'en'",[34,285,286],{"class":40}," } } ",[34,288,55],{"class":68},[34,290,291],{"class":48}," withDefaults",[34,293,294],{"class":40},"(props, {\n",[34,296,298,301,304],{"class":36,"line":297},17,[34,299,300],{"class":40},"  role: ",[34,302,303],{"class":58},"'user'",[34,305,306],{"class":40},",\n",[34,308,310,312,315,318,321],{"class":36,"line":309},18,[34,311,167],{"class":48},[34,313,314],{"class":40},": () ",[34,316,317],{"class":68},"=>",[34,319,320],{"class":40}," [],  ",[34,322,323],{"class":97},"\u002F\u002F factory for arrays\u002Fobjects (fresh per instance)\n",[34,325,327,329,331,333,336,338,340,342],{"class":36,"line":326},19,[34,328,185],{"class":48},[34,330,314],{"class":40},[34,332,317],{"class":68},[34,334,335],{"class":40}," ({ theme: ",[34,337,277],{"class":58},[34,339,280],{"class":40},[34,341,283],{"class":58},[34,343,344],{"class":40}," }),\n",[34,346,348],{"class":36,"line":347},20,[34,349,350],{"class":40},"})\n",[34,352,354],{"class":36,"line":353},21,[34,355,91],{"emptyLinePlaceholder":90},[34,357,359],{"class":36,"line":358},22,[34,360,361],{"class":97},"\u002F\u002F ── Runtime validation (alternative: for prop validation at runtime) ──\n",[34,363,365],{"class":36,"line":364},23,[34,366,367],{"class":97},"\u002F\u002F Use when you need runtime checks (not just compile-time TS types).\n",[34,369,371,373,376,378,380],{"class":36,"line":370},24,[34,372,110],{"class":68},[34,374,375],{"class":113}," props2",[34,377,117],{"class":68},[34,379,120],{"class":48},[34,381,382],{"class":40},"({\n",[34,384,386,389,392],{"class":36,"line":385},25,[34,387,388],{"class":40},"  userId: { type: Number, required: ",[34,390,391],{"class":113},"true",[34,393,394],{"class":40}," },\n",[34,396,398],{"class":36,"line":397},26,[34,399,400],{"class":40},"  role: {\n",[34,402,404,407,410,413,415,418,420,422,424,426,429],{"class":36,"line":403},27,[34,405,406],{"class":40},"    type: String ",[34,408,409],{"class":68},"as",[34,411,412],{"class":48}," PropType",[34,414,41],{"class":40},[34,416,417],{"class":58},"'admin'",[34,419,150],{"class":68},[34,421,153],{"class":58},[34,423,150],{"class":68},[34,425,158],{"class":58},[34,427,428],{"class":40},">,  ",[34,430,431],{"class":97},"\u002F\u002F cast for literal unions\n",[34,433,435,438,440],{"class":36,"line":434},28,[34,436,437],{"class":40},"    default: ",[34,439,303],{"class":58},[34,441,306],{"class":40},[34,443,445,448,451,454,456,458,461,463,466,468,470,472,474,477,480,483],{"class":36,"line":444},29,[34,446,447],{"class":48},"    validator",[34,449,450],{"class":40},": (",[34,452,453],{"class":129},"v",[34,455,133],{"class":68},[34,457,173],{"class":113},[34,459,460],{"class":40},") ",[34,462,317],{"class":68},[34,464,465],{"class":40}," [",[34,467,417],{"class":58},[34,469,258],{"class":40},[34,471,303],{"class":58},[34,473,258],{"class":40},[34,475,476],{"class":58},"'guest'",[34,478,479],{"class":40},"].",[34,481,482],{"class":48},"includes",[34,484,485],{"class":40},"(v),\n",[34,487,489],{"class":36,"line":488},30,[34,490,491],{"class":40},"  },\n",[34,493,495],{"class":36,"line":494},31,[34,496,350],{"class":40},[34,498,500,503,505],{"class":36,"line":499},32,[34,501,502],{"class":40},"\u003C\u002F",[34,504,45],{"class":44},[34,506,62],{"class":40},[15,508,510],{"id":509},"typed-emits-defineemits-with-function-signatures","Typed Emits — defineEmits with Function Signatures",[20,512,514],{"filename":513,"language":23},"TypedEmits.vue",[25,515,517],{"className":27,"code":516,"language":23,"meta":29,"style":29},"\u003Cscript setup lang=\"ts\">\n\u002F\u002F ── Call signature style (preferred — full type safety on payloads) ──\nconst emit = defineEmits\u003C{\n  \u002F\u002F Each property is a function signature: (event name, payload) => void\n  (e: 'update', userId: number, changes: Partial\u003CUser>): void\n  (e: 'delete', payload: { id: number; soft: boolean }): void\n  (e: 'cancel'): void  \u002F\u002F no payload\n}>()\n\n\u002F\u002F ── Alternative: object syntax (3.3+) ──\nconst emit2 = defineEmits\u003C{\n  update: [userId: number, changes: Partial\u003CUser>]\n  delete: [payload: { id: number; soft: boolean }]\n  cancel: []\n}>()\n\n\u002F\u002F ── Usage: type-checked at call site ──\nfunction handleSave() {\n  emit('update', props.userId, { name: newName.value })  \u002F\u002F ✅ typed\n  \u002F\u002F emit('update', '42', {})  \u002F\u002F ❌ TS error: '42' is string, not number\n}\n\u003C\u002Fscript>\n",[31,518,519,535,540,554,559,605,649,671,675,679,684,697,730,761,771,775,779,784,795,812,820,825],{"__ignoreMap":29},[34,520,521,523,525,527,529,531,533],{"class":36,"line":37},[34,522,41],{"class":40},[34,524,45],{"class":44},[34,526,49],{"class":48},[34,528,52],{"class":48},[34,530,55],{"class":40},[34,532,59],{"class":58},[34,534,62],{"class":40},[34,536,537],{"class":36,"line":65},[34,538,539],{"class":97},"\u002F\u002F ── Call signature style (preferred — full type safety on payloads) ──\n",[34,541,542,544,547,549,552],{"class":36,"line":87},[34,543,110],{"class":68},[34,545,546],{"class":113}," emit",[34,548,117],{"class":68},[34,550,551],{"class":48}," defineEmits",[34,553,123],{"class":40},[34,555,556],{"class":36,"line":94},[34,557,558],{"class":97},"  \u002F\u002F Each property is a function signature: (event name, payload) => void\n",[34,560,561,564,567,569,572,574,577,579,582,584,587,589,592,594,597,600,602],{"class":36,"line":101},[34,562,563],{"class":40},"  (",[34,565,566],{"class":129},"e",[34,568,133],{"class":68},[34,570,571],{"class":58}," 'update'",[34,573,258],{"class":40},[34,575,576],{"class":129},"userId",[34,578,133],{"class":68},[34,580,581],{"class":113}," number",[34,583,258],{"class":40},[34,585,586],{"class":129},"changes",[34,588,133],{"class":68},[34,590,591],{"class":48}," Partial",[34,593,41],{"class":40},[34,595,596],{"class":48},"User",[34,598,599],{"class":40},">)",[34,601,133],{"class":68},[34,603,604],{"class":113}," void\n",[34,606,607,609,611,613,616,618,621,623,625,628,630,632,634,637,639,642,645,647],{"class":36,"line":107},[34,608,563],{"class":40},[34,610,566],{"class":129},[34,612,133],{"class":68},[34,614,615],{"class":58}," 'delete'",[34,617,258],{"class":40},[34,619,620],{"class":129},"payload",[34,622,133],{"class":68},[34,624,72],{"class":40},[34,626,627],{"class":129},"id",[34,629,133],{"class":68},[34,631,581],{"class":113},[34,633,199],{"class":40},[34,635,636],{"class":129},"soft",[34,638,133],{"class":68},[34,640,641],{"class":113}," boolean",[34,643,644],{"class":40}," })",[34,646,133],{"class":68},[34,648,604],{"class":113},[34,650,651,653,655,657,660,663,665,668],{"class":36,"line":126},[34,652,563],{"class":40},[34,654,566],{"class":129},[34,656,133],{"class":68},[34,658,659],{"class":58}," 'cancel'",[34,661,662],{"class":40},")",[34,664,133],{"class":68},[34,666,667],{"class":113}," void",[34,669,670],{"class":97},"  \u002F\u002F no payload\n",[34,672,673],{"class":36,"line":139},[34,674,218],{"class":40},[34,676,677],{"class":36,"line":164},[34,678,91],{"emptyLinePlaceholder":90},[34,680,681],{"class":36,"line":182},[34,682,683],{"class":97},"\u002F\u002F ── Alternative: object syntax (3.3+) ──\n",[34,685,686,688,691,693,695],{"class":36,"line":215},[34,687,110],{"class":68},[34,689,690],{"class":113}," emit2",[34,692,117],{"class":68},[34,694,551],{"class":48},[34,696,123],{"class":40},[34,698,699,702,704,706,708,711,714,716,718,720,723,725,727],{"class":36,"line":221},[34,700,701],{"class":129},"  update",[34,703,133],{"class":68},[34,705,465],{"class":40},[34,707,576],{"class":48},[34,709,710],{"class":40},": ",[34,712,713],{"class":113},"number",[34,715,258],{"class":40},[34,717,586],{"class":48},[34,719,710],{"class":40},[34,721,722],{"class":48},"Partial",[34,724,41],{"class":40},[34,726,596],{"class":48},[34,728,729],{"class":40},">]\n",[34,731,732,735,737,739,741,744,746,748,750,752,754,756,758],{"class":36,"line":226},[34,733,734],{"class":129},"  delete",[34,736,133],{"class":68},[34,738,465],{"class":40},[34,740,620],{"class":48},[34,742,743],{"class":40},": { ",[34,745,627],{"class":129},[34,747,133],{"class":68},[34,749,581],{"class":113},[34,751,199],{"class":40},[34,753,636],{"class":129},[34,755,133],{"class":68},[34,757,641],{"class":113},[34,759,760],{"class":40}," }]\n",[34,762,763,766,768],{"class":36,"line":232},[34,764,765],{"class":129},"  cancel",[34,767,133],{"class":68},[34,769,770],{"class":40}," []\n",[34,772,773],{"class":36,"line":238},[34,774,218],{"class":40},[34,776,777],{"class":36,"line":244},[34,778,91],{"emptyLinePlaceholder":90},[34,780,781],{"class":36,"line":297},[34,782,783],{"class":97},"\u002F\u002F ── Usage: type-checked at call site ──\n",[34,785,786,789,792],{"class":36,"line":309},[34,787,788],{"class":68},"function",[34,790,791],{"class":48}," handleSave",[34,793,794],{"class":40},"() {\n",[34,796,797,800,803,806,809],{"class":36,"line":326},[34,798,799],{"class":48},"  emit",[34,801,802],{"class":40},"(",[34,804,805],{"class":58},"'update'",[34,807,808],{"class":40},", props.userId, { name: newName.value })  ",[34,810,811],{"class":97},"\u002F\u002F ✅ typed\n",[34,813,814,817],{"class":36,"line":347},[34,815,816],{"class":97},"  \u002F\u002F emit('update', '42', {})",[34,818,819],{"class":97},"  \u002F\u002F ❌ TS error: '42' is string, not number\n",[34,821,822],{"class":36,"line":353},[34,823,824],{"class":40},"}\n",[34,826,827,829,831],{"class":36,"line":358},[34,828,502],{"class":40},[34,830,45],{"class":44},[34,832,62],{"class":40},[15,834,836],{"id":835},"typed-component-instance-defineexpose","Typed Component Instance — defineExpose",[20,838,840,1064,1225,1229,1701,1705,2123,2127,2954,2958,3081,3085,3093,3229],{"filename":839,"language":23},"TypedExpose.vue",[25,841,843],{"className":27,"code":842,"language":23,"meta":29,"style":29},"\u003Cscript setup lang=\"ts\">\nimport { ref } from 'vue'\n\n\u002F\u002F ── defineExpose: type the public API of the component ──\n\u002F\u002F Parent using template ref gets typed access to exposed methods.\nconst count = ref(0)\nconst internalState = ref('private')  \u002F\u002F NOT exposed — parent can't access\n\nfunction increment() { count.value++ }\nfunction reset() { count.value = 0 }\nfunction getValue() { return count.value }  \u002F\u002F return value, not ref\n\n\u002F\u002F ── Expose with return type inference ──\ndefineExpose({\n  increment,  \u002F\u002F () => void\n  reset,      \u002F\u002F () => void\n  getValue,   \u002F\u002F () => number\n  count,      \u002F\u002F Ref\u003Cnumber> — exposed as a ref, parent accesses via .value\n})\n\n\u002F\u002F ── Parent usage (typed ref): ──────────────────────────\n\u002F\u002F const childRef = ref\u003C{ increment: () => void; reset: () => void; getValue: () => number; count: Ref\u003Cnumber> } | null>(null)\n\u002F\u002F childRef.value?.increment()  \u002F\u002F ✅ typed\n\u002F\u002F childRef.value?.count.value  \u002F\u002F ✅ typed\n",[31,844,845,861,872,876,881,886,906,928,932,948,965,984,988,993,1000,1008,1015,1023,1031,1035,1039,1044,1049,1057],{"__ignoreMap":29},[34,846,847,849,851,853,855,857,859],{"class":36,"line":37},[34,848,41],{"class":40},[34,850,45],{"class":44},[34,852,49],{"class":48},[34,854,52],{"class":48},[34,856,55],{"class":40},[34,858,59],{"class":58},[34,860,62],{"class":40},[34,862,863,865,868,870],{"class":36,"line":65},[34,864,69],{"class":68},[34,866,867],{"class":40}," { ref } ",[34,869,81],{"class":68},[34,871,84],{"class":58},[34,873,874],{"class":36,"line":87},[34,875,91],{"emptyLinePlaceholder":90},[34,877,878],{"class":36,"line":94},[34,879,880],{"class":97},"\u002F\u002F ── defineExpose: type the public API of the component ──\n",[34,882,883],{"class":36,"line":101},[34,884,885],{"class":97},"\u002F\u002F Parent using template ref gets typed access to exposed methods.\n",[34,887,888,890,893,895,898,900,903],{"class":36,"line":107},[34,889,110],{"class":68},[34,891,892],{"class":113}," count",[34,894,117],{"class":68},[34,896,897],{"class":48}," ref",[34,899,802],{"class":40},[34,901,902],{"class":113},"0",[34,904,905],{"class":40},")\n",[34,907,908,910,913,915,917,919,922,925],{"class":36,"line":126},[34,909,110],{"class":68},[34,911,912],{"class":113}," internalState",[34,914,117],{"class":68},[34,916,897],{"class":48},[34,918,802],{"class":40},[34,920,921],{"class":58},"'private'",[34,923,924],{"class":40},")  ",[34,926,927],{"class":97},"\u002F\u002F NOT exposed — parent can't access\n",[34,929,930],{"class":36,"line":139},[34,931,91],{"emptyLinePlaceholder":90},[34,933,934,936,939,942,945],{"class":36,"line":164},[34,935,788],{"class":68},[34,937,938],{"class":48}," increment",[34,940,941],{"class":40},"() { count.value",[34,943,944],{"class":68},"++",[34,946,947],{"class":40}," }\n",[34,949,950,952,955,958,960,963],{"class":36,"line":182},[34,951,788],{"class":68},[34,953,954],{"class":48}," reset",[34,956,957],{"class":40},"() { count.value ",[34,959,55],{"class":68},[34,961,962],{"class":113}," 0",[34,964,947],{"class":40},[34,966,967,969,972,975,978,981],{"class":36,"line":215},[34,968,788],{"class":68},[34,970,971],{"class":48}," getValue",[34,973,974],{"class":40},"() { ",[34,976,977],{"class":68},"return",[34,979,980],{"class":40}," count.value }  ",[34,982,983],{"class":97},"\u002F\u002F return value, not ref\n",[34,985,986],{"class":36,"line":221},[34,987,91],{"emptyLinePlaceholder":90},[34,989,990],{"class":36,"line":226},[34,991,992],{"class":97},"\u002F\u002F ── Expose with return type inference ──\n",[34,994,995,998],{"class":36,"line":232},[34,996,997],{"class":48},"defineExpose",[34,999,382],{"class":40},[34,1001,1002,1005],{"class":36,"line":238},[34,1003,1004],{"class":40},"  increment,  ",[34,1006,1007],{"class":97},"\u002F\u002F () => void\n",[34,1009,1010,1013],{"class":36,"line":244},[34,1011,1012],{"class":40},"  reset,      ",[34,1014,1007],{"class":97},[34,1016,1017,1020],{"class":36,"line":297},[34,1018,1019],{"class":40},"  getValue,   ",[34,1021,1022],{"class":97},"\u002F\u002F () => number\n",[34,1024,1025,1028],{"class":36,"line":309},[34,1026,1027],{"class":40},"  count,      ",[34,1029,1030],{"class":97},"\u002F\u002F Ref\u003Cnumber> — exposed as a ref, parent accesses via .value\n",[34,1032,1033],{"class":36,"line":326},[34,1034,350],{"class":40},[34,1036,1037],{"class":36,"line":347},[34,1038,91],{"emptyLinePlaceholder":90},[34,1040,1041],{"class":36,"line":353},[34,1042,1043],{"class":97},"\u002F\u002F ── Parent usage (typed ref): ──────────────────────────\n",[34,1045,1046],{"class":36,"line":358},[34,1047,1048],{"class":97},"\u002F\u002F const childRef = ref\u003C{ increment: () => void; reset: () => void; getValue: () => number; count: Ref\u003Cnumber> } | null>(null)\n",[34,1050,1051,1054],{"class":36,"line":364},[34,1052,1053],{"class":97},"\u002F\u002F childRef.value?.increment()",[34,1055,1056],{"class":97},"  \u002F\u002F ✅ typed\n",[34,1058,1059,1062],{"class":36,"line":370},[34,1060,1061],{"class":97},"\u002F\u002F childRef.value?.count.value",[34,1063,1056],{"class":97},[20,1065,1068],{"filename":1066,"language":1067},"typed-ref.ts","typescript",[25,1069,1072],{"className":1070,"code":1071,"language":1067,"meta":29,"style":29},"language-typescript shiki shiki-themes github-light github-dark","import { ref, type ComponentPublicInstance } from 'vue'\nimport TypedComponent from '.\u002FTypedComponent.vue'\n\n\u002F\u002F ── Type the template ref using InstanceType ──────────\n\u002F\u002F This gives you intellisense on the component's exposed API.\nconst childRef = ref\u003CComponentPublicInstance\u003Ctypeof TypedComponent> | null>(null)\n\n\u002F\u002F ── Or use the instance type directly: ──\n\u002F\u002F If the component uses defineExpose, the ref type includes exposed members.\n\u002F\u002F If not, you get the default ComponentPublicInstance (with $el, $emit, etc.)\n\n\u002F\u002F ── Generic component typing: ──────────────────────────\nimport type { VNode } from 'vue'\nconst vNode = ref\u003CVNode | null>(null)  \u002F\u002F for render function components\n",[31,1073,1074,1090,1102,1106,1111,1116,1154,1158,1163,1168,1173,1177,1182,1196],{"__ignoreMap":29},[34,1075,1076,1078,1081,1083,1086,1088],{"class":36,"line":37},[34,1077,69],{"class":68},[34,1079,1080],{"class":40}," { ref, ",[34,1082,75],{"class":68},[34,1084,1085],{"class":40}," ComponentPublicInstance } ",[34,1087,81],{"class":68},[34,1089,84],{"class":58},[34,1091,1092,1094,1097,1099],{"class":36,"line":65},[34,1093,69],{"class":68},[34,1095,1096],{"class":40}," TypedComponent ",[34,1098,81],{"class":68},[34,1100,1101],{"class":58}," '.\u002FTypedComponent.vue'\n",[34,1103,1104],{"class":36,"line":87},[34,1105,91],{"emptyLinePlaceholder":90},[34,1107,1108],{"class":36,"line":94},[34,1109,1110],{"class":97},"\u002F\u002F ── Type the template ref using InstanceType ──────────\n",[34,1112,1113],{"class":36,"line":101},[34,1114,1115],{"class":97},"\u002F\u002F This gives you intellisense on the component's exposed API.\n",[34,1117,1118,1120,1123,1125,1127,1129,1132,1134,1137,1140,1143,1146,1149,1152],{"class":36,"line":107},[34,1119,110],{"class":68},[34,1121,1122],{"class":113}," childRef",[34,1124,117],{"class":68},[34,1126,897],{"class":48},[34,1128,41],{"class":40},[34,1130,1131],{"class":48},"ComponentPublicInstance",[34,1133,41],{"class":40},[34,1135,1136],{"class":68},"typeof",[34,1138,1139],{"class":40}," TypedComponent> ",[34,1141,1142],{"class":68},"|",[34,1144,1145],{"class":113}," null",[34,1147,1148],{"class":40},">(",[34,1150,1151],{"class":113},"null",[34,1153,905],{"class":40},[34,1155,1156],{"class":36,"line":126},[34,1157,91],{"emptyLinePlaceholder":90},[34,1159,1160],{"class":36,"line":139},[34,1161,1162],{"class":97},"\u002F\u002F ── Or use the instance type directly: ──\n",[34,1164,1165],{"class":36,"line":164},[34,1166,1167],{"class":97},"\u002F\u002F If the component uses defineExpose, the ref type includes exposed members.\n",[34,1169,1170],{"class":36,"line":182},[34,1171,1172],{"class":97},"\u002F\u002F If not, you get the default ComponentPublicInstance (with $el, $emit, etc.)\n",[34,1174,1175],{"class":36,"line":215},[34,1176,91],{"emptyLinePlaceholder":90},[34,1178,1179],{"class":36,"line":221},[34,1180,1181],{"class":97},"\u002F\u002F ── Generic component typing: ──────────────────────────\n",[34,1183,1184,1186,1189,1192,1194],{"class":36,"line":226},[34,1185,69],{"class":68},[34,1187,1188],{"class":68}," type",[34,1190,1191],{"class":40}," { VNode } ",[34,1193,81],{"class":68},[34,1195,84],{"class":58},[34,1197,1198,1200,1203,1205,1207,1209,1212,1214,1216,1218,1220,1222],{"class":36,"line":232},[34,1199,110],{"class":68},[34,1201,1202],{"class":113}," vNode",[34,1204,117],{"class":68},[34,1206,897],{"class":48},[34,1208,41],{"class":40},[34,1210,1211],{"class":48},"VNode",[34,1213,150],{"class":68},[34,1215,1145],{"class":113},[34,1217,1148],{"class":40},[34,1219,1151],{"class":113},[34,1221,924],{"class":40},[34,1223,1224],{"class":97},"\u002F\u002F for render function components\n",[15,1226,1228],{"id":1227},"typed-provideinject-injectionkey","Typed provide\u002Finject — InjectionKey",[20,1230,1232],{"filename":1231,"language":1067},"typed-provide-inject.ts",[25,1233,1235],{"className":1070,"code":1234,"language":1067,"meta":29,"style":29},"import { provide, inject, type InjectionKey, type Ref } from 'vue'\n\n\u002F\u002F ── InjectionKey: a Symbol that carries the TypeScript type ──\n\u002F\u002F Using Symbol (not string) prevents naming collisions across libraries.\n\u002F\u002F The generic parameter is the type of the provided value.\nexport const USER_CONTEXT: InjectionKey\u003C{\n  user: Ref\u003CUser | null>\n  login: (credentials: Credentials) => Promise\u003Cvoid>\n  logout: () => void\n}> = Symbol('user-context')\n\n\u002F\u002F ── Provider: type-checked ──\nexport function provideUser() {\n  const user = ref\u003CUser | null>(null)\n\n  const context = {\n    user,\n    login: async (credentials: Credentials) => { \u002F* ... *\u002F },\n    logout: () => { user.value = null },\n  }\n\n  provide(USER_CONTEXT, context)  \u002F\u002F ✅ type matches InjectionKey\u003CT>\n  return context\n}\n\n\u002F\u002F ── Consumer: typed, with fallback ──────────────────────\nexport function useUser() {\n  const ctx = inject(USER_CONTEXT)\n  if (!ctx) {\n    throw new Error('useUser() must be called within a component that provides USER_CONTEXT')\n  }\n  return ctx  \u002F\u002F type: { user: Ref\u003CUser | null>; login: ...; logout: () => void }\n}\n\n\u002F\u002F ── inject with default (if not provided): ──\nconst ctx = inject(USER_CONTEXT, {\n  user: ref(null),\n  login: async () => {},\n  logout: () => {},\n})\n\u002F\u002F ctx is non-null (default used if no provider) — no guard needed.\n",[31,1236,1237,1258,1262,1267,1272,1277,1295,1315,1347,1361,1378,1382,1387,1399,1425,1429,1441,1446,1475,1493,1498,1502,1518,1526,1530,1534,1539,1550,1568,1581,1599,1603,1613,1618,1623,1629,1647,1663,1679,1690,1695],{"__ignoreMap":29},[34,1238,1239,1241,1244,1246,1249,1251,1254,1256],{"class":36,"line":37},[34,1240,69],{"class":68},[34,1242,1243],{"class":40}," { provide, inject, ",[34,1245,75],{"class":68},[34,1247,1248],{"class":40}," InjectionKey, ",[34,1250,75],{"class":68},[34,1252,1253],{"class":40}," Ref } ",[34,1255,81],{"class":68},[34,1257,84],{"class":58},[34,1259,1260],{"class":36,"line":65},[34,1261,91],{"emptyLinePlaceholder":90},[34,1263,1264],{"class":36,"line":87},[34,1265,1266],{"class":97},"\u002F\u002F ── InjectionKey: a Symbol that carries the TypeScript type ──\n",[34,1268,1269],{"class":36,"line":94},[34,1270,1271],{"class":97},"\u002F\u002F Using Symbol (not string) prevents naming collisions across libraries.\n",[34,1273,1274],{"class":36,"line":101},[34,1275,1276],{"class":97},"\u002F\u002F The generic parameter is the type of the provided value.\n",[34,1278,1279,1282,1285,1288,1290,1293],{"class":36,"line":107},[34,1280,1281],{"class":68},"export",[34,1283,1284],{"class":68}," const",[34,1286,1287],{"class":113}," USER_CONTEXT",[34,1289,133],{"class":68},[34,1291,1292],{"class":48}," InjectionKey",[34,1294,123],{"class":40},[34,1296,1297,1300,1302,1305,1307,1309,1311,1313],{"class":36,"line":126},[34,1298,1299],{"class":129},"  user",[34,1301,133],{"class":68},[34,1303,1304],{"class":48}," Ref",[34,1306,41],{"class":40},[34,1308,596],{"class":48},[34,1310,150],{"class":68},[34,1312,1145],{"class":113},[34,1314,62],{"class":40},[34,1316,1317,1320,1322,1325,1328,1330,1333,1335,1337,1340,1342,1345],{"class":36,"line":139},[34,1318,1319],{"class":48},"  login",[34,1321,133],{"class":68},[34,1323,1324],{"class":40}," (",[34,1326,1327],{"class":129},"credentials",[34,1329,133],{"class":68},[34,1331,1332],{"class":48}," Credentials",[34,1334,460],{"class":40},[34,1336,317],{"class":68},[34,1338,1339],{"class":48}," Promise",[34,1341,41],{"class":40},[34,1343,1344],{"class":113},"void",[34,1346,62],{"class":40},[34,1348,1349,1352,1354,1357,1359],{"class":36,"line":164},[34,1350,1351],{"class":48},"  logout",[34,1353,133],{"class":68},[34,1355,1356],{"class":40}," () ",[34,1358,317],{"class":68},[34,1360,604],{"class":113},[34,1362,1363,1366,1368,1371,1373,1376],{"class":36,"line":182},[34,1364,1365],{"class":40},"}> ",[34,1367,55],{"class":68},[34,1369,1370],{"class":48}," Symbol",[34,1372,802],{"class":40},[34,1374,1375],{"class":58},"'user-context'",[34,1377,905],{"class":40},[34,1379,1380],{"class":36,"line":215},[34,1381,91],{"emptyLinePlaceholder":90},[34,1383,1384],{"class":36,"line":221},[34,1385,1386],{"class":97},"\u002F\u002F ── Provider: type-checked ──\n",[34,1388,1389,1391,1394,1397],{"class":36,"line":226},[34,1390,1281],{"class":68},[34,1392,1393],{"class":68}," function",[34,1395,1396],{"class":48}," provideUser",[34,1398,794],{"class":40},[34,1400,1401,1404,1407,1409,1411,1413,1415,1417,1419,1421,1423],{"class":36,"line":232},[34,1402,1403],{"class":68},"  const",[34,1405,1406],{"class":113}," user",[34,1408,117],{"class":68},[34,1410,897],{"class":48},[34,1412,41],{"class":40},[34,1414,596],{"class":48},[34,1416,150],{"class":68},[34,1418,1145],{"class":113},[34,1420,1148],{"class":40},[34,1422,1151],{"class":113},[34,1424,905],{"class":40},[34,1426,1427],{"class":36,"line":238},[34,1428,91],{"emptyLinePlaceholder":90},[34,1430,1431,1433,1436,1438],{"class":36,"line":244},[34,1432,1403],{"class":68},[34,1434,1435],{"class":113}," context",[34,1437,117],{"class":68},[34,1439,1440],{"class":40}," {\n",[34,1442,1443],{"class":36,"line":297},[34,1444,1445],{"class":40},"    user,\n",[34,1447,1448,1451,1453,1456,1458,1460,1462,1464,1466,1468,1470,1473],{"class":36,"line":309},[34,1449,1450],{"class":48},"    login",[34,1452,710],{"class":40},[34,1454,1455],{"class":68},"async",[34,1457,1324],{"class":40},[34,1459,1327],{"class":129},[34,1461,133],{"class":68},[34,1463,1332],{"class":48},[34,1465,460],{"class":40},[34,1467,317],{"class":68},[34,1469,72],{"class":40},[34,1471,1472],{"class":97},"\u002F* ... *\u002F",[34,1474,394],{"class":40},[34,1476,1477,1480,1482,1484,1487,1489,1491],{"class":36,"line":326},[34,1478,1479],{"class":48},"    logout",[34,1481,314],{"class":40},[34,1483,317],{"class":68},[34,1485,1486],{"class":40}," { user.value ",[34,1488,55],{"class":68},[34,1490,1145],{"class":113},[34,1492,394],{"class":40},[34,1494,1495],{"class":36,"line":347},[34,1496,1497],{"class":40},"  }\n",[34,1499,1500],{"class":36,"line":353},[34,1501,91],{"emptyLinePlaceholder":90},[34,1503,1504,1507,1509,1512,1515],{"class":36,"line":358},[34,1505,1506],{"class":48},"  provide",[34,1508,802],{"class":40},[34,1510,1511],{"class":113},"USER_CONTEXT",[34,1513,1514],{"class":40},", context)  ",[34,1516,1517],{"class":97},"\u002F\u002F ✅ type matches InjectionKey\u003CT>\n",[34,1519,1520,1523],{"class":36,"line":364},[34,1521,1522],{"class":68},"  return",[34,1524,1525],{"class":40}," context\n",[34,1527,1528],{"class":36,"line":370},[34,1529,824],{"class":40},[34,1531,1532],{"class":36,"line":385},[34,1533,91],{"emptyLinePlaceholder":90},[34,1535,1536],{"class":36,"line":397},[34,1537,1538],{"class":97},"\u002F\u002F ── Consumer: typed, with fallback ──────────────────────\n",[34,1540,1541,1543,1545,1548],{"class":36,"line":403},[34,1542,1281],{"class":68},[34,1544,1393],{"class":68},[34,1546,1547],{"class":48}," useUser",[34,1549,794],{"class":40},[34,1551,1552,1554,1557,1559,1562,1564,1566],{"class":36,"line":434},[34,1553,1403],{"class":68},[34,1555,1556],{"class":113}," ctx",[34,1558,117],{"class":68},[34,1560,1561],{"class":48}," inject",[34,1563,802],{"class":40},[34,1565,1511],{"class":113},[34,1567,905],{"class":40},[34,1569,1570,1573,1575,1578],{"class":36,"line":444},[34,1571,1572],{"class":68},"  if",[34,1574,1324],{"class":40},[34,1576,1577],{"class":68},"!",[34,1579,1580],{"class":40},"ctx) {\n",[34,1582,1583,1586,1589,1592,1594,1597],{"class":36,"line":488},[34,1584,1585],{"class":68},"    throw",[34,1587,1588],{"class":68}," new",[34,1590,1591],{"class":48}," Error",[34,1593,802],{"class":40},[34,1595,1596],{"class":58},"'useUser() must be called within a component that provides USER_CONTEXT'",[34,1598,905],{"class":40},[34,1600,1601],{"class":36,"line":494},[34,1602,1497],{"class":40},[34,1604,1605,1607,1610],{"class":36,"line":499},[34,1606,1522],{"class":68},[34,1608,1609],{"class":40}," ctx  ",[34,1611,1612],{"class":97},"\u002F\u002F type: { user: Ref\u003CUser | null>; login: ...; logout: () => void }\n",[34,1614,1616],{"class":36,"line":1615},33,[34,1617,824],{"class":40},[34,1619,1621],{"class":36,"line":1620},34,[34,1622,91],{"emptyLinePlaceholder":90},[34,1624,1626],{"class":36,"line":1625},35,[34,1627,1628],{"class":97},"\u002F\u002F ── inject with default (if not provided): ──\n",[34,1630,1632,1634,1636,1638,1640,1642,1644],{"class":36,"line":1631},36,[34,1633,110],{"class":68},[34,1635,1556],{"class":113},[34,1637,117],{"class":68},[34,1639,1561],{"class":48},[34,1641,802],{"class":40},[34,1643,1511],{"class":113},[34,1645,1646],{"class":40},", {\n",[34,1648,1650,1653,1656,1658,1660],{"class":36,"line":1649},37,[34,1651,1652],{"class":40},"  user: ",[34,1654,1655],{"class":48},"ref",[34,1657,802],{"class":40},[34,1659,1151],{"class":113},[34,1661,1662],{"class":40},"),\n",[34,1664,1666,1668,1670,1672,1674,1676],{"class":36,"line":1665},38,[34,1667,1319],{"class":48},[34,1669,710],{"class":40},[34,1671,1455],{"class":68},[34,1673,1356],{"class":40},[34,1675,317],{"class":68},[34,1677,1678],{"class":40}," {},\n",[34,1680,1682,1684,1686,1688],{"class":36,"line":1681},39,[34,1683,1351],{"class":48},[34,1685,314],{"class":40},[34,1687,317],{"class":68},[34,1689,1678],{"class":40},[34,1691,1693],{"class":36,"line":1692},40,[34,1694,350],{"class":40},[34,1696,1698],{"class":36,"line":1697},41,[34,1699,1700],{"class":97},"\u002F\u002F ctx is non-null (default used if no provider) — no guard needed.\n",[15,1702,1704],{"id":1703},"generic-composable-usefetch-with-type-inference","Generic Composable — useFetch with Type Inference",[20,1706,1708],{"filename":1707,"language":1067},"generic-composable.ts",[25,1709,1711],{"className":1070,"code":1710,"language":1067,"meta":29,"style":29},"import { ref, type Ref, type MaybeRefOrGetter } from 'vue'\n\n\u002F\u002F ── Generic composable: type T is inferred from the fetcher's return type ──\n\u002F\u002F The caller doesn't need to specify T — it's inferred from the generic.\nexport function useFetch\u003CT>(\n  url: MaybeRefOrGetter\u003Cstring>,\n  options?: { transform?: (data: unknown) => T }\n): { data: Ref\u003CT | null>; loading: Ref\u003Cboolean>; error: Ref\u003CError | null> } {\n  const data: Ref\u003CT | null> = ref(null) as Ref\u003CT | null>\n  const loading = ref(true)\n  const error = ref\u003CError | null>(null)\n\n  \u002F\u002F ... fetch logic ...\n\n  return { data, loading, error }\n}\n\n\u002F\u002F ── Usage: T is inferred ─────────────────────────────────\ninterface User { id: number; name: string }\n\n\u002F\u002F T is inferred as User from the transform's return type\nconst { data } = useFetch('\u002Fapi\u002Fuser', {\n  transform: (raw): User => ({ id: raw.id, name: raw.name })\n})\n\u002F\u002F data.value is User | null — fully typed\n\n\u002F\u002F ── Without transform, T defaults to unknown ────────────\nconst { data: rawData } = useFetch('\u002Fapi\u002Funknown')\n\u002F\u002F rawData.value is unknown | null — must narrow before use\n",[31,1712,1713,1733,1737,1742,1747,1764,1782,1815,1873,1919,1936,1961,1965,1970,1974,1981,1985,1989,1994,2021,2025,2030,2052,2074,2078,2083,2087,2092,2118],{"__ignoreMap":29},[34,1714,1715,1717,1719,1721,1724,1726,1729,1731],{"class":36,"line":37},[34,1716,69],{"class":68},[34,1718,1080],{"class":40},[34,1720,75],{"class":68},[34,1722,1723],{"class":40}," Ref, ",[34,1725,75],{"class":68},[34,1727,1728],{"class":40}," MaybeRefOrGetter } ",[34,1730,81],{"class":68},[34,1732,84],{"class":58},[34,1734,1735],{"class":36,"line":65},[34,1736,91],{"emptyLinePlaceholder":90},[34,1738,1739],{"class":36,"line":87},[34,1740,1741],{"class":97},"\u002F\u002F ── Generic composable: type T is inferred from the fetcher's return type ──\n",[34,1743,1744],{"class":36,"line":94},[34,1745,1746],{"class":97},"\u002F\u002F The caller doesn't need to specify T — it's inferred from the generic.\n",[34,1748,1749,1751,1753,1756,1758,1761],{"class":36,"line":101},[34,1750,1281],{"class":68},[34,1752,1393],{"class":68},[34,1754,1755],{"class":48}," useFetch",[34,1757,41],{"class":40},[34,1759,1760],{"class":48},"T",[34,1762,1763],{"class":40},">(\n",[34,1765,1766,1769,1771,1774,1776,1779],{"class":36,"line":107},[34,1767,1768],{"class":129},"  url",[34,1770,133],{"class":68},[34,1772,1773],{"class":48}," MaybeRefOrGetter",[34,1775,41],{"class":40},[34,1777,1778],{"class":113},"string",[34,1780,1781],{"class":40},">,\n",[34,1783,1784,1787,1789,1791,1794,1796,1798,1801,1803,1806,1808,1810,1813],{"class":36,"line":126},[34,1785,1786],{"class":129},"  options",[34,1788,170],{"class":68},[34,1790,72],{"class":40},[34,1792,1793],{"class":48},"transform",[34,1795,170],{"class":68},[34,1797,1324],{"class":40},[34,1799,1800],{"class":129},"data",[34,1802,133],{"class":68},[34,1804,1805],{"class":113}," unknown",[34,1807,460],{"class":40},[34,1809,317],{"class":68},[34,1811,1812],{"class":48}," T",[34,1814,947],{"class":40},[34,1816,1817,1819,1821,1823,1825,1827,1829,1831,1833,1835,1837,1840,1843,1845,1847,1849,1852,1854,1857,1859,1861,1863,1866,1868,1870],{"class":36,"line":139},[34,1818,662],{"class":40},[34,1820,133],{"class":68},[34,1822,72],{"class":40},[34,1824,1800],{"class":129},[34,1826,133],{"class":68},[34,1828,1304],{"class":48},[34,1830,41],{"class":40},[34,1832,1760],{"class":48},[34,1834,150],{"class":68},[34,1836,1145],{"class":113},[34,1838,1839],{"class":40},">; ",[34,1841,1842],{"class":129},"loading",[34,1844,133],{"class":68},[34,1846,1304],{"class":48},[34,1848,41],{"class":40},[34,1850,1851],{"class":113},"boolean",[34,1853,1839],{"class":40},[34,1855,1856],{"class":129},"error",[34,1858,133],{"class":68},[34,1860,1304],{"class":48},[34,1862,41],{"class":40},[34,1864,1865],{"class":48},"Error",[34,1867,150],{"class":68},[34,1869,1145],{"class":113},[34,1871,1872],{"class":40},"> } {\n",[34,1874,1875,1877,1880,1882,1884,1886,1888,1890,1892,1895,1897,1899,1901,1903,1905,1907,1909,1911,1913,1915,1917],{"class":36,"line":164},[34,1876,1403],{"class":68},[34,1878,1879],{"class":113}," data",[34,1881,133],{"class":68},[34,1883,1304],{"class":48},[34,1885,41],{"class":40},[34,1887,1760],{"class":48},[34,1889,150],{"class":68},[34,1891,1145],{"class":113},[34,1893,1894],{"class":40},"> ",[34,1896,55],{"class":68},[34,1898,897],{"class":48},[34,1900,802],{"class":40},[34,1902,1151],{"class":113},[34,1904,460],{"class":40},[34,1906,409],{"class":68},[34,1908,1304],{"class":48},[34,1910,41],{"class":40},[34,1912,1760],{"class":48},[34,1914,150],{"class":68},[34,1916,1145],{"class":113},[34,1918,62],{"class":40},[34,1920,1921,1923,1926,1928,1930,1932,1934],{"class":36,"line":182},[34,1922,1403],{"class":68},[34,1924,1925],{"class":113}," loading",[34,1927,117],{"class":68},[34,1929,897],{"class":48},[34,1931,802],{"class":40},[34,1933,391],{"class":113},[34,1935,905],{"class":40},[34,1937,1938,1940,1943,1945,1947,1949,1951,1953,1955,1957,1959],{"class":36,"line":215},[34,1939,1403],{"class":68},[34,1941,1942],{"class":113}," error",[34,1944,117],{"class":68},[34,1946,897],{"class":48},[34,1948,41],{"class":40},[34,1950,1865],{"class":48},[34,1952,150],{"class":68},[34,1954,1145],{"class":113},[34,1956,1148],{"class":40},[34,1958,1151],{"class":113},[34,1960,905],{"class":40},[34,1962,1963],{"class":36,"line":221},[34,1964,91],{"emptyLinePlaceholder":90},[34,1966,1967],{"class":36,"line":226},[34,1968,1969],{"class":97},"  \u002F\u002F ... fetch logic ...\n",[34,1971,1972],{"class":36,"line":232},[34,1973,91],{"emptyLinePlaceholder":90},[34,1975,1976,1978],{"class":36,"line":238},[34,1977,1522],{"class":68},[34,1979,1980],{"class":40}," { data, loading, error }\n",[34,1982,1983],{"class":36,"line":244},[34,1984,824],{"class":40},[34,1986,1987],{"class":36,"line":297},[34,1988,91],{"emptyLinePlaceholder":90},[34,1990,1991],{"class":36,"line":309},[34,1992,1993],{"class":97},"\u002F\u002F ── Usage: T is inferred ─────────────────────────────────\n",[34,1995,1996,1999,2002,2004,2006,2008,2010,2012,2015,2017,2019],{"class":36,"line":326},[34,1997,1998],{"class":68},"interface",[34,2000,2001],{"class":48}," User",[34,2003,72],{"class":40},[34,2005,627],{"class":129},[34,2007,133],{"class":68},[34,2009,581],{"class":113},[34,2011,199],{"class":40},[34,2013,2014],{"class":129},"name",[34,2016,133],{"class":68},[34,2018,173],{"class":113},[34,2020,947],{"class":40},[34,2022,2023],{"class":36,"line":347},[34,2024,91],{"emptyLinePlaceholder":90},[34,2026,2027],{"class":36,"line":353},[34,2028,2029],{"class":97},"\u002F\u002F T is inferred as User from the transform's return type\n",[34,2031,2032,2034,2036,2038,2041,2043,2045,2047,2050],{"class":36,"line":358},[34,2033,110],{"class":68},[34,2035,72],{"class":40},[34,2037,1800],{"class":113},[34,2039,2040],{"class":40}," } ",[34,2042,55],{"class":68},[34,2044,1755],{"class":48},[34,2046,802],{"class":40},[34,2048,2049],{"class":58},"'\u002Fapi\u002Fuser'",[34,2051,1646],{"class":40},[34,2053,2054,2057,2059,2062,2064,2066,2068,2071],{"class":36,"line":364},[34,2055,2056],{"class":48},"  transform",[34,2058,450],{"class":40},[34,2060,2061],{"class":129},"raw",[34,2063,662],{"class":40},[34,2065,133],{"class":68},[34,2067,2001],{"class":48},[34,2069,2070],{"class":68}," =>",[34,2072,2073],{"class":40}," ({ id: raw.id, name: raw.name })\n",[34,2075,2076],{"class":36,"line":370},[34,2077,350],{"class":40},[34,2079,2080],{"class":36,"line":385},[34,2081,2082],{"class":97},"\u002F\u002F data.value is User | null — fully typed\n",[34,2084,2085],{"class":36,"line":397},[34,2086,91],{"emptyLinePlaceholder":90},[34,2088,2089],{"class":36,"line":403},[34,2090,2091],{"class":97},"\u002F\u002F ── Without transform, T defaults to unknown ────────────\n",[34,2093,2094,2096,2098,2100,2102,2105,2107,2109,2111,2113,2116],{"class":36,"line":434},[34,2095,110],{"class":68},[34,2097,72],{"class":40},[34,2099,1800],{"class":129},[34,2101,710],{"class":40},[34,2103,2104],{"class":113},"rawData",[34,2106,2040],{"class":40},[34,2108,55],{"class":68},[34,2110,1755],{"class":48},[34,2112,802],{"class":40},[34,2114,2115],{"class":58},"'\u002Fapi\u002Funknown'",[34,2117,905],{"class":40},[34,2119,2120],{"class":36,"line":444},[34,2121,2122],{"class":97},"\u002F\u002F rawData.value is unknown | null — must narrow before use\n",[15,2124,2126],{"id":2125},"template-type-narrowing-v-if-as-type-guard","Template Type Narrowing — v-if as Type Guard",[20,2128,2130,2444,2448],{"filename":2129,"language":23},"TypeNarrowing.vue",[25,2131,2133],{"className":27,"code":2132,"language":23,"meta":29,"style":29},"\u003Cscript setup lang=\"ts\">\nimport { ref, computed } from 'vue'\n\ntype Result =\n  | { status: 'loading' }\n  | { status: 'error'; message: string }\n  | { status: 'success'; data: User[] }\n\nconst result = ref\u003CResult>({ status: 'loading' })\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003C!-- ── v-if acts as a type guard in templates ── -->\n  \u003C!-- After v-if=\"result.status === 'success'\", Vue's template compiler -->\n  \u003C!-- narrows `result` to the success variant — result.data is available. -->\n  \u003Cdiv v-if=\"result.status === 'loading'\">Loading…\u003C\u002Fdiv>\n\n  \u003Cdiv v-else-if=\"result.status === 'error'\">\n    Error: {{ result.message }}  \u003C!-- ✅ narrowed: message is accessible -->\n  \u003C\u002Fdiv>\n\n  \u003Cdiv v-else-if=\"result.status === 'success'\">\n    \u003Cdiv v-for=\"user in result.data\" :key=\"user.id\">  \u003C!-- ✅ narrowed: data exists -->\n      {{ user.name }}\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[31,2134,2135,2151,2162,2166,2176,2193,2217,2241,2245,2270,2278,2282,2291,2296,2301,2306,2329,2333,2349,2357,2366,2370,2385,2414,2419,2428,2436],{"__ignoreMap":29},[34,2136,2137,2139,2141,2143,2145,2147,2149],{"class":36,"line":37},[34,2138,41],{"class":40},[34,2140,45],{"class":44},[34,2142,49],{"class":48},[34,2144,52],{"class":48},[34,2146,55],{"class":40},[34,2148,59],{"class":58},[34,2150,62],{"class":40},[34,2152,2153,2155,2158,2160],{"class":36,"line":65},[34,2154,69],{"class":68},[34,2156,2157],{"class":40}," { ref, computed } ",[34,2159,81],{"class":68},[34,2161,84],{"class":58},[34,2163,2164],{"class":36,"line":87},[34,2165,91],{"emptyLinePlaceholder":90},[34,2167,2168,2170,2173],{"class":36,"line":94},[34,2169,75],{"class":68},[34,2171,2172],{"class":48}," Result",[34,2174,2175],{"class":68}," =\n",[34,2177,2178,2181,2183,2186,2188,2191],{"class":36,"line":101},[34,2179,2180],{"class":68},"  |",[34,2182,72],{"class":40},[34,2184,2185],{"class":129},"status",[34,2187,133],{"class":68},[34,2189,2190],{"class":58}," 'loading'",[34,2192,947],{"class":40},[34,2194,2195,2197,2199,2201,2203,2206,2208,2211,2213,2215],{"class":36,"line":107},[34,2196,2180],{"class":68},[34,2198,72],{"class":40},[34,2200,2185],{"class":129},[34,2202,133],{"class":68},[34,2204,2205],{"class":58}," 'error'",[34,2207,199],{"class":40},[34,2209,2210],{"class":129},"message",[34,2212,133],{"class":68},[34,2214,173],{"class":113},[34,2216,947],{"class":40},[34,2218,2219,2221,2223,2225,2227,2230,2232,2234,2236,2238],{"class":36,"line":126},[34,2220,2180],{"class":68},[34,2222,72],{"class":40},[34,2224,2185],{"class":129},[34,2226,133],{"class":68},[34,2228,2229],{"class":58}," 'success'",[34,2231,199],{"class":40},[34,2233,1800],{"class":129},[34,2235,133],{"class":68},[34,2237,2001],{"class":48},[34,2239,2240],{"class":40},"[] }\n",[34,2242,2243],{"class":36,"line":139},[34,2244,91],{"emptyLinePlaceholder":90},[34,2246,2247,2249,2252,2254,2256,2258,2261,2264,2267],{"class":36,"line":164},[34,2248,110],{"class":68},[34,2250,2251],{"class":113}," result",[34,2253,117],{"class":68},[34,2255,897],{"class":48},[34,2257,41],{"class":40},[34,2259,2260],{"class":48},"Result",[34,2262,2263],{"class":40},">({ status: ",[34,2265,2266],{"class":58},"'loading'",[34,2268,2269],{"class":40}," })\n",[34,2271,2272,2274,2276],{"class":36,"line":182},[34,2273,502],{"class":40},[34,2275,45],{"class":44},[34,2277,62],{"class":40},[34,2279,2280],{"class":36,"line":215},[34,2281,91],{"emptyLinePlaceholder":90},[34,2283,2284,2286,2289],{"class":36,"line":221},[34,2285,41],{"class":40},[34,2287,2288],{"class":44},"template",[34,2290,62],{"class":40},[34,2292,2293],{"class":36,"line":226},[34,2294,2295],{"class":97},"  \u003C!-- ── v-if acts as a type guard in templates ── -->\n",[34,2297,2298],{"class":36,"line":232},[34,2299,2300],{"class":97},"  \u003C!-- After v-if=\"result.status === 'success'\", Vue's template compiler -->\n",[34,2302,2303],{"class":36,"line":238},[34,2304,2305],{"class":97},"  \u003C!-- narrows `result` to the success variant — result.data is available. -->\n",[34,2307,2308,2311,2314,2317,2319,2322,2325,2327],{"class":36,"line":244},[34,2309,2310],{"class":40},"  \u003C",[34,2312,2313],{"class":44},"div",[34,2315,2316],{"class":48}," v-if",[34,2318,55],{"class":40},[34,2320,2321],{"class":58},"\"result.status === 'loading'\"",[34,2323,2324],{"class":40},">Loading…\u003C\u002F",[34,2326,2313],{"class":44},[34,2328,62],{"class":40},[34,2330,2331],{"class":36,"line":297},[34,2332,91],{"emptyLinePlaceholder":90},[34,2334,2335,2337,2339,2342,2344,2347],{"class":36,"line":309},[34,2336,2310],{"class":40},[34,2338,2313],{"class":44},[34,2340,2341],{"class":48}," v-else-if",[34,2343,55],{"class":40},[34,2345,2346],{"class":58},"\"result.status === 'error'\"",[34,2348,62],{"class":40},[34,2350,2351,2354],{"class":36,"line":326},[34,2352,2353],{"class":40},"    Error: {{ result.message }}  ",[34,2355,2356],{"class":97},"\u003C!-- ✅ narrowed: message is accessible -->\n",[34,2358,2359,2362,2364],{"class":36,"line":347},[34,2360,2361],{"class":40},"  \u003C\u002F",[34,2363,2313],{"class":44},[34,2365,62],{"class":40},[34,2367,2368],{"class":36,"line":353},[34,2369,91],{"emptyLinePlaceholder":90},[34,2371,2372,2374,2376,2378,2380,2383],{"class":36,"line":358},[34,2373,2310],{"class":40},[34,2375,2313],{"class":44},[34,2377,2341],{"class":48},[34,2379,55],{"class":40},[34,2381,2382],{"class":58},"\"result.status === 'success'\"",[34,2384,62],{"class":40},[34,2386,2387,2390,2392,2395,2397,2400,2403,2405,2408,2411],{"class":36,"line":364},[34,2388,2389],{"class":40},"    \u003C",[34,2391,2313],{"class":44},[34,2393,2394],{"class":48}," v-for",[34,2396,55],{"class":40},[34,2398,2399],{"class":58},"\"user in result.data\"",[34,2401,2402],{"class":48}," :key",[34,2404,55],{"class":40},[34,2406,2407],{"class":58},"\"user.id\"",[34,2409,2410],{"class":40},">  ",[34,2412,2413],{"class":97},"\u003C!-- ✅ narrowed: data exists -->\n",[34,2415,2416],{"class":36,"line":370},[34,2417,2418],{"class":40},"      {{ user.name }}\n",[34,2420,2421,2424,2426],{"class":36,"line":385},[34,2422,2423],{"class":40},"    \u003C\u002F",[34,2425,2313],{"class":44},[34,2427,62],{"class":40},[34,2429,2430,2432,2434],{"class":36,"line":397},[34,2431,2361],{"class":40},[34,2433,2313],{"class":44},[34,2435,62],{"class":40},[34,2437,2438,2440,2442],{"class":36,"line":403},[34,2439,502],{"class":40},[34,2441,2288],{"class":44},[34,2443,62],{"class":40},[15,2445,2447],{"id":2446},"type-safe-definemodel-34","Type-Safe defineModel (3.4+)",[20,2449,2451,2721,2725],{"filename":2450,"language":23},"TypedModel.vue",[25,2452,2454],{"className":27,"code":2453,"language":23,"meta":29,"style":29},"\u003Cscript setup lang=\"ts\">\n\u002F\u002F ── defineModel with generic type ──\n\u002F\u002F Creates: modelValue prop (typed) + update:modelValue emit (typed)\nconst model = defineModel\u003Cstring>()  \u002F\u002F Ref\u003Cstring> — writable, two-way\n\n\u002F\u002F ── Named model with type ──\nconst firstName = defineModel\u003Cstring>('firstName')  \u002F\u002F v-model:firstName\nconst lastName = defineModel\u003Cstring>('lastName')\n\n\u002F\u002F ── Model with options ──\nconst count = defineModel\u003Cnumber>({\n  default: 0,\n  required: true,\n})\n\n\u002F\u002F ── Model with custom modifiers (3.4+) ──\n\u002F\u002F v-model.capitalize → modelModifiers.capitalize\nconst text = defineModel\u003Cstring>({\n  set: (v) => v.charAt(0).toUpperCase() + v.slice(1),  \u002F\u002F transform on write\n})\n\n\u002F\u002F ── Usage in template: ──────────────────────────────────\n\u002F\u002F \u003Cinput v-model=\"model\" \u002F> — model.value is string, two-way binding\n\u002F\u002F model.value = 'hello' — emits update:modelValue automatically\n\u003C\u002Fscript>\n",[31,2455,2456,2472,2477,2482,2504,2508,2513,2538,2560,2564,2569,2586,2595,2604,2608,2612,2617,2622,2639,2690,2694,2698,2703,2708,2713],{"__ignoreMap":29},[34,2457,2458,2460,2462,2464,2466,2468,2470],{"class":36,"line":37},[34,2459,41],{"class":40},[34,2461,45],{"class":44},[34,2463,49],{"class":48},[34,2465,52],{"class":48},[34,2467,55],{"class":40},[34,2469,59],{"class":58},[34,2471,62],{"class":40},[34,2473,2474],{"class":36,"line":65},[34,2475,2476],{"class":97},"\u002F\u002F ── defineModel with generic type ──\n",[34,2478,2479],{"class":36,"line":87},[34,2480,2481],{"class":97},"\u002F\u002F Creates: modelValue prop (typed) + update:modelValue emit (typed)\n",[34,2483,2484,2486,2489,2491,2494,2496,2498,2501],{"class":36,"line":94},[34,2485,110],{"class":68},[34,2487,2488],{"class":113}," model",[34,2490,117],{"class":68},[34,2492,2493],{"class":48}," defineModel",[34,2495,41],{"class":40},[34,2497,1778],{"class":113},[34,2499,2500],{"class":40},">()  ",[34,2502,2503],{"class":97},"\u002F\u002F Ref\u003Cstring> — writable, two-way\n",[34,2505,2506],{"class":36,"line":101},[34,2507,91],{"emptyLinePlaceholder":90},[34,2509,2510],{"class":36,"line":107},[34,2511,2512],{"class":97},"\u002F\u002F ── Named model with type ──\n",[34,2514,2515,2517,2520,2522,2524,2526,2528,2530,2533,2535],{"class":36,"line":126},[34,2516,110],{"class":68},[34,2518,2519],{"class":113}," firstName",[34,2521,117],{"class":68},[34,2523,2493],{"class":48},[34,2525,41],{"class":40},[34,2527,1778],{"class":113},[34,2529,1148],{"class":40},[34,2531,2532],{"class":58},"'firstName'",[34,2534,924],{"class":40},[34,2536,2537],{"class":97},"\u002F\u002F v-model:firstName\n",[34,2539,2540,2542,2545,2547,2549,2551,2553,2555,2558],{"class":36,"line":139},[34,2541,110],{"class":68},[34,2543,2544],{"class":113}," lastName",[34,2546,117],{"class":68},[34,2548,2493],{"class":48},[34,2550,41],{"class":40},[34,2552,1778],{"class":113},[34,2554,1148],{"class":40},[34,2556,2557],{"class":58},"'lastName'",[34,2559,905],{"class":40},[34,2561,2562],{"class":36,"line":164},[34,2563,91],{"emptyLinePlaceholder":90},[34,2565,2566],{"class":36,"line":182},[34,2567,2568],{"class":97},"\u002F\u002F ── Model with options ──\n",[34,2570,2571,2573,2575,2577,2579,2581,2583],{"class":36,"line":215},[34,2572,110],{"class":68},[34,2574,892],{"class":113},[34,2576,117],{"class":68},[34,2578,2493],{"class":48},[34,2580,41],{"class":40},[34,2582,713],{"class":113},[34,2584,2585],{"class":40},">({\n",[34,2587,2588,2591,2593],{"class":36,"line":221},[34,2589,2590],{"class":40},"  default: ",[34,2592,902],{"class":113},[34,2594,306],{"class":40},[34,2596,2597,2600,2602],{"class":36,"line":226},[34,2598,2599],{"class":40},"  required: ",[34,2601,391],{"class":113},[34,2603,306],{"class":40},[34,2605,2606],{"class":36,"line":232},[34,2607,350],{"class":40},[34,2609,2610],{"class":36,"line":238},[34,2611,91],{"emptyLinePlaceholder":90},[34,2613,2614],{"class":36,"line":244},[34,2615,2616],{"class":97},"\u002F\u002F ── Model with custom modifiers (3.4+) ──\n",[34,2618,2619],{"class":36,"line":297},[34,2620,2621],{"class":97},"\u002F\u002F v-model.capitalize → modelModifiers.capitalize\n",[34,2623,2624,2626,2629,2631,2633,2635,2637],{"class":36,"line":309},[34,2625,110],{"class":68},[34,2627,2628],{"class":113}," text",[34,2630,117],{"class":68},[34,2632,2493],{"class":48},[34,2634,41],{"class":40},[34,2636,1778],{"class":113},[34,2638,2585],{"class":40},[34,2640,2641,2644,2646,2648,2650,2652,2655,2658,2660,2662,2665,2668,2671,2674,2676,2679,2681,2684,2687],{"class":36,"line":326},[34,2642,2643],{"class":48},"  set",[34,2645,450],{"class":40},[34,2647,453],{"class":129},[34,2649,460],{"class":40},[34,2651,317],{"class":68},[34,2653,2654],{"class":40}," v.",[34,2656,2657],{"class":48},"charAt",[34,2659,802],{"class":40},[34,2661,902],{"class":113},[34,2663,2664],{"class":40},").",[34,2666,2667],{"class":48},"toUpperCase",[34,2669,2670],{"class":40},"() ",[34,2672,2673],{"class":68},"+",[34,2675,2654],{"class":40},[34,2677,2678],{"class":48},"slice",[34,2680,802],{"class":40},[34,2682,2683],{"class":113},"1",[34,2685,2686],{"class":40},"),  ",[34,2688,2689],{"class":97},"\u002F\u002F transform on write\n",[34,2691,2692],{"class":36,"line":347},[34,2693,350],{"class":40},[34,2695,2696],{"class":36,"line":353},[34,2697,91],{"emptyLinePlaceholder":90},[34,2699,2700],{"class":36,"line":358},[34,2701,2702],{"class":97},"\u002F\u002F ── Usage in template: ──────────────────────────────────\n",[34,2704,2705],{"class":36,"line":364},[34,2706,2707],{"class":97},"\u002F\u002F \u003Cinput v-model=\"model\" \u002F> — model.value is string, two-way binding\n",[34,2709,2710],{"class":36,"line":370},[34,2711,2712],{"class":97},"\u002F\u002F model.value = 'hello' — emits update:modelValue automatically\n",[34,2714,2715,2717,2719],{"class":36,"line":385},[34,2716,502],{"class":40},[34,2718,45],{"class":44},[34,2720,62],{"class":40},[15,2722,2724],{"id":2723},"tips-tricks","💡 Tips & Tricks",[20,2726,2728],{"filename":2727,"language":1067},"tips.ts",[25,2729,2731],{"className":1070,"code":2730,"language":1067,"meta":29,"style":29},"\u002F\u002F ── 1. Type the $event in inline handlers ──\n\u002F\u002F @click=\"(e: MouseEvent) => handler(e)\" — e is typed\n\n\u002F\u002F ── 2. ComponentInstance type for refs ──\nimport type { ComponentPublicInstance } from 'vue'\nconst ref = ref\u003CComponentPublicInstance | null>(null)\n\n\u002F\u002F ── 3. DefineEmit with object syntax (3.3+) is cleaner ──\nconst emit = defineEmits\u003C{\n  update: [id: number, data: Partial\u003CUser>]\n  delete: [id: number]\n}>()\n\n\u002F\u002F ── 4. Generic type for slots: use defineSlots ──\nconst slots = defineSlots\u003C{\n  default: (props: { item: T }) => any\n  header: () => any\n}>()\n\n\u002F\u002F ── 5. Template expression type checking ──\n\u002F\u002F Vue's template compiler checks template expressions against \u003Cscript setup> types.\n\u002F\u002F {{ user.name }} where user: User → name is type-checked.\n\u002F\u002F {{ user.nonExistent }} → TS error in template (with Volar).\n",[31,2732,2733,2738,2743,2747,2752,2765,2789,2793,2798,2810,2838,2855,2859,2863,2868,2882,2913,2926,2930,2934,2939,2944,2949],{"__ignoreMap":29},[34,2734,2735],{"class":36,"line":37},[34,2736,2737],{"class":97},"\u002F\u002F ── 1. Type the $event in inline handlers ──\n",[34,2739,2740],{"class":36,"line":65},[34,2741,2742],{"class":97},"\u002F\u002F @click=\"(e: MouseEvent) => handler(e)\" — e is typed\n",[34,2744,2745],{"class":36,"line":87},[34,2746,91],{"emptyLinePlaceholder":90},[34,2748,2749],{"class":36,"line":94},[34,2750,2751],{"class":97},"\u002F\u002F ── 2. ComponentInstance type for refs ──\n",[34,2753,2754,2756,2758,2761,2763],{"class":36,"line":101},[34,2755,69],{"class":68},[34,2757,1188],{"class":68},[34,2759,2760],{"class":40}," { ComponentPublicInstance } ",[34,2762,81],{"class":68},[34,2764,84],{"class":58},[34,2766,2767,2769,2771,2773,2775,2777,2779,2781,2783,2785,2787],{"class":36,"line":107},[34,2768,110],{"class":68},[34,2770,897],{"class":113},[34,2772,117],{"class":68},[34,2774,897],{"class":48},[34,2776,41],{"class":40},[34,2778,1131],{"class":48},[34,2780,150],{"class":68},[34,2782,1145],{"class":113},[34,2784,1148],{"class":40},[34,2786,1151],{"class":113},[34,2788,905],{"class":40},[34,2790,2791],{"class":36,"line":126},[34,2792,91],{"emptyLinePlaceholder":90},[34,2794,2795],{"class":36,"line":139},[34,2796,2797],{"class":97},"\u002F\u002F ── 3. DefineEmit with object syntax (3.3+) is cleaner ──\n",[34,2799,2800,2802,2804,2806,2808],{"class":36,"line":164},[34,2801,110],{"class":68},[34,2803,546],{"class":113},[34,2805,117],{"class":68},[34,2807,551],{"class":48},[34,2809,123],{"class":40},[34,2811,2812,2814,2816,2818,2820,2822,2824,2826,2828,2830,2832,2834,2836],{"class":36,"line":182},[34,2813,701],{"class":129},[34,2815,133],{"class":68},[34,2817,465],{"class":40},[34,2819,627],{"class":48},[34,2821,710],{"class":40},[34,2823,713],{"class":113},[34,2825,258],{"class":40},[34,2827,1800],{"class":48},[34,2829,710],{"class":40},[34,2831,722],{"class":48},[34,2833,41],{"class":40},[34,2835,596],{"class":48},[34,2837,729],{"class":40},[34,2839,2840,2842,2844,2846,2848,2850,2852],{"class":36,"line":215},[34,2841,734],{"class":129},[34,2843,133],{"class":68},[34,2845,465],{"class":40},[34,2847,627],{"class":48},[34,2849,710],{"class":40},[34,2851,713],{"class":113},[34,2853,2854],{"class":40},"]\n",[34,2856,2857],{"class":36,"line":221},[34,2858,218],{"class":40},[34,2860,2861],{"class":36,"line":226},[34,2862,91],{"emptyLinePlaceholder":90},[34,2864,2865],{"class":36,"line":232},[34,2866,2867],{"class":97},"\u002F\u002F ── 4. Generic type for slots: use defineSlots ──\n",[34,2869,2870,2872,2875,2877,2880],{"class":36,"line":238},[34,2871,110],{"class":68},[34,2873,2874],{"class":113}," slots",[34,2876,117],{"class":68},[34,2878,2879],{"class":48}," defineSlots",[34,2881,123],{"class":40},[34,2883,2884,2887,2889,2891,2894,2896,2898,2901,2903,2905,2908,2910],{"class":36,"line":244},[34,2885,2886],{"class":48},"  default",[34,2888,133],{"class":68},[34,2890,1324],{"class":40},[34,2892,2893],{"class":129},"props",[34,2895,133],{"class":68},[34,2897,72],{"class":40},[34,2899,2900],{"class":129},"item",[34,2902,133],{"class":68},[34,2904,1812],{"class":48},[34,2906,2907],{"class":40}," }) ",[34,2909,317],{"class":68},[34,2911,2912],{"class":113}," any\n",[34,2914,2915,2918,2920,2922,2924],{"class":36,"line":297},[34,2916,2917],{"class":48},"  header",[34,2919,133],{"class":68},[34,2921,1356],{"class":40},[34,2923,317],{"class":68},[34,2925,2912],{"class":113},[34,2927,2928],{"class":36,"line":309},[34,2929,218],{"class":40},[34,2931,2932],{"class":36,"line":326},[34,2933,91],{"emptyLinePlaceholder":90},[34,2935,2936],{"class":36,"line":347},[34,2937,2938],{"class":97},"\u002F\u002F ── 5. Template expression type checking ──\n",[34,2940,2941],{"class":36,"line":353},[34,2942,2943],{"class":97},"\u002F\u002F Vue's template compiler checks template expressions against \u003Cscript setup> types.\n",[34,2945,2946],{"class":36,"line":358},[34,2947,2948],{"class":97},"\u002F\u002F {{ user.name }} where user: User → name is type-checked.\n",[34,2950,2951],{"class":36,"line":364},[34,2952,2953],{"class":97},"\u002F\u002F {{ user.nonExistent }} → TS error in template (with Volar).\n",[15,2955,2957],{"id":2956},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[20,2959,2961],{"filename":2960,"language":1067},"edge-cases.ts",[25,2962,2964],{"className":1070,"code":2963,"language":1067,"meta":29,"style":29},"\u002F\u002F ── 1. defineProps generic syntax: no default values at runtime ──\n\u002F\u002F defineProps\u003C{ x?: string }>() → x is string | undefined, no default.\n\u002F\u002F withDefaults adds runtime defaults (required for optional props to have values).\n\n\u002F\u002F ── 2. PropType cast for literal unions in runtime validation ──\n\u002F\u002F type: String as PropType\u003C'a' | 'b'> → TS knows it's the union, Vue checks string at runtime.\n\u002F\u002F Without cast, Vue sees String (general), not the literal union.\n\n\u002F\u002F ── 3. defineModel default value doesn't affect the parent's bound ref ──\n\u002F\u002F defineModel\u003Cstring>({ default: 'x' }) — default used if parent doesn't pass v-model.\n\u002F\u002F If parent passes v-model=\"myRef\", default is ignored — myRef controls the value.\n\n\u002F\u002F ── 4. inject() returns undefined if no provider — always guard or provide default ──\n\u002F\u002F const ctx = inject(KEY)  → T | undefined\n\u002F\u002F const ctx = inject(KEY, defaultValue)  → T (no undefined)\n\n\u002F\u002F ── 5. Template ref typing with generic components ──\n\u002F\u002F ref\u003Ctypeof GenericComponent> doesn't capture the generic parameter.\n\u002F\u002F Use ComponentPublicInstance\u003Ctypeof GenericComponent> for full typing.\n\n\u002F\u002F ── 6. Ref\u003CT | null> vs Ref\u003CT> — null assignment ──\n\u002F\u002F const x = ref\u003CT | null>(null) — correct: starts as null, can be T later.\n\u002F\u002F const x = ref\u003CT>(null) — TS error: null is not assignable to T.\n\u002F\u002F Always include | null in the ref type if the initial value is null.\n",[31,2965,2966,2971,2976,2981,2985,2990,2995,3000,3004,3009,3014,3019,3023,3028,3033,3038,3042,3047,3052,3057,3061,3066,3071,3076],{"__ignoreMap":29},[34,2967,2968],{"class":36,"line":37},[34,2969,2970],{"class":97},"\u002F\u002F ── 1. defineProps generic syntax: no default values at runtime ──\n",[34,2972,2973],{"class":36,"line":65},[34,2974,2975],{"class":97},"\u002F\u002F defineProps\u003C{ x?: string }>() → x is string | undefined, no default.\n",[34,2977,2978],{"class":36,"line":87},[34,2979,2980],{"class":97},"\u002F\u002F withDefaults adds runtime defaults (required for optional props to have values).\n",[34,2982,2983],{"class":36,"line":94},[34,2984,91],{"emptyLinePlaceholder":90},[34,2986,2987],{"class":36,"line":101},[34,2988,2989],{"class":97},"\u002F\u002F ── 2. PropType cast for literal unions in runtime validation ──\n",[34,2991,2992],{"class":36,"line":107},[34,2993,2994],{"class":97},"\u002F\u002F type: String as PropType\u003C'a' | 'b'> → TS knows it's the union, Vue checks string at runtime.\n",[34,2996,2997],{"class":36,"line":126},[34,2998,2999],{"class":97},"\u002F\u002F Without cast, Vue sees String (general), not the literal union.\n",[34,3001,3002],{"class":36,"line":139},[34,3003,91],{"emptyLinePlaceholder":90},[34,3005,3006],{"class":36,"line":164},[34,3007,3008],{"class":97},"\u002F\u002F ── 3. defineModel default value doesn't affect the parent's bound ref ──\n",[34,3010,3011],{"class":36,"line":182},[34,3012,3013],{"class":97},"\u002F\u002F defineModel\u003Cstring>({ default: 'x' }) — default used if parent doesn't pass v-model.\n",[34,3015,3016],{"class":36,"line":215},[34,3017,3018],{"class":97},"\u002F\u002F If parent passes v-model=\"myRef\", default is ignored — myRef controls the value.\n",[34,3020,3021],{"class":36,"line":221},[34,3022,91],{"emptyLinePlaceholder":90},[34,3024,3025],{"class":36,"line":226},[34,3026,3027],{"class":97},"\u002F\u002F ── 4. inject() returns undefined if no provider — always guard or provide default ──\n",[34,3029,3030],{"class":36,"line":232},[34,3031,3032],{"class":97},"\u002F\u002F const ctx = inject(KEY)  → T | undefined\n",[34,3034,3035],{"class":36,"line":238},[34,3036,3037],{"class":97},"\u002F\u002F const ctx = inject(KEY, defaultValue)  → T (no undefined)\n",[34,3039,3040],{"class":36,"line":244},[34,3041,91],{"emptyLinePlaceholder":90},[34,3043,3044],{"class":36,"line":297},[34,3045,3046],{"class":97},"\u002F\u002F ── 5. Template ref typing with generic components ──\n",[34,3048,3049],{"class":36,"line":309},[34,3050,3051],{"class":97},"\u002F\u002F ref\u003Ctypeof GenericComponent> doesn't capture the generic parameter.\n",[34,3053,3054],{"class":36,"line":326},[34,3055,3056],{"class":97},"\u002F\u002F Use ComponentPublicInstance\u003Ctypeof GenericComponent> for full typing.\n",[34,3058,3059],{"class":36,"line":347},[34,3060,91],{"emptyLinePlaceholder":90},[34,3062,3063],{"class":36,"line":353},[34,3064,3065],{"class":97},"\u002F\u002F ── 6. Ref\u003CT | null> vs Ref\u003CT> — null assignment ──\n",[34,3067,3068],{"class":36,"line":358},[34,3069,3070],{"class":97},"\u002F\u002F const x = ref\u003CT | null>(null) — correct: starts as null, can be T later.\n",[34,3072,3073],{"class":36,"line":364},[34,3074,3075],{"class":97},"\u002F\u002F const x = ref\u003CT>(null) — TS error: null is not assignable to T.\n",[34,3077,3078],{"class":36,"line":370},[34,3079,3080],{"class":97},"\u002F\u002F Always include | null in the ref type if the initial value is null.\n",[15,3082,3084],{"id":3083},"spot-the-bug","🧠 Spot the Bug",[3086,3087,3088,3089,3092],"p",{},"A component's optional prop is ",[31,3090,3091],{},"undefined"," at runtime despite the parent passing a value.",[20,3094,3096],{"filename":3095,"language":23},"TypeBug.vue",[25,3097,3099],{"className":27,"code":3098,"language":23,"meta":29,"style":29},"\u003C!-- Child -->\n\u003Cscript setup lang=\"ts\">\nconst props = defineProps\u003C{\n  title?: string\n  count?: number\n}>()\n\u002F\u002F No withDefaults — title and count are undefined at runtime if parent doesn't pass them\n\u002F\u002F But even if parent passes them, the TYPE says optional (string | undefined)\n\u003C\u002Fscript>\n\n\u003C!-- Parent -->\n\u003Ctemplate>\n  \u003CChild :title=\"hello\" \u002F>  \u003C!-- title is passed, count is not -->\n\u003C\u002Ftemplate>\n",[31,3100,3101,3106,3122,3134,3144,3153,3157,3162,3167,3175,3179,3184,3192,3221],{"__ignoreMap":29},[34,3102,3103],{"class":36,"line":37},[34,3104,3105],{"class":97},"\u003C!-- Child -->\n",[34,3107,3108,3110,3112,3114,3116,3118,3120],{"class":36,"line":65},[34,3109,41],{"class":40},[34,3111,45],{"class":44},[34,3113,49],{"class":48},[34,3115,52],{"class":48},[34,3117,55],{"class":40},[34,3119,59],{"class":58},[34,3121,62],{"class":40},[34,3123,3124,3126,3128,3130,3132],{"class":36,"line":87},[34,3125,110],{"class":68},[34,3127,114],{"class":113},[34,3129,117],{"class":68},[34,3131,120],{"class":48},[34,3133,123],{"class":40},[34,3135,3136,3139,3141],{"class":36,"line":94},[34,3137,3138],{"class":129},"  title",[34,3140,170],{"class":68},[34,3142,3143],{"class":113}," string\n",[34,3145,3146,3149,3151],{"class":36,"line":101},[34,3147,3148],{"class":129},"  count",[34,3150,170],{"class":68},[34,3152,136],{"class":113},[34,3154,3155],{"class":36,"line":107},[34,3156,218],{"class":40},[34,3158,3159],{"class":36,"line":126},[34,3160,3161],{"class":97},"\u002F\u002F No withDefaults — title and count are undefined at runtime if parent doesn't pass them\n",[34,3163,3164],{"class":36,"line":139},[34,3165,3166],{"class":97},"\u002F\u002F But even if parent passes them, the TYPE says optional (string | undefined)\n",[34,3168,3169,3171,3173],{"class":36,"line":164},[34,3170,502],{"class":40},[34,3172,45],{"class":44},[34,3174,62],{"class":40},[34,3176,3177],{"class":36,"line":182},[34,3178,91],{"emptyLinePlaceholder":90},[34,3180,3181],{"class":36,"line":215},[34,3182,3183],{"class":97},"\u003C!-- Parent -->\n",[34,3185,3186,3188,3190],{"class":36,"line":221},[34,3187,41],{"class":40},[34,3189,2288],{"class":44},[34,3191,62],{"class":40},[34,3193,3194,3196,3199,3202,3205,3207,3210,3213,3215,3218],{"class":36,"line":226},[34,3195,2310],{"class":40},[34,3197,3198],{"class":44},"Child",[34,3200,3201],{"class":40}," :",[34,3203,3204],{"class":48},"title",[34,3206,55],{"class":40},[34,3208,3209],{"class":58},"\"",[34,3211,3212],{"class":40},"hello",[34,3214,3209],{"class":58},[34,3216,3217],{"class":40}," \u002F>  ",[34,3219,3220],{"class":97},"\u003C!-- title is passed, count is not -->\n",[34,3222,3223,3225,3227],{"class":36,"line":232},[34,3224,502],{"class":40},[34,3226,2288],{"class":44},[34,3228,62],{"class":40},[3230,3231,3232,3236,3261,3273,3379],"details",{},[3233,3234,3235],"summary",{},"Answer",[3086,3237,3238,3239,3241,3242,3244,3245,3248,3249,3251,3252,258,3255,3241,3257,3260],{},"The issue isn't that ",[31,3240,3204],{}," is ",[31,3243,3091],{}," — it's that without ",[31,3246,3247],{},"withDefaults",", optional props are ",[31,3250,3091],{}," at runtime when not passed. But if the parent passes ",[31,3253,3254],{},":title=\"hello\"",[31,3256,3204],{},[31,3258,3259],{},"'hello'",". The real bug is the assumption that \"optional\" means \"has a default\" — it doesn't. Optional means \"can be absent (undefined).\"",[3086,3262,3263,3264,3267,3268,3270,3271,133],{},"If the developer expects ",[31,3265,3266],{},"count"," to default to ",[31,3269,902],{}," when not passed, it's actually ",[31,3272,3091],{},[20,3274,3276],{"filename":3275,"language":1067},"TypeFixed.ts",[25,3277,3279],{"className":1070,"code":3278,"language":1067,"meta":29,"style":29},"const props = defineProps\u003C{\n  title?: string\n  count?: number\n}>()\n\n\u002F\u002F ✅ Add withDefaults to provide runtime defaults\nconst { title = 'Default', count = 0 } = withDefaults(props, {\n  title: 'Default',\n  count: 0,\n})\n\u002F\u002F Now: count is 0 when parent doesn't pass it (not undefined)\n",[31,3280,3281,3293,3301,3309,3313,3317,3322,3351,3361,3370,3374],{"__ignoreMap":29},[34,3282,3283,3285,3287,3289,3291],{"class":36,"line":37},[34,3284,110],{"class":68},[34,3286,114],{"class":113},[34,3288,117],{"class":68},[34,3290,120],{"class":48},[34,3292,123],{"class":40},[34,3294,3295,3297,3299],{"class":36,"line":65},[34,3296,3138],{"class":129},[34,3298,170],{"class":68},[34,3300,3143],{"class":113},[34,3302,3303,3305,3307],{"class":36,"line":87},[34,3304,3148],{"class":129},[34,3306,170],{"class":68},[34,3308,136],{"class":113},[34,3310,3311],{"class":36,"line":94},[34,3312,218],{"class":40},[34,3314,3315],{"class":36,"line":101},[34,3316,91],{"emptyLinePlaceholder":90},[34,3318,3319],{"class":36,"line":107},[34,3320,3321],{"class":97},"\u002F\u002F ✅ Add withDefaults to provide runtime defaults\n",[34,3323,3324,3326,3328,3330,3332,3335,3337,3339,3341,3343,3345,3347,3349],{"class":36,"line":126},[34,3325,110],{"class":68},[34,3327,72],{"class":40},[34,3329,3204],{"class":113},[34,3331,117],{"class":68},[34,3333,3334],{"class":58}," 'Default'",[34,3336,258],{"class":40},[34,3338,3266],{"class":113},[34,3340,117],{"class":68},[34,3342,962],{"class":113},[34,3344,2040],{"class":40},[34,3346,55],{"class":68},[34,3348,291],{"class":48},[34,3350,294],{"class":40},[34,3352,3353,3356,3359],{"class":36,"line":139},[34,3354,3355],{"class":40},"  title: ",[34,3357,3358],{"class":58},"'Default'",[34,3360,306],{"class":40},[34,3362,3363,3366,3368],{"class":36,"line":164},[34,3364,3365],{"class":40},"  count: ",[34,3367,902],{"class":113},[34,3369,306],{"class":40},[34,3371,3372],{"class":36,"line":182},[34,3373,350],{"class":40},[34,3375,3376],{"class":36,"line":215},[34,3377,3378],{"class":97},"\u002F\u002F Now: count is 0 when parent doesn't pass it (not undefined)\n",[3086,3380,3381,3385,3386,3389,3390,3393,3394,3396,3397,3400],{},[3382,3383,3384],"strong",{},"The lesson",": optional props (",[31,3387,3388],{},"?",") in ",[31,3391,3392],{},"defineProps\u003C{...}>()"," are ",[31,3395,3091],{}," at runtime when absent — they don't have defaults. Use ",[31,3398,3399],{},"withDefaults()"," to provide runtime fallback values.",[3402,3403,3404],"style",{},"html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .sk71V, html code.shiki .sk71V{--shiki-default:#22863A;--shiki-github-dark:#85E89D}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html pre.shiki code .sCrzJ, html code.shiki .sCrzJ{--shiki-default:#E36209;--shiki-github-dark:#FFAB70}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);}html .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}",{"title":29,"searchDepth":65,"depth":65,"links":3406},[3407,3408,3409,3410,3411,3412,3413,3414],{"id":17,"depth":65,"text":18},{"id":509,"depth":65,"text":510},{"id":835,"depth":65,"text":836},{"id":1227,"depth":65,"text":1228},{"id":1703,"depth":65,"text":1704},{"id":2125,"depth":65,"text":2126},{"id":2956,"depth":65,"text":2957},{"id":3083,"depth":65,"text":3084},"defineProps\u002FdefineEmits generics, component instance typing, defineModel type safety, provide\u002Finject InjectionKey typing, composable generics, and type narrowing in templates.","md",{},"\u002Fvue\u002F17-typescript-with-vue",{"title":5,"description":3415},"vue\u002F17-typescript-with-vue","ilxzWmr9BFnIphDZ7TVPx0k34GoJwosd8cIEac1Qy00",1789924655749]