-
-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Description
Describe the bug
Maybe this is more of language tools question, or possibly just an immutable TypeScript behavior... but I'm working on a component library where a number of components inherit props from other components to avoid redeclaration.
The library project was created with the library option in npm create svelte@latest
.
I'm defining $$Props
explicitly in many of the components, and using ComponentProps
to bring forward the types I want the component to inherit. The components are written with TS, but I'm also using JSDoc style comments to give nice documentation on hover in your IDE.
This all works great in development where the uncompiled TS .svelte
files are used, but when I build the library out to .js
and .d.ts
files, type utilities like ComponentProps
are expanded and the JSDoc comments are lost.
See the reproduction section, but basically if you do something like this in your component:
interface $$Props extends ComponentProps<SomeComponent> {
/** Prop description */
additionalProp: T;
}
All JSDoc prop annotations from SomeComponent
are lost in the built type definition output, even if they're declared correctly in SomeComponent
. The comment on additionalProp
makes it through.
I've tried several permutations of tsconfig
settings, including removeComments
etc. with no joy.
My current work-around plan is to distribute the library exporting the src
files instead of the built dist
files. I suppose the other alternative would be to duplicate and redeclare all props and their JSDoc annotations manually instead of using ComponentProps
and $$Props
, or some post-build processing to try to stitch everything back together... but I really don't want to go there.
This might be tangentially related to documentation loss reported in language-tools issues #1377 and #1698.
Any help or ideas are appreciated.
Reproduction
I'll publish the full lib to GitHub in a few days even if I can't figure this out, but minimal snippets to understand the problem are below:
Excerpt of a prop declaration chain, note the JSDoc style comments.
Source files:
Binding.svelte
(excerpt, this is effectively a "base" component class):
/** Object with values to manipulate. */
export let params: T;
/** The key for the value in the params object the control should manipulate. */
export let key: string;
/** Prevent interactivity. Defaults to `false`. */
export let disabled: boolean = false;
/** Text displayed next to control. */
export let label: string | undefined = undefined;
/** Control configuration exposing TweakPane's internal [BindingParams](https://tweakpane.github.io/docs/api/types/BindingParams.html), contingent on type of bound param. TODO: Templatized types. */
export let bindingParams: object | undefined = undefined;
/** Custom color scheme. Only applies if the control component is created outside a `<Pane>` component. */
export let theme: Theme | undefined = undefined;
/** Bindable reference to internal TweakPane [BindingApi](https://tweakpane.github.io/docs/api/classes/_internal_.BindingApi.html) for this control, not generally intended for direct use. Treat as read only. */
export let bindingRef: BindingApi | undefined = undefined;
/** Imported Tweakpane `TpPluginBundle` module to register before creating the binding. Primarily for internal use. */
export let plugin: TpPluginBundle | undefined = undefined;
ValueBinding.svelte
(excerpt, "inherits" props from Binding.svelte
):
import Binding from '../core/Binding.svelte';
import type { ComponentProps } from 'svelte';
interface BindableValue extends Bindable {
[x: string]: T;
}
interface $$Props extends Omit<ComponentProps<Binding<BindableValue>>, 'key' | 'params'> {
/** Value to manipulate. */
value: T;
}
Build Output:
Binding.svelte.d.ts
(excerpt, comments survive, great!):
declare class __sveltets_Render<T extends Bindable> {
props(): {
/** Object with values to manipulate. */ params: T;
/** The key for the value in the params object the control should manipulate. */ key: string;
/** Prevent interactivity. Defaults to `false`. */ disabled?: boolean | undefined;
/** Text displayed next to control. */ label?: string | undefined;
/** Control configuration exposing TweakPane's internal [BindingParams](https://tweakpane.github.io/docs/api/types/BindingParams.html), contingent on type of bound param. TODO: Templatized types. */ bindingParams?: object | undefined;
/** Custom color scheme. Only applies if the control component is created outside a `<Pane>` component. */ theme?: Theme | undefined;
/** Bindable reference to internal TweakPane [BindingApi](https://tweakpane.github.io/docs/api/classes/_internal_.BindingApi.html) for this control, not generally intended for direct use. Treat as read only. */ bindingRef?: BindingApi<unknown, unknown, import("@tweakpane/core/dist/blade/binding/controller/binding.js").BindingController<unknown, import("@tweakpane/core").ValueController<unknown, import("@tweakpane/core").View, import("@tweakpane/core").Value<unknown>>, import("@tweakpane/core").BindingValue<unknown>>> | undefined;
/** Imported Tweakpane `TpPluginBundle` module to register before creating the binding. Primarily for internal use. */ plugin?: TpPluginBundle | undefined;
};
events(): {} & {
[evt: string]: CustomEvent<any>;
};
slots(): {};
}
ValueBinding.svelte.d.ts
(note expansion of prop definitions, and loss of JSDoc comments on inherited props):
declare class __sveltets_Render<T extends any> {
props(): Omit<{
params: Bindable & {
[x: string]: T;
};
key: string;
disabled?: boolean | undefined;
label?: string | undefined;
bindingParams?: object | undefined;
theme?: import("..").Theme | undefined;
bindingRef?: import("@tweakpane/core").BindingApi<unknown, unknown, import("@tweakpane/core/dist/blade/binding/controller/binding").BindingController<unknown, import("@tweakpane/core").ValueController<unknown, import("@tweakpane/core").View, import("@tweakpane/core").Value<unknown>>, import("@tweakpane/core").BindingValue<unknown>>> | undefined;
plugin?: import("@tweakpane/core").TpPluginBundle | undefined;
}, "key" | "params"> & {
/** Value to manipulate. */
value: T;
};
events(): {} & {
[evt: string]: CustomEvent<any>;
};
slots(): {};
}
System Info
System:
OS: macOS 14.0
CPU: (8) arm64 Apple M1
Memory: 1.85 GB / 16.00 GB
Shell: 5.9 - /bin/zsh
Binaries:
Node: 18.17.1 - ~/.nvm/versions/node/v18.17.1/bin/node
Yarn: 1.22.19 - ~/.nvm/versions/node/v18.17.1/bin/yarn
npm: 10.1.0 - ~/.nvm/versions/node/v18.17.1/bin/npm
pnpm: 8.6.1 - ~/.nvm/versions/node/v18.17.1/bin/pnpm
Browsers:
Chrome: 118.0.5993.70
Edge: 118.0.2088.46
Safari: 17.0
npmPackages:
svelte: ^4.2.1 => 4.2.1
Severity
annoyance