svelte icon indicating copy to clipboard operation
svelte copied to clipboard

Svelte5 JSDoc for `$props`

Open dreitzner opened this issue 1 year ago • 1 comments

Describe the problem

When declaring props with TS, we can add JSDoc comments and type annotations:

interface Props {
	/**
	 * This is an important optional count
	*/
	count?: number;
}

let { 
	count = 0
} = $props<Props>();

This will show all the info also in intellisense when the component is used: image

But when not using TS, I don't think there is a way to pass that extra info to the LSP.

Describe the proposed solution

It might be nice to add the possibility to augment the destructured props like this:

let { 
	/**
	 * This is an important optional count
	 * @type {number}
	*/
	count
} = $props();

There might be already a working solution out there, that I'm not aware of....

In that case I'd be happy to add it to the docs :)

Importance

nice to have

dreitzner avatar Feb 19 '24 15:02 dreitzner

You can use typedef for this:

/**
 * @typedef Props
 * @property {number} count Some documentation
 */

/** @type {Props} */
let { count } = $props();

Giving this the documentation label to note this wherever in the final new docs we're going to talk about typing stuff.

dummdidumm avatar Feb 19 '24 16:02 dummdidumm

@dummdidumm this does not seem to work at the moment.

If I do a typedef inisde of a normal <script /> block I get the following error:

image

dreitzner avatar Feb 20 '24 07:02 dreitzner

@dummdidumm this does not seem to work at the moment.

If I do a typedef inisde of a normal <script /> block I get the following error:

@dreitzner It does work:

Screenshot 2024-02-20 at 20 18 46

Nonetheless the error occurs in different IDE's and is definitely a bug. There are more options though:

  1. Define the type inline with an object literal:
<script>
  /** @type {{ count: number }} */
  let { count } = $props();
</script>

2.a. Define the type in a separate definition file:

// Counter.d.ts

interface CounterProps {
  count: number;
}
// Counter.svelte
<script>
  /** @type {CounterProps} */
  let { count } = $props();
</script>

2.b. Though in most cases it would probably be more useful to make the definition file a module:

// Counter.d.ts

export interface CounterProps {
  count: number;
}
// Counter.svelte

<script>
  /** @type {import('./Counter').CounterProps} */
  let { count } = $props();
</script>
  1. If you really wanted to you could also use <script lang="ts" context="module"> (though it seems the LSP implicitly "adds" lang="ts" to the second script, which in my opinion is a bug):
<script lang="ts" context="module">
  export interface CounterProps {
    count: number;
  }
</script>

<script>
  /** @type {CounterProps} */
  let { count } = $props();
</script>

sheijne avatar Feb 20 '24 19:02 sheijne

The code I gave is correct, there's a bug in the VS Code Svelte extension which I fixed in https://github.com/sveltejs/language-tools/pull/2294, release pending.

dummdidumm avatar Feb 21 '24 09:02 dummdidumm

@dummdidumm thank you so much for the explenation and the fix :)

dreitzner avatar Feb 21 '24 10:02 dreitzner

The @typedef method works for me. I tried using @param, but was not successful:

<script>
  /**
   * @param {Object} Props
   * @param {string} Props.name
   */
  let { name, age, date } = $props()
</script>

<p>Your {name}</p>
<!--     ^ type is `unknown` -->

Can we allow this method of documentation and typing? It could save a couple of lines.

It seems to work with plain JS: playground.

theetrain avatar Apr 26 '24 18:04 theetrain

That's invalid syntax, the types just come from inference based on the assigned value.

You can do something like this:

/**
 * @type {{
 *  name: string,
 *  age: number,
 *  ...
 * }}
 */
let { name, age, date } = $props()

brunnerh avatar Apr 26 '24 19:04 brunnerh

Thanks for educating me; I don't know why I thought @param could be used for anything except functions. I suppose @typedef + @property are ideal for documentation, and @type for just types.

I was hoping @typedef can be applied in one step, but that's not possible. All good.

theetrain avatar Apr 26 '24 21:04 theetrain