18

Is it possible to import a Svelte component in a Typescript file and have Rollup successfully compile it?

The following code works as a Javascript file, but errors when converted to Typescript, because the TS compiler doesn’t know how to handle a .svelte file:

import Component from './Component.svelte';

const foo = () => new Component({ target: document.body });

Is there a combination of rollup-plugin-svelte and @rollup/plugin-typescript that will preprocess the Svelte component in such a way that the Typescript compiler can include the Svelte code?


In case some more context is helpful, boardgame.io includes an in-browser debugging component built with Svelte, which is bundled in both a plain JS client & in a React client component, and we’re trying to update our Rollup config for exactly this scenario.

swithinbank
  • 1,127
  • 1
  • 6
  • 15
  • The typescript compiler need to know about `./Component.svelte` module. You need to define it. `Rollup` is a bundler, and it needs to know how to handle modules as well, which is the role of rollup's plugin. – HTN Jul 06 '20 at 20:36
  • 1
    Full Typescript support for Svelte is likely to be days away - take a look at this Twitter thread which is pretty close to confirmation https://twitter.com/sveltejs/status/1277235019845644288?s=20 – Daniel Elkington Jul 07 '20 at 00:15

2 Answers2

22

Just to expand on Rich's answer, I was curious to investigate why importing Svelte helped in some setups.

the TS compiler doesn’t know how to handle a .svelte file

Svelte solves this (at least in the version at the time of writing, 3.35.0) by distributing a file in svelte/types/runtime/ambient.d.ts:

declare module '*.svelte' {
    export { SvelteComponentDev as default } from 'svelte/internal';
}

This effectively makes the TS compiler analyse .svelte files. Out of academic interest, it also declares the typings for all the runtime functionality that a script in a .svelte file has access to, such as set_attributes() (see svelte/internal for more). So just writing declare module '*.svelte' {} would only get you part of the way, as you'd also need those runtime declarations.

So, to get the TypeScript compiler to handle .svelte files, you need to – one way or another – reference the types of that file, svelte/types/runtime/ambient.d.ts. It's actually referenced indirectly by the package's entrypoint typings file (the value specified in the types field of Svelte's package.json), types/runtime/index.d.ts, so equally, you could reference that entrypoint typings file. Doing so would be best practice, as it would be robust against future refactors in the directory structure.

Extending @tsconfig/svelte/tsconfig.json satisfies this because the tsconfig file in question references the "svelte" node module through its compilerOptions.types property:

{
  // ... Omitted...

  "compilerOptions": {
    // ... Omitted...

    "types": ["svelte"]
  }
}

This effectively means that the compilation context will include this reference:

/// <reference types="svelte" />

You could equally write that same line yourself, though you might as well extend the Svelte tsconfig, as it would be robust to future Svelte evolution.

The suggestion of importing for side-effects works for the same reason:

import "svelte";

I found that even though I was extending the Svelte tsconfig, the Svelte types weren't being referenced. This turned out to be because I'd ended up with this line in my tsconfig:

{
  "extends": "@tsconfig/svelte/tsconfig.json",
  
  // ... Omitted...

  "compilerOptions": {
    // ... Omitted...

    "types": ["node"] // This overrides ["svelte"]!
  }
}

I fixed it by replacing "types": ["node"] with "types": ["node", "svelte"].

Jamie Birch
  • 5,839
  • 1
  • 46
  • 60
6

Try adding @tsconfig/svelte to your project, then updating your tsconfig.json file:

{
  "extends": "@tsconfig/svelte/tsconfig.json",
  "include": ["src/**/*"],
  "exclude": ["node_modules/*", "__sapper__/*", "public/*"],
}
Rich Harris
  • 28,091
  • 3
  • 84
  • 99
  • Thanks! This seems to make `tsc` happy, but for a library consumer using Typescript, no declaration file is output for the Svelte entrypoint and they would see a “TS2307: Cannot find module `./Component.svelte` or its corresponding type declarations” error when requiring a module that imports the component. Would any Typescript consumer also need tsconfig to extend `@tsconfig/svelte/tsconfig.json` or add `"types": ["svelte", ...]`? Or is there a way to output a type declaration for the component? – swithinbank Jul 09 '20 at 15:10
  • Not sure — if it's a library that consumes a component, it sounds like maybe you need to remove `node_modules` from the `exclude` array? – Rich Harris Jul 09 '20 at 18:57
  • 7
    Not sure if this is the right approach, but I got it working by adding `import 'svelte'` to our main type definitions. Svelte has to be a dependency (instead of a dev dependency) to ensure it’s installed for consumers, but this seems to make the ambient `*.svelte` module declaration visible for consumers too. – swithinbank Jul 10 '20 at 10:32
  • Ah, in fact, doing that means we also don’t need to tell `tsc` about the Svelte types in tsconfig.json, because they’re already hoisted by the import. – swithinbank Jul 10 '20 at 11:07
  • 3
    I just added `import 'svelte'` to one of my ts files (the root one) and it worked. Thanks @delucis – Robbie Cook Jan 27 '21 at 22:47