Skip to content
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

Using with typescript #473

Closed
farzinmirzaie opened this issue Jul 18, 2020 · 11 comments
Closed

Using with typescript #473

farzinmirzaie opened this issue Jul 18, 2020 · 11 comments

Comments

@farzinmirzaie
Copy link

🐛 Bug Report

I'm using @svgr/webpack inside a TypeScript react project and everything works fine without doing anything fancy! But if i pass props to svg component then i face exceptions. I found many same issues here but all closed without any answer so i'm opening a new one.

Note: I'm inside a monorepo and everything works fine on ReactNative using react-native-svg-transformer with TypeScript.

To Reproduce

<Icon /> This works fine

<Icon width={100} height={100} fill='red' /> This cause exception

Type '{ width: number; height: number; fill: string; }' is not assignable to type 'IntrinsicAttributes'.
Property 'width' does not exist on type 'IntrinsicAttributes'.  TS2322

So i tried to pass typescript: true option in my .svgrrc.js file to generates .tsx files with TypeScript typings but i get following exception and even using <Icon /> without passing props wont work anymore.

SyntaxError: unknown: Unexpected token, expected "," (3:21)

  1 | import * as React from "react";
  2 | 
> 3 | function SvgEye(props: React.SVGProps<SVGSVGElement>) {
    |                      ^
  4 |   ...

I tried to use custom template to generate .tsx file but still face same problem.

// .svgrrc.js
module.exports = {
	// typescript: true,
	ext: 'tsx',
	template: function defaultTemplate({ template }, opts, { componentName, jsx }) {
		const typeScriptTpl = template.smart({ plugins: ['typescript'] })
		return typeScriptTpl.ast`
		import * as React from "react"
		const ${componentName} = (props: React.SVGProps<SVGSVGElement>) => null;
		export default ${componentName}`
	},
	replaceAttrValues: {
		'#707070': '{props.fill}',
	},
}

Im returning null instead of ${jsx} just to make error message more readable

SyntaxError: unknown: Unexpected token, expected "," (3:21)

  1 | import * as React from "react";
  2 | 
> 3 | const SvgEye = (props: React.SVGProps<SVGSVGElement>) => null;
    |                      ^
  4 | 
  5 | export default SvgEye;

Expected behavior

The typescript: true option should fix the issue.

Paste the results here:

"devDependencies": {
	"@svgr/webpack": "^5.4.0",
	"typescript": "^3.9.6"
}
@open-collective-bot
Copy link

Hey @farzinmirzaie 👋,
Thank you for opening an issue. We'll get back to you as soon as we can.
Please, consider supporting us on Open Collective. We give a special attention to issues opened by backers.
If you use SVGR at work, you can also ask your company to sponsor us ❤️.

@michalpokojski
Copy link

So I had the exact same problem but with nextjs. It seems that when I added custom.d.ts with module desciber for *.svg it seems to work properly

// custom.d.ts

declare module '*.svg' {
  import * as React from 'react';

  const ReactComponent: React.FunctionComponent<React.SVGProps<SVGSVGElement>>;

  export default ReactComponent;
}
// next.config.js (where I inject webpack support to next webpack config)

module.exports = {
  webpack(config) {
    config.module.rules.push({
      test: /\.svg$/,
      use: [
        {
          loader: '@svgr/webpack',
        },
      ],
    });

    return config;
  },
};

and now type any prop for svg component, the only downside is it doesnt propose any props in IDE, but maybe its my fault, I'm writing in typescript for about a week :)

@farzinmirzaie
Copy link
Author

@michalpokojski Thank you but still not working for me! I see you are no passing typescript: ture option to @svgr/webpack so you are not generating SVG components with typescript template. can you share your tsconfig.json file here?

@michalpokojski
Copy link

michalpokojski commented Jul 23, 2020

@farzinmirzaie sure, i've deleted typescript: true just because it was not working, making same errors as yours, here's the config

{
  "compilerOptions": {
    "target": "es6",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": false,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "baseUrl": "."
  },
  "exclude": ["node_modules", ".next", "out"],
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.js", "custom.d.ts"]
}

I even tried to include ext: 'tsx' but with same result, just like it wouldn't generate tsx files but normal js.

@farzinmirzaie
Copy link
Author

@michalpokojski Thank you for sharing, But i couldn't make it work!
It should not has anything to do with monorepo but i don't know what else can be the problem.

@blaiz
Copy link

blaiz commented Sep 21, 2020

FYI if you're configuring your SVGR to work in tandem with the file loader and by default, when importing an SVG file you'll get the file's URL by default you have to import the ReactComponent export specifically to get the React component. I tweaked the suggestion from #473 (comment) slightly and found this to work with this scenario:

declare module "*.svg" {
  import * as React from "react";

  const ReactComponent: React.FunctionComponent<React.SVGProps<SVGSVGElement>>;
  export { ReactComponent };

  export default string;
}

Thought it could be useful to folks.

@stale
Copy link

stale bot commented Nov 24, 2020

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the wontfix label Nov 24, 2020
@stale stale bot closed this as completed Dec 2, 2020
@bernardoamim
Copy link

bernardoamim commented Dec 4, 2020

Hi, I was just having this problem, and this is how I got it to work: ✅

.svgrrc.js

module.exports = {
  template: require('./typescript-template'),
  ext: 'tsx',
  prettierConfig: {
    parser: 'typescript',
  },
  jsx: {
    babelConfig: {
      plugins: [
        // I removed these properties to have full control over them
        [
          '@svgr/babel-plugin-remove-jsx-attribute',
          {
            elements: ['path'],
            attributes: ['strokeWidth', 'stroke'],
          },
        ],
      ],
    },
  },
}

typescript-template.js

function typescriptTemplate(
  { template },
  opts,
  { imports, interfaces, componentName, props, jsx, exports },
) {
  const plugins = ['jsx'];

  if (opts.typescript) {
    plugins.push('typescript')
  }

  const typeScriptTpl = template.smart({ plugins });

  const formattedName = `${componentName.name}: React.FC<React.SVGProps<SVGSVGElement>>`

  return typeScriptTpl.ast`
  import React from 'react';

  const ${formattedName} = ${props} => {
    return ${jsx};
  }

  ${exports}
  `
}

module.exports = typescriptTemplate
`

CLI Command:

npx @svgr/cli --out-dir src/assets/svg/SVGComponents --template typescript-template.js --typescript

Hope it serves you well! ☺️

@willsmanley
Copy link

@bernardoamim thank you so much for providing your config!

It works beautifully for 90% of the SVGs that I have tried it on, but there are some edge cases where it doesn't not copy over the correct paths.

Try running your command on this.svg:

<svg width="23" height="18" viewBox="0 0 23 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<line y1="1" x2="23" y2="1" stroke="white" stroke-width="2"/>
<line y1="9" x2="23" y2="9" stroke="white" stroke-width="2"/>
<line y1="17" x2="23" y2="17" stroke="white" stroke-width="2"/>
</svg>

And the output is broken like SvgThis.tsx:

import React from 'react';

const SvgThis: React.FC<React.SVGProps<SVGSVGElement>> = (
  props: React.SVGProps<SVGSVGElement>,
) => {
  return (
    <svg
      width={23}
      height={18}
      fill='none'
      xmlns='http://www.w3.org/2000/svg'
      {...props}
    >
      <path d='M0 1h23M0 9h23M0 17h23' />
    </svg>
  );
};

export default SvgThis;

Whereas it should look like this instead:

import React from 'react';

const SvgThis: React.FC<React.SVGProps<SVGSVGElement>> = (
  props: React.SVGProps<SVGSVGElement>,
) => {
  return (
    <svg
      width={23}
      height={18}
      fill='none'
      xmlns='http://www.w3.org/2000/svg'
      {...props}
    >
      <line y1='1' x2='23' y2='1' stroke='white' strokeWidth='2' />
      <line y1='9' x2='23' y2='9' stroke='white' strokeWidth='2' />
      <line y1='17' x2='23' y2='17' stroke='white' strokeWidth='2' />
    </svg>
  );
};

export default SvgThis;

@bernardoamim
Copy link

bernardoamim commented May 24, 2021

@famouspotatoes Hi! I'm sorry I didn't answer sooner. I haven't checked out these issues in a while.

I don't think that is a problem with my solution. Actually, I don't see that as a problem at all.

I applied your code to see if there was any unexpected behaviour, but the result was the same.

Here are both SVGs place aside from each other:
Screen Shot 2021-05-24 at 14 09 47

As you can see, there is no difference. I just configured my icon as follows

Original SVG:

<svg width="23" height="18" viewBox="0 0 23 18" fill="none" xmlns="http://www.w3.org/2000/svg"> <line y1="1" x2="23" y2="1" stroke="white" stroke-width="2" /> <line y1="9" x2="23" y2="9" stroke="white" stroke-width="2" /> <line y1="17" x2="23" y2="17" stroke="white" stroke-width="2" /> </svg>

**Converted SVG (This.tsx): **

`import React from 'react';

const SvgThis: React.FC<React.SVGProps> = (
props: React.SVGProps,
) => {
return (
<svg
viewBox="0 0 23 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>


);
};

export default SvgThis;
`

Configured TSX as an imported component:

<ThisIcon width="23" stroke="#ff0000" strokeWidth={2} fill="red" />

@gregberge
Copy link
Owner

Fixed in #573

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

No branches or pull requests

6 participants