Skip to content

chore: revamp how we export types #2118

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

Merged
merged 1 commit into from
May 4, 2025
Merged

Conversation

SethFalco
Copy link
Member

@SethFalco SethFalco commented May 4, 2025

Finally, this is what all of my previous PRs recently have been leading up to. I've spent a lot of time reading up on TypeScript, JSDocs, and reviewing how different projects choose to maintain their types and settled on this for us.

I personally like writing JavaScript, not TypeScript. So I wanted to maximize use of the TypeScript tooling, but without adopting the TypeScript language. We were doing this already by typing through JSDocs, but this takes things further.

The fundamental change that this applies is that instead of manually maintaining type declarations for our public interface, we now generate those declarations using tsc. Running yarn run build:types is now part of the build process before publishing to npm, similar to how we must run build:bundles to create the CJS and browser bundles too.

Benefits

  • We maintain types closer to the implementation.
  • We guarantee that types are in sync with the implementation, and reduces the risk of mistakes when defining types.

Breaking Changes

I've decided to drop default exports from the library. My original plan was to keep them in SVGO v4, and export with both named and default exports. However, this seems a little more annoying to maintain than I was expecting.

Reasons:

  • Not a great experience to keep named exports and default exports in sync.
  • Not a great experience to maintain default exports that also need to re-export from other files.
  • When generating types with tsc, default exports didn't properly have their documentation forwarded in type declaration.
  • I'm sure these were all solvable issues, but it didn't seem worth solving when there's little to no benefit from exporting twice anyway.

Not the main reason, but other considerations:

This has no impact on users who:

  • Use SVGO from the CLI.
  • Import the project from CJS (require).
  • Who were already using named exports when importing SVGO.

However, if you are using the import syntax and imported SVGO via the default export, then you must adapt your code for v4:

- import svgo from 'svgo';
- svgo.optimize('<svg></svg>');

// Option 1. Use named exports!
+ import { optimize } from 'svgo';
+ optimize('<svg></svg>');

// Option 2. Or import everything as svgo!
+ import * as svgo from 'svgo';
+ svgo.optimize('<svg></svg>');

Changes

  • Avoid using @typedef unless we actually want to declare/export a type to be used in other files. TypeScript generates an export whenever we used @typedef. This is also why for common types like XastElement, the IDE would often auto-complete it to the @typedef of a random file rather than the file that actually defined the type.
  • Migrate some types from .d.ts files to JSDocs to maintain the type closer to the documentation.
  • Rename types.d.ts to types.ts. The .d.ts format is usually to define types for code you don't control, which doesn't apply in our case. When documenting your types, one should use .ts.
  • Migrate all manual type declarations to types.ts, as we'll generate types for the rest from the JSDoc types.
  • Avoid using = undefined in the JavaScript function signature, and prefer using the = suffix in the JSDoc type.
  • In our package.json, we no longer specify typesVersions.
    • This field is reluctant in our case since we only maintain one version of types.
    • TypeScript will prioritize exports.types over typesVersions anyway.

Testing

Documentation

  • Many of our plugins had an @example in their documentation that featured an arrow to separate before vs. after. I've normalized the indentation of the arrow to 2 spaces just to keep it consistent between examples.

Chores

  • Increment version to v4.0.0-rc.2.
  • Update documentation in CONTRIBUTING.md to refer to the new file to update when making a new plugin.
  • Update CONTRIBUTING.md to require Node.js v16 as a minimum, and clarify why.
  • Test types in a separate build step in GitHub Actions now.
  • Add directories that Yarn recommend to .gitattributes.
  • Organize our .gitignore and add some comments.

Related

Thanks

I want to give a special thanks to @open-wc who have written great articles on how they handle their type declarations, which I largely referenced to implement the workflow here.

The Open Web Components team have a great article titled Generating TypeScript Definition Files from JavaScript. This was very helpful because most guides share how to generate types declarations, which is the easy part, but don't dive into much detail on workflow or a good way to incorporate the generated types into a package to publish. Meanwhile, open-wc does dive into deal and have many simple examples of the setup in their repositories.

@SethFalco SethFalco merged commit 747cc72 into svg:main May 4, 2025
14 checks passed
@SethFalco SethFalco deleted the revamp-types branch May 4, 2025 16:39
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 this pull request may close these issues.

optimize types missing from svgo-node.d.ts in 4.0.0-rc.1
1 participant