svelte-persisted-store
svelte-persisted-store copied to clipboard
Import Writable as a type
Import Writable as a type because it is a type-only import. If user has preserveValueImports and isolatedModules both enabled in their typescript configuration it will provide an error unless imported as a type.
Not sure I understand why TypeScript can't infer it's a type
@joshnuss TypeScript knows that Writable is a type, but other bundlers (such as Svelte, Babel, or esbuild) only know what might be a type. Usually, these tools can strip out unused variables. In this example, all bundlers can safely remove the Writable and cookie variables from imports, as they aren't used in generated JavaScript.
import { Writable, writable } from "svelte/stores"
import { cookie } from "my-cookie-library"
function createAWritableThing() {
return writable(23)
}
outputs
import { writable } from "svelte/stores"
function createAWritableThing() {
return writable(23)
}
However, if preserveValueImports is enabled, all values (things that aren't types or interfaces) are preserved in the generated JavaScript files. This is especially useful in templating libraries such as Vue or Svelte where imports are used in the template, not the JavaScript itself.
If we wrote the same example above with preserveValueImports set to true, TypeScript would know that cookie is a value and keep it in our generated code, while at the same time removing Writable, because it can look in the original file and make sure it's a type or interface, not a variable.
import { Writable, writable } from "svelte/stores"
import { cookie } from "my-cookie-library"
function createAWritableThing() {
return writable(23)
}
outputs
import { writable } from "svelte/stores"
import { cookie } from "my-cookie-library"
function createAWritableThing() {
return writable(23)
}
TypeScript also has a flag called isolatedModules. When this is enabled, the TypeScript compiler is disallowed from looking into other modules to get values and check things. Thus, it doesn't know whether Writable and cookie are values or types, so it would include both at runtime. For bundlers like esbuild or Svelte, this property is often required because these compilers can only handle one file at a time and don't have the same freedom as TypeScript.
However, there's now a problem. Because Svelte, Vue, and esbuild can only handle one file at a time, they have no idea whether Writable or cookie are types or values, so using preserveValueImports with them would run into a huge issue. Namely, plugging our example into esbuild with preserveValueImports and isolatedModules set to true produces this output:
import { writable, Writable } from "svelte/stores"
import { cookie } from "my-cookie-library"
function createAWritableThing() {
return writable(23)
}
Do you see the problem? Because we enabled preserveValueImports (presumably, because we use eval, Vue, or Svelte) and isolatedModules, esbuild has decided to import Writable even though we don't actually want it. However, JavaScript imports are strict, and Writable won't just be undefined. It'll throw an error before our code even gets a chance to run.
TypeScript's solution is simple: just add type before any type imports you use. Then, all bundlers know that they're types and everything will work perfectly fine. However, this solution doesn't just depend on the code you write. It means that any .ts or .d.ts files written by any libraries you use (including svelte-local-storage-store) must also use import type. We could fix this code by simply adding type to Writable.
import { writable, type Writable } from "svelte/stores"
import { cookie } from "my-cookie-library"
function createAWritableThing() {
return writable(23)
}
Done! Now our code won't crash at runtime and we can keep the omnipotent being that is the TypeScript compiler happy.
@zsakowitz Thanks for the explanation. Even I got something from that.
Thanks @amserra & @zsakowitz!
:tada: This PR is included in version 0.6.0 :tada:
The release is available on:
Your semantic-release bot :package::rocket: