Skip to content

typescript does not recognize svelte syntax #144

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
plimeor opened this issue May 8, 2020 · 15 comments · Fixed by sveltejs/svelte#4973
Closed

typescript does not recognize svelte syntax #144

plimeor opened this issue May 8, 2020 · 15 comments · Fixed by sveltejs/svelte#4973

Comments

@plimeor
Copy link

plimeor commented May 8, 2020

Describe the bug
typescript does not recognize svelte syntax when I use Sapper.

Logs

syntax error reporting:

# error for 'import Nav from '../components/Nav.svelte';'
Module '"/Users/mervin/Projects/vache/src/components/Nav.svelte"' has no default export.

# error for '$: a = segment + 'aa';'
Cannot find name 'a'.ts(2304)

build error reporting:

To Reproduce

repo: https://github.com/mervinjin/vache/tree/svelte-ts-issue

// src/routes/_layout.svelte

<script lang="typescript" >
	import Nav from '../components/Nav.svelte';

	export let segment;

	$: a = segment + 'aa';
</script>

Expected behavior
should have no error.

Information about your project:

  • Your operating system: OS X 10.15.4

  • svelte-preprocess version : v3.7.4

  • Whether your project uses Webpack or Rollup: yes, I use rollup

@plimeor
Copy link
Author

plimeor commented May 8, 2020

I just want to solve the build error.

@plimeor
Copy link
Author

plimeor commented May 12, 2020

By looking at the code it seems like that should be supported:

https://github.com/kaisermann/svelte-preprocess/blob/52dccb7d45e2bb790e1a9689ce090a3b01469394/src/transformers/typescript.ts#L88

https://github.com/kaisermann/svelte-preprocess/blob/52dccb7d45e2bb790e1a9689ce090a3b01469394/test/transformers/typescript.test.ts#L145

Here's an example in case it helps you figure out what's wrong:

https://github.com/kaisermann/svelte-preprocess/blob/master/examples/sapper-rollup/src/routes/blog/index.svelte

@benmccann
It still has the error.

I try to add a test case, and it will fail.

it('should apply "$: " syntax', async () => {
  const { diagnostics } = await transpile(
    `
      $: anything = "test";
    `,
  );
  expect(diagnostics.some(d => d.code === 2304)).toBe(false);
});

@high1
Copy link

high1 commented Jun 2, 2020

By looking at the code it seems like that should be supported:

https://github.com/kaisermann/svelte-preprocess/blob/52dccb7d45e2bb790e1a9689ce090a3b01469394/src/transformers/typescript.ts#L88
https://github.com/kaisermann/svelte-preprocess/blob/52dccb7d45e2bb790e1a9689ce090a3b01469394/test/transformers/typescript.test.ts#L145

Here's an example in case it helps you figure out what's wrong:

https://github.com/kaisermann/svelte-preprocess/blob/master/examples/sapper-rollup/src/routes/blog/index.svelte

Isn't this for diagnostic code 2552? Diagnostic code here is 2304, and the message is different:

 [log] src/App.svelte:5:4 - error TS2304: Cannot find name 'double'.

  5 $: double = count * 2;

I'm seeing this too when using typescript with svelte-preprocess.

@benmccann
Copy link
Member

benmccann commented Jun 2, 2020

Hmm. It looks like the example app I was testing on has a workaround of declaring the variable first:

https://github.com/babichjacob/sapper-typescript-graphql-template/blob/81bc762b6e7f61ddc659e5691533d28abc5ca703/src/routes/_layout.svelte#L13

Or maybe they just do that to declare the type? It's a good question of how do you declare the type on a reactive variable like that. You'd probably want to be able to do $: squared: number = num * num

@benmccann
Copy link
Member

Yeah, I just put the example from the Svelte docs in a TypeScript app:

	export let num;

	// we don't need to declare `squared` and `cubed`
	// — Svelte does it for us
	$: squared = num * num;
	$: cubed = squared * num;

It failed:

src/routes/index.svelte:8:4 - error TS2304: Cannot find name 'squared'.

8 $: squared = num * num;
     ~~~~~~~
src/routes/index.svelte:9:4 - error TS2304: Cannot find name 'cubed'.

9 $: cubed = squared * num;
     ~~~~~
src/routes/index.svelte:9:12 - error TS2304: Cannot find name 'squared'.

9 $: cubed = squared * num;

@benmccann
Copy link
Member

benmccann commented Jun 2, 2020

It looks like TypeScript has multiple "Cannot find name" diagnostic codes
https://github.com/microsoft/TypeScript/blob/v3.9.3/src/compiler/diagnosticMessages.json

I wonder if some others like 2662 might also be triggered under certain circumstances. I'm not sure the best way to handle this. If we should still check just for certain codes or the message

@high1
Copy link

high1 commented Jun 2, 2020

Hmm. It looks like the example app I was testing on has a workaround of declaring the variable first:

https://github.com/babichjacob/sapper-typescript-graphql-template/blob/81bc762b6e7f61ddc659e5691533d28abc5ca703/src/routes/_layout.svelte#L13

Or maybe they just do that to declare the type? It's a good question of how do you declare the type on a reactive variable like that. You'd probably want to be able to do $: squared: number = num * num

This does remove the issue - so you need to declare the variable first for it to be recognized by TypeScript. Makes sense - TypeScript compiler is running before Svelte, and this is not a valid TypeScript syntax. Is this valid JS?

@benmccann
Copy link
Member

My understanding is the preprocessor pulls the code out of the script tags in .svelte files and runs it through the TypeScript compiler. And then the Svelte compiler takes over and knows how to deal with it and turn it into JS.

I'm not affiliated with this project, btw. I've just spent some time of my own trying to get TypeScript stuff working. So I might be wrong on some of this

@mervinjin's test case above is a good one. I wonder if someone just changed the diagnostic code check and added that test case if it'd be enough to get things working and send in a PR

@high1
Copy link

high1 commented Jun 2, 2020

It would definitely work, but should another workaround be added for this? I'd rather document this as something that needs to be handled a bit differently on TS side.

@benmccann
Copy link
Member

I don't think it's a workaround. I think that's how this was actually meant to be implemented. We're telling TypeScript to ignore the $ syntax because it's fine and valid Svelte syntax. It's just that the current check only catches the $ sometimes

@high1
Copy link

high1 commented Jun 2, 2020

I see the point. $: is actually valid JS syntax - it's just that this is not valid for TS compiler. So you need to handle that also as a special case on preprocess side or user needs to declare the variable before the label. It's just that the number of special cases handled for each supported preprocessor could become overwhelming.

@benmccann
Copy link
Member

benmccann commented Jun 3, 2020

We have to do something special to handle $ whether it's ignoring the resulting error, reprocessing the source before passing it to TypeScript, etc. That's the whole point of the preprocessor. If we do nothing then TypeScript can't compile it.

I'm still wondering the best way to handle this. Neither Svelte nor TypeScript say how the two should be combined. We basically have to invent that here.

The most natural would be:

$: let squared: number = num * num;
$: let cubed: number = squared * num;

However, neither TypeScript nor Svelte like that. We can deal with the TypeScript part here, but the Svelte part might require support from the Svelte team.

The following seems to work now:

	$: {
		let squared: number = num * num;
		let cubed: number = squared * num;
	}

I think we could also make the following work by only changing the preprocessor:

	$: { let squared: number = num * num; }
	$: { let cubed: number = squared * num; }

@benmccann
Copy link
Member

Based off the discussion in sveltejs/svelte#4965 I'm convinced that nothing should change in Svelte itself. There was a suggestion there that we use the two line split declaration and initialization syntax:

let a: string;
$: a = segment + 'aa';

The good thing about this option is that it doesn't introduce new syntax that's specific to the pre-processor and is already supported today. We could just close this ticket by adding better documentation. It is more verbose, but I haven't written enough code with the combination of TypeScript and Svelte to know whether it'd be terribly annoying or not.

@fnune
Copy link

fnune commented Jul 4, 2020

Hey there, thanks for the thread.

My two cents on this:

$: a = segment + 'aa'; is valid TypeScript syntax. The error is not a parsing error but a type error. TS interprets that you're trying to access an a variable on the global scope (because there's no let or const or var before it), but there's no a variable in the global scope. Just to illustrate it, you can change it to a global variable that does exist and see the error change:

$: name = segment + 'aa'; //  error TS2588: Cannot assign to 'name' because it is a constant.

So as @benmccann suggests, you just need to declare a variable in the scope of the component for the error to go away.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants