网站搜索

typescript - Svelte 强类型组件类


我正在开发一个组件库,该库在组件分组方式上有一些不同。我将组件分为不同的类,以便最终用户使用这个库时更简单。他们不需要记住可用的命名 slots,而是可以使用点表示法访问自定义 slot。例如,一个 Card 组件可以有 Header、Body 和 Footer。在我的库中,我希望这样使用这些组件:

<Card>
  <Card.Header>I am a Header</Card.Header>
  <Card.Body>I am a Body</Card.Body>
  <Card.Footer>I am a Footer</Card.Footer>
</Card>

我目前这个可以工作,但我也在尝试让它具有强类型,这就是我遇到麻烦的地方。以下是我的文件结构:

card
  | Card.svelte
  | Body.svelte
  | Header.svelte
  | Footer.svelte
  | index.ts

我的 index.ts 文件包含以下代码:

import OriginalCard from './Card.svelte';
import Header from './Header.svelte';
import Footer from './Footer.svelte';
import Body from './Body.svelte';

const Card = OriginalCard;
Card.Header = Header;
Card.Body = Body;
Card.Footer = Footer;

export default Card;

页眉、正文和页脚都会返回以下错误:属性 'Header' 在类型 'typeof default' 上不存在。ts(2339)

像这样使用组件时:

<script lang="ts>
  import Card from '@components/card';
</script>

<Card>
  <Card.Header>I am a Header</Card.Header>
  <Card.Body>I am a Body</Card.Body>
  <Card.Footer>I am a Footer</Card.Footer>
</Card>

Header、Body 和 Footer 都返回以下错误:Property 'Body' does not exist on type 'typeof Card__SvelteComponent_'. ts(2339)

我该如何修复这些错误?

编辑

在另一个项目中安装此软件包后,我注意到两个已接受的答案没有解决的问题:

1). <Card /> 返回以下 ts 错误 - 预期 0 个参数,但得到 1 个。这会导致所有允许属性的智能提示失效。

2). Intellisense 不适用于点符号。当输入 <Card. 时,我没有得到任何子组件的提示,而是显示整个包中的所有组件。

期望的结果是不出现 ts 错误,并且在使用该包时不丢失智能感知功能。

我也尝试过像这样扩展 CardStatic:interface CardStatic extends OriginalCard,但同样的问题仍然存在。

答案#1

你必须扩展已知类型以添加额外的属性。由于它们是静态的,我会尝试如下方法:

const Card = OriginalCard as CardStatic;
Card.Header = Header;
Card.Body = Body;
Card.Footer = Footer;

export default Card;

export interface CardStatic {
    new(...args: ConstructorParameters<typeof OriginalCard>): OriginalCard;
    Header: typeof Header;
    Body: typeof Body;
    Footer: typeof Footer;
}

答案#2

我在 SvelteKit 中用于创建组件库时,发现了一个更简单的方法:在 lib 文件夹的 index.ts 文件中这样导出一个名为 Card 的对象:

import _Card from "./Card.svelte";
import _CardBody from "./CardBody.svelte";
import _CardHeader from "./CardHeader.svelte";
import _CardFooter from "./CardFooter.svelte";

export const Card = {
    Root: _Card,
    Footer: _CardFooter,
    Header: _CardHeader,
    Body: _CardBody
};

使用这个你不需要额外的类型定义,但有一个问题:<Card> 组件现在变成了 <Card.Root>。它可以像下面这样从其他项目中导入和使用:

<script lang="ts">
    import { Card } from 'the-component-library';
</script>

<Card.Root>
    <Card.Header>My Header</Card.Header>
    <Card.Footer>My Footer</Card.Footer>
</Card.Root>

因为有这个陷阱,我会说这不能算作一个答案,但我仍然自愿提供,以防对于那些不介意去执行已被采纳答案所提供的打字部分的人来说,这个陷阱只是小事一桩。