[[ðArticles]] > [[ð2024 Articles]]
![[2024-07-26.webp|cover-picture]]
[[Neovim]]ã«ãŠãã§ããã ã**åå®å
š**ãã€**å¿«é©**ãª[[Nuxt3]]ã®éçºãã§ãããããèšå®ããã¥ãŒãã³ã°ããŠã¿ãŸããã
## ã¯ããã«
ç§ã¯ãã¹ãŠã®éçºã[[Neovim]]ã§è¡ã£ãŠããŸãããã¡ããWebéçºãã§ããæè¿ã§ã¯[[Nuxt3]]ãš[[TypeScript]]ã§éçºããããšãå€ãã§ãã
ãããã[[Nuxt3]]ãããžã§ã¯ãã®ããã©ã«ãèšå®ã§ã¯ã**å®å
šãããå¿«é©ã**ã«èµãåãããŠããå°è±¡ãåããŸããããããããšã[[IntelliJ IDEA]]/[[WebStorm]]ã[[VSCode]]ã§ã¯ãšãã£ã¿ããã©ã°ã€ã³ãããŸãããšãã©ããŒããŠãããŠããã®ãããããŸãããã§ãããå°ãªããšã[[Neovim]]ã䜿ã£ãŠããäžã§ã¯ãäžèŸã«ãå®å
šãšã¯çšé ããšæããŠããŸãã
æ¬èšäºã§ã¯ã**å®å
šã**ãš**å¿«é©ã**ã«é¢ãã課é¡ã亀äºã«åãäžãã解決çã玹ä»ããŠãããŸãã
## åäœç¢ºèªãããžã§ã¯ãã®äœæ
åäœç¢ºèªãããããžã§ã¯ããäœæããŸãã[[Bun]]ã䜿ã£ãŠããŸãã[[Node.js]]ã§ãããããåããããªæåã«ãªããšæããŸãã
| ããŒã« | ããŒãžã§ã³ |
| ------- | ------ |
| [[Bun]] | 1.1.20 |
```console
bun x nuxi@latest init "article-nuxt-sandbox"
cd article-nuxt-sandbox
bun add --optional typescript
```
`app.vue`ãå€æŽããŸãã
```html
<template>
<NuxtPage />
</template>
```
ããã€ããã¡ã€ã«ãäœæããŸãã
`pages/top.vue`
```html
<template>
<Header1 text="hello!" />
</template>
```
`components/Header1.vue`
```html
<script setup lang="ts">
defineProps<{
text: string;
}>();
</script>
<template>
<h1 v-text="text"></h1>
</template>
```
**`bun dev` ãå®è¡ããŠãããŸãã**
```console
bun dev
```
## Auto-importsãç¡å¹åããã
### Auto-importsã®ã¡ãªãã
[[Auto-imports (Nuxt)|Auto-imports]]ã¯ãimportæãæžããªããŠãèªåã§é¢æ°ãã³ã³ããŒãã³ãã¿ã°ãªã©ãèªèããŠããã[[Nuxt]]ã®æ©èœã§ãã以äžã®ãããªæ©æµããããŸãã
- ã³ãŒãéãå°ãªããªã
- ãããžã§ã¯ãæ§æãå€ãã£ããšãã«importæã®ä¿®æ£ãäžèŠ
å
ã»ã©ã®`top.vue`ã§`Header1.vue`ã®importæããªãã®ã¯[[Auto-imports (Nuxt)|Auto-imports]]ã®å¹æã«ãããã®ã§ãã
```html
<template>
<Header1 text="hello!" />
</template>
```
æšæºã§æå¹ã«ãªã£ãŠããããšããã[[Nuxt3]]ãšããŠã¯æšå¥šãããŠããæ©èœã ãšæããŸãã
### Auto-importsã®ãã¡ãªãã
äžæ¹ã[[Auto-imports (Nuxt)|Auto-imports]]ã«ã¯ãã¡ãªããããããšæã£ãŠããŸãã
- ãœãŒã¹ã³ãŒãããåå®çŸ©ããã«ãããããã[[IDE]]ã«èªèããããŸã§ã¯æ£ããåããªã
- [[LSP]]ãåèµ·åãããããã°ããåŸ
ã£ããããŠæ§åèŠ... ãšãããœã¯ãœã¯æ
- importäžèŠã«ããããããããœãŒã¹ã³ãŒãã«ãã£ãŠã¯importæãæ··åšãã
- [[IDE]]ã®è£å®ã§å
¥åãããšãåæã«importæãæ¿å
¥ãããããšãå€ããã
- ã³ãŒããžã£ã³ãã®ãšãã«åå®çŸ©ãã¡ã€ã«ãçµç±ããå¿
èŠããã
3ã€ç®ã«ã€ããŠãããšãã° `<Header1>`ããã³ãŒããžã£ã³ããããšã以äžã®ãããª`components.d.ts`ãéããŸãã
```ts
import type { DefineComponent, SlotsType } from 'vue'
type IslandComponent<T extends DefineComponent> = T & DefineComponent<{}, {refresh: () => Promise<void>}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, SlotsType<{ fallback: { error: unknown } }>>
interface _GlobalComponents {
'Header1': typeof import("../components/Header1.vue")['default']
'NuxtWelcome': typeof import("../node_modules/nuxt/dist/app/components/welcome")['default']
'NuxtLayout': typeof import("../node_modules/nuxt/dist/app/components/nuxt-layout")['default']
```
`Header1`ã®ãšããã«ã«ãŒãœã«ãããã£ãŠããã®ã§ãããããå³åŽã® `"../components/Header1.vue"`ã«ç§»åããŠåã³ãžã£ã³ãããããšã§ããããã`Helader1.vue`ãéããŸããæ°ç§ã«ãæºããªãæéã§ã¯ãããŸãããç©ã¿éãªããšãããªãã®ã¹ãã¬ã¹ã«ãªããŸãã
> [!question]- `<Header1>`ããã³ãŒããžã£ã³ãã§ããªãå Žå
> `bun dev`ã³ãã³ãã«ãããã«ããå¿ããŠããªãã確èªããŠãã ãããäžåºŠããã«ãããŠããªããš `.nuxt` é
äžã® `components.d.ts` ãçæãããªãã®ã§ã³ãŒããžã£ã³ãã§ããŸããã
### Auto-importsãå©çšããç¯å²ã«ã€ããŠ
ç¡å¹åã®è³åŠã«ã€ããŠã¯å²ãããšæããŸãããããã§ã¯ç¡å¹åè³æã®ç«å Žããšããã©ã®ããã«ç¡å¹åããŠãããã«ã€ããŠè©±ãé²ããŸãããã ããã¹ãŠã®[[Auto-imports (Nuxt)|Auto-imports]]ãç¡å¹ã«ããã®ã¯ãããã«ããããããªãšæã£ãŠããŸãã
ããšãã°ã以äžã®ã³ãŒãã¯[[Auto-imports (Nuxt)|Auto-imports]]ãå®å
šã«ç¡å¹åãããšã`ref`, `computed`, `onMounted` ããšã©ãŒã«ãªããŸãã
```html
<script setup lang="ts">
const count = ref(0);
const doubleCount = computed(() => count.value * 2);
const handleClick = () => {
count.value++;
};
onMounted(() => {
console.log("mounted!!");
});
</script>
<template>
<div>{{ count }}</div>
<div>{{ doubleCount }}</div>
<button @click="handleClick">+</button>
</template>
```
以äžã®importæãè¿œå ããã°ãšã©ãŒã¯æ¶ããŸãã
```ts
import { computed, ref, onMounted } from "vue";
```
ããããããããã«ãã€ã³ã®é¢æ°ã¯éåžžã«å©çšé »åºŠãé«ããã¹ãããããå©çšããŠã³ãŒãã£ã³ã°ããŠããå Žåã¯importæã®æ¿å
¥ãããªãé¢åã«ãªããŸãã
![[2024-07-25_07h25_16.webm|frame]]
*quick fixã䜿ãããšã¯ããéœåºŠæ¿å
¥ãå¿
èŠ*
ãŸãããããã«ã€ããŠã¯ã[[Auto-imports (Nuxt)|Auto-imports]]ã䜿ã£ããšããŠã以äž2ã€ã®ãã¡ãªããã¯çºçããŸããã
- ãœãŒã¹ã³ãŒãããåå®çŸ©ããã«ãããããã[[IDE]]ã«èªèããããŸã§ã¯æ£ããåããªã
- æ§æãå€ããããšã¯ã»ãŒãªããããäžåºŠã€ã³ã¹ããŒã«ããã°åé¡ã¯çºçããªã
- ã³ãŒããžã£ã³ãã®ãšãã«åå®çŸ©ãã¡ã€ã«ãçµç±ããå¿
èŠããã
- çŽæ¥ `node_modules` ã®åå®çŸ©ã«ããžã£ã³ãã§ãã
以äžããã**éçºäžãããžã§ã¯ãã«å«ãŸãããã¡ã€ã«ã«éã** [[Auto-imports (Nuxt)|Auto-imports]]ãç¡å¹ã«ããããšããã«ãŒã«ãæ¡çšããããšã«ããŸãããèšãæãããšã**ãã«ãã€ã³ã«ã¯** [[Auto-imports (Nuxt)|Auto-imports]]ãå©çšããããšãèšããŸãã
### ç¡å¹åã®æ¹æ³
`nuxt.config.ts`ã«ä»¥äž2è¡ãè¿œå ããããšã§å®çŸããŸãã
```ts
export default defineNuxtConfig({
// ã€ã³ããŒãçš(Auto-importså«ã)ã®åå®çŸ©ã«è¿œå ããããã¹ãã£ã³ããªã
imports: { scan: false },
// ã³ã³ããŒãã³ããšããŠæ±ããã£ã¬ã¯ããªãæå®ããªã
components: { dirs: [] }
})
```
> [!caution]
> `imports.scan`ã¯[[Nuxt]]ã®ããã¥ã¡ã³ãã§èŠã€ããããŸããã§ããã[imports.ts](https://github.com/nuxt/nuxt/blob/f78da087df40e08f2e9239be10a11a75fac07db8/packages/schema/src/types/imports.ts?plain=1#L23) ã«ã¯èšèŒãããŠãããå®éã«æå³éãåãã¯ããŸãããããããªãåããªããªããªã¹ã¯ãæ¿ç¥ã®äžã§ãå©çšãã ããã
#### imports.scan
`imports.scan`ã`false`ã«ãããšã`imports.d.ts`ã«`composables`ã`utils`ã®å
¬éã¡ã³ããè¿œå ãããªããªããŸããããšãã°ã`composables/useBye.ts`ãååšãããšã
```ts
export function useBye() {
const bye = () => "ð";
return { bye };
}
```
éåžžã¯`.nuxt/imports.d.ts`ã«è¿œå ããã以äžã®å®çŸ©ãè¿œå ãããªããªããŸãã
```ts
export { useBye } from '../composables/useBye';
```
çµæçã«ã[[Auto-imports (Nuxt)|Auto-imports]]ã`"#imports"`ããã®importæã䜿ããªããªããŸãã
> [!hint]
> `ref` ã `computed` ãªã©ããã¹ãŠç¡å¹ã«ãããå Žå㯠`imports.autoImport` ã `false` ã«ãããšããã§ãããã
#### components.dirs
`components.dirs`ã«ç©ºãæå®ããããšã«ãã£ãŠãã³ã³ããŒãã³ããšããŠæ±ã察象ããªããªããçµæçã«[[Auto-imports (Nuxt)|Auto-imports]]ãç¡å¹åãããŸãã
> To disable auto-importing components from your own ~/components directory, you can set components.dirs to an empty array (though note that this will not affect components added by modules).
> *[Auto\-imports · Nuxt > Auto-imported Components](https://nuxt.com/docs/guide/concepts/auto-imports#auto-imported-components) ãã*
å
ã»ã©ã®`Header1.vue`ãäŸã«ããããšã`components.d.ts`ã«å®çŸ©ãããŠãã以äžã®ã€ã³ã¿ãŒãã§ãŒã¹ããããã£ãèšèŒãããªããªããŸãã
```ts
'Header1': typeof import("../components/Header1.vue")['default']
```
### ç¡å¹åã®ç¢ºèª
[[Auto-imports (Nuxt)|Auto-imports]]ãç¡å¹åãããããšã確èªããŸããäžåºŠãã«ãããªãããŸãã
```console
bun dev
```
`pages/top.vue`ã®`Header1`ã§ã³ãŒããžã£ã³ãããŠãäœãèµ·ãããªãããšã確èªããŸãã
```html
<template>
<Header1 text="hello!" />
</template>
```
次ã«`import`æãè¿œå ããŠã¿ãŸãã
```html
<script setup lang="ts">
import Header1 from "../components/Header1.vue";
</script>
<template>
<Header1 text="hello!" />
</template>
```
ãã®çŽåŸã« `Header1` ããã³ãŒããžã£ã³ãã§ããããšã確èªããŸãããã
![[2024-07-28_18h52_16.webm|frame]]
*`<Header1>`ããçŽæ¥`Header1.vue`ã«ãžã£ã³ãã§ãã*
ãŸãã`top.vue`ã«`ref`ã`computed`ãè¿œå ããŠãããšã©ãŒã«ãªããåäœããããšã確èªããŸãã
```html
<script setup lang="ts">
import Header1 from "../components/Header1.vue";
const message = ref("hello");
const displayMessage = computed(() => `${message.value}!!!`);
</script>
<template>
<Header1 :text="displayMessage" />
</template>
```
> [!question]- `ref`ã`computed`ããšã©ãŒã«ãªãå Žåã¯?
> `.nuxt/imports.d.ts`ãäœæãããŠããªãå¯èœæ§ããããŸãããã `.nuxt` ãæ瀺çã«åé€ããå Žå㯠`bun dev` ã§ããäžåºŠäœãçŽããŸãããã
## æªäœ¿çšimportãåé€ããã
### æªäœ¿çšimportãŽãåé¡
importæãèšèŒããããã«ããããšã§æ°ããªåé¡ãæµ®äžããŸããæªäœ¿çšimportããŽããšããŠæ®ãåé¡ã§ããããšãã°ä»¥äžã®ã³ãŒããèããŠã¿ãŸãããã
`pages/top.vue`
```html
<script setup lang="ts">
import Header1 from "../components/Header1.vue";
const message = ref("hello");
const displayMessage = computed(() => `${message.value}!!!`);
</script>
<template>
<div>hoge</div>
</template>
```
å
ã»ã©ã®ã³ãŒããããŒã¹ã« `<template>` é
äžã1ã€ã®[[divã¿ã°]]ã«çœ®ãæãããã®ã§ãããã®å€æŽã«ããã`Header1.vue`ã®importæã¯äžèŠã«ãªããŸããã
ä»åã®ã±ãŒã¹ã¯1è¡åé€ããã ããªã®ã§å€§ããæéã§ã¯ãããŸããããã ããããå¢ããŠãããšåé€ãé¢åã§ããã³ãŒããèŠæ ããæªããªããdiagnosticãæ±æããŠããŸããŸãã
### æªäœ¿çšimportã®èªååé€
äžèŠãªimportæã¯èªåã§åé€ããŠãåé¡ã«ãªã確çã¯ã»ãŒ0ã§ãããªãã°ããã¡ã€ã«ãä¿åããããšãã«èªåã§åé€ããããã«ããŸãããã[[Prettier]]ã®ãã©ã°ã€ã³ã[[Prettier Plugin Organize Imports]]ã䜿ããŸãã
<div class="link-card">
<div class="link-card-header">
<img src="https://github.githubassets.com/favicons/favicon.svg" class="link-card-site-icon"/>
<span class="link-card-site-name">GitHub</span>
</div>
<div class="link-card-body">
<div class="link-card-content">
<p class="link-card-title">GitHub - simonhaenisch/prettier-plugin-organize-imports</p>
<p class="link-card-description">Make Prettier organize your imports using the TypeScript language service API. - simonhaenisch/prettier-plu ... </p>
</div>
<img src="https://opengraph.githubassets.com/4e25e16c5155c4eb78cd70a49e9017c2493e2228e17859803c9c2e446b73d6bf/simonhaenisch/prettier-plugin-organize-imports" class="link-card-image" />
</div>
<a href="https://github.com/simonhaenisch/prettier-plugin-organize-imports"></a>
</div>
ãŸãã¯ã€ã³ã¹ããŒã«ããŸãã
```console
bun add -D prettier-plugin-organize-imports
```
èšå®ãã¡ã€ã« `.prettierrc.json` ãè¿œå ããŸãã
```json
{
"plugins": ["prettier-plugin-organize-imports"]
}
```
ãã¡ã€ã«ãä¿åããŠimportãåé€ãããããšã確èªããŸãã
![[2024-07-28_19h17_09.webm|frame]]
*`:w`ã§ãã¡ã€ã«ä¿ååŸã«äžèŠimportãåé€ããã*
> [!hint]
> [[Prettier Plugin Organize Imports]]ã¯æªäœ¿çšimportåé€ã®ä»ãimportæã®ãœãŒããããŒãžãªã©ã®æ©èœãæã£ãŠããŸãã
### scriptã¿ã°ã§å©çšãããªãã¢ããæ¶ãããŠããŸãåé¡
å
ã»ã©ã®æé ã§ã¯1ã€å¯Ÿå¿ã§ããªãã±ãŒã¹ããããŸãã以äžã®ãã㪠`utils/strings.ts` ããã£ããšããŸãã
```ts
export function join2str(a: string, b: string): string {
return a + b;
}
```
ãã®`join2str`ãå©çšããŠãã`pages/top.vue`ãèããŸãã
```html
<script setup lang="ts">
import { join2str } from "~/utils/strings";
</script>
<template>
<div v-text="join2str('hoge', 'hoge')"></div>
</template>
```
ãã®ã³ãŒãã¯äžèŠåé¡ãªãèŠããŸãã`join2str`ã¯å©çšãããŠãããããimportæãæ®ãã ãã...ãšãã§ã¯å®éã®æåãèŠãŠãŸãããã
![[2024-07-28_19h29_58.webm|frame]]
*䜿çšãããŠããé¢æ°ãèªååé€ãããŠããŸã*
Oh... ãªããšããããšã§ããããäžåºŠ`script`ã¿ã°ã§å¥ã®å€æ°ã«ä»£å
¥ããã°ãã®äºè±¡ã¯åé¿ã§ããŸã... ãããããããªã!! ãšããå®å¿ãã ãããã¡ãããšãã解決çããããŸãã[[vue-tsc]]ãã€ã³ã¹ããŒã«ããŸãã
```console
bun add -D vue-tsc
```
ããã§å
ã»ã©ã®ã±ãŒã¹ã確èªããŠã¿ããšã`join2str`ã¯åé€ãããªããªã£ãŠããã¯ãã§ãã
> [!info]
> 詳现㯠[[ðPrettier Plugin Organize Importsã§vueãã¡ã€ã«ã®templateã¿ã°å
ã§çŽæ¥importããé¢æ°ãå€æ°ã䜿ããšèªååé€ã®å¯Ÿè±¡ã«ãªã£ãŠããŸã]] ãåç
§ã
## æªå®çŸ©ã¿ã°åé¡ã«ã³ãŒãã£ã³ã°æ®µéã§æ°ã¥ããã
### æªå®çŸ©ã¿ã°åé¡
importæã®ç®¡çã楜ã
ã«ãªã£ãŠææ°æã
ãšããŠããç¢å
ãæ°ããªåé¡ãç«ã¡ã¯ã ãããŸããæªå®çŸ©ã¿ã°åé¡ã§ããããã¯ã**vueãã¡ã€ã«ã§å®çŸ©ãããŠããªã[[HTMLã¿ã°]]ãtypoãªã©ã®çç±ã§èšè¿°ããŠããŸã£ãå Žåã==å®è¡æãŸã§==æ€ç¥ãã§ããªã**ãšãããã®ã§ãã**ããããå®è¡æã¯warningãåºãã ããªã®ã§ãææªproductionç°å¢ãŸã§æµžéããŠããŸããªã¹ã¯ããããŸã**ããªããŠã¹ãªãªã³ã°...ã
äŸã«ãã£ãŠ `pages/top.vue` ã§èããŠã¿ãŸãããã
```html
<script setup lang="ts">
import Header1 from "~/components/Header1.vue";
</script>
<template>
<Header1 text="hoge" />
</template>
```
ããã¯åé¡ã®ãªãã³ãŒãã§ãããããã`Header`ãš`Head`ãééããŠããŸã£ããã©ããªãã§ããããã
![[Pasted image 20240728194547.png|frame]]
*`Header1`ã`Head1`ã«typo*
æ®å¿µãªãããšã©ãŒãèŠåã¯åºãŸãããäžèŠimportãåé€ãããŠãŸãããä»åã®ã±ãŒã¹ãªãããæ°ã¥ãããã§ãããå®éã®ãããžã§ã¯ãã§ã¯äœ¿çšããã³ã³ããŒãã³ãæ°ã¯ããªãå€ããªããã人ã®ç®ã§æ°ã¥ãã®ã¯å°é£ã§ãããã
ãããŠã`http://localhost:3000/top`ã«ã¢ã¯ã»ã¹ãããšããããããwarningã衚瀺ãããŸããç»é¢ã§ã¯ãªãã³ã³ãœãŒã«ãã°ã«ã
```warning
WARN [Vue warn]: Failed to resolve component: Head1
If this is a native custom element, make sure to exclude it from component resolution via compilerOptions.isCustomElement.
WARN [Vue warn]: Component <Anonymous> is missing template or render function.
```
### æªå®çŸ©ã¿ã°ããšã©ãŒãšããŠè¡šç€ºãã
[[tsconfig.json]]ã«[[vueCompilerOptions.strictTemplates]]ãè¿œå ããŸãã
```json
{
// https://nuxt.com/docs/guide/concepts/typescript
"extends": "./.nuxt/tsconfig.json",
// ãããè¿œå
"vueCompilerOptions": {
"strictTemplates": true
}
}
```
ãã¡ãã¯[[Volar]]ã®issueã§ææ¡ãããŠããŸããã[[TypeScript]]ã®ä»çµã¿ã§æ€ç¥ããããšããããšã§ããã
<div class="link-card">
<div class="link-card-header">
<img src="https://github.githubassets.com/favicons/favicon.svg" class="link-card-site-icon"/>
<span class="link-card-site-name">GitHub</span>
</div>
<div class="link-card-body">
<div class="link-card-content">
<p class="link-card-title">Unknown HTML elements or components are allowed in templates · Issue #2291 · vuejs/language-tools</p>
<p class="link-card-description">Using the "strictTemplates" option, I would have exp ... </p>
</div>
<img src="https://opengraph.githubassets.com/f2e441dd85efb7bd1512a99a5a8e88018c062d944281d81583a5bb2924080f2c/vuejs/language-tools/issues/2291" class="link-card-image" />
</div>
<a href="https://github.com/vuejs/language-tools/issues/2291#issuecomment-1376263263"></a>
</div>
æ©éè¿œå ããŠã¿ããš...ã
![[Pasted image 20240728195457.png]]
æªå®çŸ©ã®`Head1`ããšã©ãŒã«ãªããŸããïŒ
ã¡ãªã¿ã«ããã®[[vueCompilerOptions.strictTemplates]]ã¯æªå®çŸ©ã¿ã°ã ãã§ãªããæªå®çŸ©ã®[[å±æ§å]]ããšã©ãŒã«ããŸãã
![[Pasted image 20240728195901.png]]
ãããåå®å
šã§ããã«ã¯è¶ããããšããªãã®ã§LGTMã§ããç§ãããæã£ãŠããŸããããã®æã¯ãŸã ã
## ãã©ãŒã«ã¹ã«ãŒå±æ§ã䜿ããã
### ãã©ãŒã«ã¹ã«ãŒå±æ§ãšã¯
[[ãã©ãŒã«ã¹ã«ãŒå±æ§ (Vue)|ãã©ãŒã«ã¹ã«ãŒå±æ§]]ãšã¯ã[[Vue]]ã§ã³ã³ããŒãã³ãã«æå®ããã[[å±æ§ (HTML)|å±æ§]]ãã³ã³ããŒãã³ãã«**æ瀺çã«**宣èšãããŠããªãã£ãå Žåãã³ã³ããŒãã³ãã®ã«ãŒãå±æ§ã«èªåã§è¿œå ãããä»çµã¿ã®ããšã§ãã
> [!hint]
> ã«ãŒãå±æ§ã«è¿œå ãããã®ã¯åäžã«ãŒãå±æ§ã§ããå Žåã®ã¿ã§ããè€æ°ã«ãŒãå±æ§ã ã£ãããèšå®å€ã§æå®ãããå Žåã¯ãã®éãã§ã¯ãããŸããã詳现㯠[Vue.js](https://vuejs.org/guide/components/attrs.html#fallthrough-attributes) ãåç
§ã
ããšãã°ã`components/LargeButton.vue`ãèããŠã¿ãŸãã
```html
<template>
<button style="font-size: 200%">
<slot />
</button>
</template>
```
`LargeButton`ã¯ãã©ã³ããµã€ãºã2åã«ããã ãã§ãããšã¯éåžžã®ãã¿ã³ãšåãããã«æ¯ãèããã¿ã³ã§ãã
```html
<script setup lang="ts">
import LargeButton from "~/components/LargeButton.vue";
</script>
<template>
<LargeButton>ã§ãããã¿ã³</LargeButton>
</template>
```
![[Pasted image 20240728211455.png]]
`LargeButton`ã«ã¯[[props (Vue)|props]]ãå®çŸ©ãããŠããŸããããããã[[ãã©ãŒã«ã¹ã«ãŒå±æ§ (Vue)|ãã©ãŒã«ã¹ã«ãŒå±æ§]]ãæå¹ã§ããã°ãåãæž¡ããæªå®çŸ©ã®[[å±æ§ (HTML)|å±æ§]]ãã³ã³ããŒãã³ãrootã«ä»äž(ãã©ãŒã«ããã¯)ã§ããŸããå
·äœçã«ã¯
```html
<template>
<LargeButton disabled>ã§ãããã¿ã³</LargeButton>
</template>
```
ãšããã°ã`components/LargeButton.vue`ã®templateã¯
```html
<button style="font-size: 200%" disabled>
<slot />
</button>
```
ãšãªãã€ã¡ãŒãžã§ãã
![[Pasted image 20240728213545.png]]
äžèšã®ãããªèãã©ãããŒã³ã³ããŒãã³ããäœæãããã³ã«ããã¹ãŠã®èŠçŽ ãã€ãã³ããç»é²ããã®ã¯éå¹çãªã®ã§ãããããæã«äŸ¿å©ãªæ¹æ³ã ãšæããŸãã`style`ã`class`ã¯æž¡ããããã®ãããŒãžãããæåã«ãªã£ãŠãããããããããçšéã«ã䟿å©ã§ããã
### ãã©ãŒã«ã¹ã«ãŒå±æ§ããšã©ãŒã«ãªã
å®ã¯å
ã»ã©èšå®ãã[[vueCompilerOptions.strictTemplates]]ãããã®[[ãã©ãŒã«ã¹ã«ãŒå±æ§ (Vue)|ãã©ãŒã«ã¹ã«ãŒå±æ§]]ã«å¯ŸããŠå€§ããªåé¡ãçã¿ãŸããå
ã»ã©ã®ã³ãŒã... å®ã¯ãšã©ãŒã«ãªãã®ã§ãã
![[Pasted image 20240728213658.png]]
```error
Object literal may only specify known properties, and 'disabled' does not exist in type 'Partial<{}> & Omit<{} & VNodeProps & AllowedComponentProps & ComponentCustomProps & Readonly<ExtractPropTypes<{}>>, never>'. ts (2353) [6, 16]
```
[[ãã©ãŒã«ã¹ã«ãŒå±æ§ (Vue)|ãã©ãŒã«ã¹ã«ãŒå±æ§]]ã®ãµããŒãããããŠããªããã§ãã
### ãã©ãŒã«ã¹ã«ãŒå±æ§ã蚱容ããæ¹æ³
ãã¹ãã¯å®è£
ã解æããŠèš±å®¹ã§ãã[[ãã©ãŒã«ã¹ã«ãŒå±æ§ (Vue)|ãã©ãŒã«ã¹ã«ãŒå±æ§]]ãå€å®ããŠã»ãã... ã§ããããããŸã§ã¯æã¿ãŸãããæäœéã[[ãã©ãŒã«ã¹ã«ãŒå±æ§ (Vue)|ãã©ãŒã«ã¹ã«ãŒå±æ§]]ããšã©ãŒã«ãªããªããã°OKãšããŸãã
察çã¯äœéããæ¹æ³ã¯ãããŸãããäžçªè¯ããšæããã®ã¯ä»¥äžIssueã®æ¹æ³ã§ããã
<div class="link-card">
<div class="link-card-header">
<img src="https://github.githubassets.com/favicons/favicon.svg" class="link-card-site-icon"/>
<span class="link-card-site-name">GitHub</span>
</div>
<div class="link-card-body">
<div class="link-card-content">
<p class="link-card-title">Detect unknown/undefined prop usage · Issue #1077 · vuejs/language-tools</p>
<p class="link-card-description">This is a proposal for enhancing vue-tsc Example: <MyComponent :propThatDoesNotExist="42" /> When runn ... </p>
</div>
<img src="https://opengraph.githubassets.com/c8afbbd12dffd8f9438d9b605f47ce712e4809dee79f3ecfc6a925b42eced027/vuejs/language-tools/issues/1077" class="link-card-image" />
</div>
<a href="https://github.com/vuejs/language-tools/issues/1077"></a>
</div>
`allow-fallthrough-props.d.ts`ãšããŠäœæããŸãã
```ts
import "@vue/runtime-core";
import "@vue/runtime-dom";
// for vue components
declare module "@vue/runtime-core" {
export interface AllowedComponentProps {
[key: string]: any;
}
}
// for native html elements
declare module "@vue/runtime-dom" {
export interface HTMLAttributes {
// allow any data-* attr
[key: `data-${string}`]: string;
}
}
export {};
```
ãã®å®çŸ©ããããšã[[Vue]]ã³ã³ããŒãã³ãå
šäœã«`AllowedComponentProps`ã®ã€ã³ã¿ãŒãã§ãŒã¹ãé©å¿ããããããã©ã®ãããª[[å±æ§ (HTML)|å±æ§]]ã§ãåãå
¥ããããããã«ãªããŸãã
äžæ¹ããã®åé¿æ¹æ³ã«ã¯å€§ããªãã¡ãªããããããŸãã**[[å±æ§ (HTML)|å±æ§]]ã®è£å®ãåºãªããªããŸãã**
![[Pasted image 20240728225934.png]]
è£å®ãåºãªããªããšããããšã¯ãå®å
šã§ãå¿«é©ã§ããªããªã£ãŠããŸããŸãããŸãããããã¯[[ãã©ãŒã«ã¹ã«ãŒå±æ§ (Vue)|ãã©ãŒã«ã¹ã«ãŒå±æ§]]ã ãã§ã¯ãããŸããããã©ãŒã«ã¹ã«ãŒã§ã¯ãªã[[props (Vue)|props]]ãšããŠå®çŸ©ããã[[å±æ§ (HTML)|å±æ§]]ãè£å®ã«åºãªããªããŸããããã«ã**å¿
é ãª[[å±æ§ (HTML)|å±æ§]]ãæå®ããªãã£ãå Žåã§ããšã©ãŒã«ãªããªã**ãšãããªãã±ã€ãã§ãã
![[Pasted image 20240729071240.png]]
### è£å®ãšå¿
é å±æ§ã衚瀺ãããããã«ãã
è£å®ãšãšã©ãŒã衚瀺ãããªããªã£ãçç±ã¯ `allow-fallthrough-props.d.ts` ã®`AllowedComponentProps` ã«ãããŸãã
```ts
// for vue components
declare module "@vue/runtime-core" {
export interface AllowedComponentProps {
[key: string]: any;
}
}
```
[[Vue]]ã³ã³ããŒãã³ãã«anyåãå€ã«ãšã[[ã€ã³ããã¯ã¹å]]ãèªããŠããŸã£ããããã©ã®ãããªå±æ§å€ãæå®ããŠããã®å®çŸ©ãåžåããŠããŸãã®ã§ããè£å®ã«ã€ããŠã¯æå®ããã³ã³ããŒãã³ãã®[[props (Vue)|props]]ãåªå
ãããŠãè¯ããããªæ°ãããŸãããããã¯ãªããªãããã§ããã
ããã§äœæŠãå€ããŸãã**[[ãã©ãŒã«ã¹ã«ãŒå±æ§ (Vue)|ãã©ãŒã«ã¹ã«ãŒå±æ§]]ãšããŠæå®ããããšã®ããå±æ§ã`AllowedComponentProps`ã«`unknownå`ãšããŠè¿œå **ããŸãã
```ts
// for vue components
declare module "@vue/runtime-core" {
export interface AllowedComponentProps {
// inputã«ãã©ãŒã«ã¹ã«ãŒå±æ§ãšããŠå©çš
type?: unknown;
// LargeInput以å€ã®ç®æã§ãã©ãŒã«ã¹ã«ãŒå±æ§ãšããŠå©çšãšä»®å®
placeholder?: unknown;
}
}
```
ãã®ç¶æ
ã§ä»¥äžã® `LargeInput.vue` ãèããŸãã
```html
<script setup lang="ts">
type PlaceHolder = { value: string };
defineProps<{
requiredArg: number;
placeholder?: PlaceHolder;
}>();
</script>
<template>
<input style="font-size: 200%" :placeholder="`ex: ${placeholder?.value}`" />
</template>
```
3ã€ã®ãã¿ãŒã³ã«ã€ããŠæåã確èªããŸãã
#### å¿
é ã®å±æ§ãæå®ããªãã£ããšã
å¿
é ã®[[å±æ§ (HTML)|å±æ§]]ã§ãã`requiredArg`ãæå®ããªãã£ããšãã¯ãšã©ãŒã«ãªããŸãã
![[Pasted image 20240729072732.png]]
ããã§å¿
é å±æ§ã®æå®å¿ãã«ããäžåã¯æ©æã«æ€ç¥ã§ããããã«ãªããŸãããäžå®å¿ã§ããããã¡ãã`required-arg`ã¯è£å®ã衚瀺ãããåééããæ€ç¥ã§ããŸãã
#### ä»»æã®å±æ§ãå¥å®çŸ©ã®ãã©ãŒã«ã¹ã«ãŒå±æ§ãšè¢«ã£ããšã
`LargeInput.vue`ã®[[props (Vue)|props]]ã§ãã`placeholder`ããæ¢ã«[[ãã©ãŒã«ã¹ã«ãŒå±æ§ (Vue)|ãã©ãŒã«ã¹ã«ãŒå±æ§]]ãšããŠå©çšãããŠããã`AllowedComponentProps`ã«ãå®çŸ©ãããŠããå Žåã§ããæ°ã«ãªããã€ã³ãã¯ä»¥äž3ç¹ã
- è£å®ãåºãã
- åãšã©ãŒãæ€ç¥ã§ããã
- ã³ãŒããžã£ã³ãã§ããã
ãŸããè£å®ã¯åºãŸãã
![[Pasted image 20240729073307.png]]
åãšã©ãŒã衚瀺ãããŸãã
![[Pasted image 20240729073403.png]]
åã[[unknownå]]ã§ã¯ãªã`PlaceHolder`åãåªå
衚瀺ãããŸãã
![[Pasted image 20240729073533.png]]
ã³ãŒããžã£ã³ãã¯`LargeInput.vue`ã«çŽæ¥ç§»å...ãšã¯ãªããŸããã§ããããåè£ã«ã¯æãã£ãŠããã®ã§èš±å®¹ç¯å²ã ãšæããŸãã
![[2024-07-29_07h39_36.avif]]
ãŸãã`AllowedComponentProps`ã§`placeholder`ã[[unknownå]]ã§ã¯ãªãå
·äœçãªåã«ããå Žåã[[亀差å (TypeScript)|亀差å]]ãšå€å®ãããŠãšã©ãŒã«ãªãã®ã§æ³šæããŠãã ãããããšãã°ã`placeholder?: string`ãšããå Žåã¯ä»¥äžã®ããã«ãªããŸãã
![[Pasted image 20240729074311.png]]
#### æªå®çŸ©ã®å±æ§ãæå®ãããšã
æªå®çŸ©å±æ§ã¯[[vueCompilerOptions.strictTemplates]]ã®ä»æ§éããšã©ãŒã«ãªããŸãã
![[Pasted image 20240729074659.png]]
[[Vue]]ã³ã³ããŒãã³ãã®[[ãã©ãŒã«ã¹ã«ãŒå±æ§ (Vue)|ãã©ãŒã«ã¹ã«ãŒå±æ§]]ãšããŠå©çšãããå Žåã¯å¿
ã`AllowedComponentProps`ãžã®è¿œå ãå¿
èŠãªããã§ãã
> [!hint] ã€ãã³ãã®ãã©ãŒã«ã¹ã«ãŒå±æ§
> ã€ãã³ãã®[[ãã©ãŒã«ã¹ã«ãŒå±æ§ (Vue)|ãã©ãŒã«ã¹ã«ãŒå±æ§]]ã`AllowedComponentProps`ãžã®è¿œå ãå¿
èŠã§ãããé¢æ°åã«æ³šæããŠãã ããã`@change`ã®å Žåã¯`onChange`ãšãã£ãããã«å€æãå¿
èŠã§ãã
>
> ```ts
> declare module "@vue/runtime-core" {
> export interface AllowedComponentProps {
> // @changeã®ãã©ãŒã«ã¹ã«ãŒå±æ§
> onChange?: unknown;
> }
> }
> ```
## ãŸãšã
[[Neovim]]ã«ãŠãã§ããã ã**åå®å
š**ãã€**å¿«é©**ãª[[Nuxt3]]ã®éçºãã§ããããã«ããæ¹æ³ã玹ä»ããŸãããå®çŸããããšã¯ä»¥äž2ç¹ããã®éã«çããå¯äœçšã軜æžããæ¹æ³ã䜵ããŠçŽ¹ä»ããŸããã
- **Auto-importsãç¡å¹åããã**
- æªäœ¿çšimportãåé€ããã
- **æªå®çŸ©ã¿ã°åé¡ã«ã³ãŒãã£ã³ã°æ®µéã§æ°ã¥ããã**
- ãã©ãŒã«ã¹ã«ãŒå±æ§ã䜿ããã
[[Vue]]ã®ä»æ§ãé£è§£ãªããšãããã[[Volar]]ã§ãã¹ãŠãçæ³çã«ãã³ããªã³ã°ããã®ã¯å°é£ã ãšæããŸãããããã[[Vue]]ã«å¯ŸããŠããã ããŸãšãã«éçºã§ããç°å¢ãæäŸãç¶ããŠãã ãã£ãŠããã¡ã³ããã®æ¹ã
ã«ã¯æè¬ãããããŸããã
ãã®ãããªå¶çŽã®äžãã§ããã ãå¿«é©ããæãªããã«å®å¿ã»å®å
šãªéçºãã§ãããããªè©Šè¡é¯èª€ãéããŠããŸãããå°ã人ãéžã¶å
容ãããããŸãããã1人ã§ãå€ãã®åããããªããšã§ãå°ãã®æ¹ãžå±ãã°å¬ããæããŸãã
æåŸã«ãæ¬èšäºã§èšå®ããå
容ã®ãŸãšããèšèŒãçµäºãšãããŠããã ããŸãã
`ã€ã³ã¹ããŒã«`
```console
bun add -D prettier-plugin-organize-imports vue-tsc
```
`nuxt.config.ts`
```ts
export default defineNuxtConfig({
// ã€ã³ããŒãçš(Auto-importså«ã)ã®åå®çŸ©ã«è¿œå ããããã¹ãã£ã³ããªã
imports: { scan: false },
// ã³ã³ããŒãã³ããšããŠæ±ããã£ã¬ã¯ããªãæå®ããªã
components: { dirs: [] }
})
```
`.prettierrc.json`
```json
{
"plugins": ["prettier-plugin-organize-imports"]
}
```
`tsconfig.json`
```ts
{
// https://nuxt.com/docs/guide/concepts/typescript
"extends": "./.nuxt/tsconfig.json",
// ãããè¿œå
"vueCompilerOptions": {
"strictTemplates": true
}
}
```
`allow-fallthrough-props.d.ts`
```ts
import "@vue/runtime-core";
import "@vue/runtime-dom";
// for vue components
declare module "@vue/runtime-core" {
export interface AllowedComponentProps {
// inputã«ãã©ãŒã«ã¹ã«ãŒå±æ§ãšããŠå©çš
type?: unknown;
// LargeInput以å€ã®ç®æã§ãã©ãŒã«ã¹ã«ãŒå±æ§ãšããŠå©çšãšä»®å®
placeholder?: unknown;
// @changeã®ãã©ãŒã«ã¹ã«ãŒå±æ§
onChange?: unknown;
// TODO: ãã©ãŒã«ã¹ã«ãŒå±æ§ãå¢ãããè¿œå ããŠãã
}
}
// for native html elements
declare module "@vue/runtime-dom" {
export interface HTMLAttributes {
// allow any data-* attr
[key: `data-${string}`]: string;
}
}
export {};
```