🕌

Svelte + JSDoc で type checking

2023/06/15に公開

前置き

TypeScript 書けばいいじゃんって言われたらまぁそうなんですが、自分が依存パッケージなるべく減らしたい人間なので、プロジェクトの規模感など場合によっては JSDoc でもいいんじゃないという趣旨です。

やり方

svelte-check を使います。

  1. jsconfig.json を設置する

    {
      "compilerOptions": {
        "target": "esnext",
        "module": "esnext",
        "moduleResolution": "node",
        "checkJs": true,
        "strict": true,
        "skipLibCheck": true,
        "noImplicitAny": false,
      },
      "include": ["src/**/*"]
    }
    
    • XXX: 設定項目についてはドキュメントに特に記載されていなかったため、それっぽいものをとりあえず置いています

      • (一部の設定項目は svelte-check 内部で上書きされている?
    • noImplicitAnytrue の場合、型情報がない(jsdoc のない)変数、仮引数などが全てエラーになるので緩くやりたい場合は false にしましょう。 [1]

  2. $ npx svelte-check を実行、または以下のようにプロジェクトに追加し $ npm run check

    $ npm install --save-dev svelte-check
    

    package.json

     {
       "scripts": {
         "build": "rollup -c",
    +    "check": "svelte-check"
       }
     }
    
    

動作例

基本

<script>
  import { onMount } from "svelte";

  let message = "";

  /**
   * @param {string} name
   * @return {string}
   */
  function greeting(name) { return `Hello ${name}`; }

  onMount(() => {
    // Oops!
    setTimeout(() => { message = greeting(1) }, 500);
  });
</script>

<div>{ message } </div>

チェック

$ npx svelte-check

====================================
Loading svelte-check in workspace: /home/ykrods/work/scratch
Getting Svelte diagnostics...

/home/ykrods/work/scratch/src/App.svelte:13:43
Error: Argument of type 'number' is not assignable to parameter of type 'string'. (js)
  onMount(() => {
    setTimeout(() => { message = greeting(1) }, 500);
  });

====================================
svelte-check found 1 error and 0 warnings in 1 file

コンポーネントのプロパティ( props )

Greeting.svelte

<script>
  export let name = '';
</script>
<p>{ name }</p>

App.svelte

<script>
  import Greeting from "./Greeting.svelte";
</script>
<div>
  <!-- Error: Type 'number' is not assignable to type 'string' -->
  <Greeting name={1} />
</div>

Store

// store.js
import { writable } from "svelte/store";

export const count = writable(null);
<script>
  import { count } from "./store.js";

  function onClick() {
    // Oops!
    $count = $count + 1;
  }
</script>
<div>
  <button on:click={onClick}>{ $count }</button>
</div>

チェック

$ npx svelte-check

====================================
Loading svelte-check in workspace: /home/ykrods/work/scratch
Getting Svelte diagnostics...

/home/ykrods/work/scratch/src/App.svelte:5:14
Error: '$count' is possibly 'null'. (js)
  function onClick() {
    $count = $count + 1;

====================================
svelte-check found 1 error and 0 warnings in 1 file

ちなみに nullable な store の型はこんな感じでかけます。

/** @type { import("svelte/store").Writable<Number | null> } */
export const count = writable(null);

(別の書き方)

/**
 * @template T
 * @typedef { import("svelte/store").Writable<T> } Writable
 */

/** @type { Writable<Number | null> } */
export const count = writable(null);

余談

jsファイルだけを JSDoc でタイプチェックする場合は typescript の tsc コマンドを使います。svelte-check も内部的には typescript を使っています。

脚注
  1. 厳密にやりたかったら TypeScript を書きましょう ↩︎

Discussion