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

Refactor to use TypeScript @import JSDoc tags #2498

Merged
merged 2 commits into from
Jun 26, 2024
Merged

Refactor to use TypeScript @import JSDoc tags #2498

merged 2 commits into from
Jun 26, 2024

Conversation

remcohaszing
Copy link
Member

@remcohaszing remcohaszing commented Jun 25, 2024

Initial checklist

  • I read the support docs
  • I read the contributing guide
  • I agree to follow the code of conduct
  • I searched issues and couldn’t find anything (or linked relevant results below)
  • If applicable, I’ve added docs and tests

Description of changes

Previously we used @typedef tags to simulate type imports. The problem is that are not really imports. They define and export a new type. We now use @import introduced by TypeScript 5.5 to actually import types.

The pattern using @typedef is still used in index files to export types.

This change also replaces the type casting inside MDX files with definitions of the Props types.

This also replaces usage of the global JSX namespace with proper types. For TypeScript<5.1 the correct return type of React components is ReactElement, for Preact VNode. For TypeScript>=5.1, the correct return type for React components is ReactNode, for Preact ComponentChildren.

ReactElement / VNode and are compatible with TypeScript>=5.1. This is why those are used for public facing APIs, but ReactNode / ComponentChildren is used for internal components.

Previously we used `@typedef` tags to simulate type imports. The problem
is that are not really imports. They define and export a new type. We
now use `@import` introduced by TypeScript 5.5 to actually import types.

The pattern using `@typedef` is still used in index files to export
types.

This change also replaces the type casting inside MDX files with
definitions of the `Props` types.

This also replaces usage of the global `JSX` namespace with proper
types. For TypeScript<5.1 the correct return type of React components is
`ReactElement`, for Preact `VNode`. For TypeScript>=5.1, the correct
return type for React components is `ReactNode`, for Preact
`ComponentChildren`.
@remcohaszing remcohaszing added 🦋 type/enhancement This is great to have ☂️ area/types This affects typings 👶 semver/patch This is a backwards-compatible fix 🤞 phase/open Post is being triaged manually labels Jun 25, 2024
Copy link

vercel bot commented Jun 25, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
mdx ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jun 25, 2024 7:52pm

* @import {
MdxJsxAttribute,
MdxJsxAttributeValueExpression,
MdxJsxExpressionAttribute
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using asterisks for multiline imports caused problems, but not in the TypeScript playground. I’m looking into this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’d work around it by not wrapping 🤷‍♂️

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered that. It would be ok’ish for this line, but some others would become extremely long. I prefer to keep it like this TBH, keeping the max length at approximately what we use for Prettier.

* @import {
MdxJsxAttribute,
MdxJsxAttributeValueExpression,
MdxJsxExpressionAttribute
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’d work around it by not wrapping 🤷‍♂️

@@ -601,7 +601,7 @@ function ErrorFallback(properties) {
/**
* @param {DisplayProperties} properties
* Properties.
* @returns {JSX.Element}
* @returns {ReactNode}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better in separate PRs

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True. One thing let to another. IMO this is fine to keep as-is now.

{/**
* @typedef Props
* @property {Item} navigationTree
*/}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not 100% on this style? I’d lean to:

{
  /**
   * @import {Item} from '../_component/sort.js'
   */

  /**
   * @typedef Props
   * @property {Item} navigationTree
   */
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have given this some thought while working on MDX language server. The indentation of this style feels a bit weird IMO.

{/**
  * @import {Component} from 'react'
  * @import {AvatarProps} from './avatar.js'
  */}

{/**
  * @typedef Comonents
  * @property {Component<AvatarProps>} avatar
  */}

{/**
  * @typedef Props
  * @property {Comonents} components
  */}

{/**
  * Some description
  *
  * @param {unknown} parameter
  * @returns {undefined}
  */}
export function someFunction(parameter) {}

# Hello

<Avatar />

But combining all comments, or combining comments arbritratily, with or without indentation feels even more weird to me.

{
/**
 * @import {Component} from 'react'
 * @import {AvatarProps} from './avatar.js'
 */

/**
 * @typedef Comonents
 * @property {Component<AvatarProps>} avatar
 */

/**
 * @typedef Props
 * @property {Comonents} components
 */
}

{/**
  * Some description
  *
  * @param {unknown} parameter
  * @returns {undefined}
  */}
export function someFunction(parameter) {}

# Hello

<Avatar />

It feels a bit similar to whether or not to group typedefs in a single comment.

So far I feel best about putting each typedef in a separate comment, and each comment in a separate mdxFlowExpression.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I feel quite strongly that entering/exiting JS each time is ugly.

And that if you’d put something in there (like a jsx element), that it would be indented 🤔

I’m fine to different typedefs in different comments.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I feel quite strongly that entering/exiting JS each time is ugly.

I don’t really mind either style for typedefs. For function docs I would find it weird to combine though, but that’s not applicable in this particular case.

Another thing is that commenting is based on {/* and */} delimiters. Though I don’t think it makes sense to uncomment jsdoc comments.

And that if you’d put something in there (like a jsx element), that it would be indented 🤔

That makes perfect sense. This also reminds me that it’s possible to have multiple comments in one a flow expression, but just one JS expression. JSDoc has meaning, which makes me lean towards preferring one doc per flow expression again. But having the types grouped using one flow expression is also kind of nice.


Anyway, these are all just nitty details. I’ll adjust them to use one MDX flow expression.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For function docs I would find it weird to combine though, but that’s not applicable in this particular case.

Can you clarify what you dislike/like with examples?

Though I don’t think it makes sense to uncomment jsdoc comments.

Agreed. These things are more like type annotations, which currently use special comments, because that’s what’s possible now.

This also reminds me that it’s possible to have multiple comments in one a flow expression, but just one JS expression. JSDoc has meaning, which makes me lean towards preferring one doc per flow expression again. But having the types grouped using one flow expression is also kind of nice.

Right, right, I was thinking about that at first too, but then there’s a difference: these are allowed to be empty! And comments are deemed empty here too. 0 or more empty is still empty. So, zero-or-more empty, then 0-1 expression.

To clarify, some examples of what I think looks good.

Regular comment:

# hi

{/* just a smol comment */}

hello

More complex expression:

# hi

{
  (function () {
    return 1
  })()
}

hello

One JSDoc:

{
  /**
   * @import {foo} from 'bar'
   */
}

# hi

hello

Multi JSDoc:

{
  /**
   * @import {foo} from 'bar'
   */

  /**
   * @typedef Options
   * @property {number} something
   */
}

# hi

hello

Multi JSDoc and expression:

# hi

{
  /**
   * @import {foo} from 'bar'
   */

  /**
   * @typedef Options
   * @property {number} something
   */

  (function () {
    return 1
  })()
}

hello

Also, was just thinking about how do-expressions will be nice!

{
  do {
    if (loggedIn) {
      <LogoutButton />
    } else {
      <LoginButton />
    }
  }
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you clarify what you dislike/like with examples?

If there are multiple exports with JSDoc annotations, It feels weird to me to merge the mdx flow expression the typedefs with that of the JSDoc of the first export.

{
  /**
   * @typedef Props
   * @property {string} whatever
   */

  /**
   * @param {string} arg
   * @returns undefined
   */
}
export function someExport(arg) {}

{
  /**
   * @param {string} arg
   * @returns undefined
   */
}
export function anotherExport(arg) {}

Also, was just thinking about how do-expressions will be nice!

Yes, that would be so nice!

Copy link
Member

@wooorm wooorm Jun 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, I can see this. But that feels like an edge case to me, induced because we allow export statements directly in markdown 🤔

I’m fine with breaking it like:

{
  /**
   * @import {foo} from 'bar'
   */

  /**
   * @typedef Options
   * @property {number} something
   */
}

{
  /**
   * @param {string} arg
   * @returns {undefined}
   */
}
export function someExport(arg) {}

* @typedef {import('./lib/evaluate.js').EvaluateOptions} EvaluateOptions
* @typedef {import('./lib/run.js').RunOptions} RunOptions
* @typedef {import('./lib/util/resolve-evaluate-options.js').EvaluateOptions} EvaluateOptions
* @typedef {import('./lib/util/resolve-evaluate-options.js').RunOptions} RunOptions
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like an index.d.ts, as it has export type {} from, here it would get rid of the typedef/type alias https://github.com/rehypejs/rehype-document/blob/main/index.d.ts

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m working on a follow-up PR where I think this is better discussed. I do agree export {} from is an improvement over a typedef.

* @typedef Props
* @property {Item} navigationTree
*/
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<3

* @typedef Props
* @property {Item} navigationTree
*/
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like this it’s also more similar to frontmatter 🤔

@wooorm wooorm changed the title Use TypeScript @import JSDoc tags Refactor to use TypeScript @import JSDoc tags Jun 26, 2024
@wooorm wooorm merged commit f12afda into main Jun 26, 2024
10 checks passed
@wooorm wooorm deleted the type-imports branch June 26, 2024 10:35
@wooorm wooorm added the 💪 phase/solved Post is done label Jun 26, 2024
@wooorm
Copy link
Member

wooorm commented Jun 26, 2024

In this PR, I saw the existing code:

// Register MDX nodes in mdast:
/// <reference types="remark-mdx" />

I think we can now change, and recommend @import {} from 'remark-mdx?

@wooorm
Copy link
Member

wooorm commented Jun 26, 2024

Thanks :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
☂️ area/types This affects typings 🤞 phase/open Post is being triaged manually 💪 phase/solved Post is done 👶 semver/patch This is a backwards-compatible fix 🦋 type/enhancement This is great to have
Development

Successfully merging this pull request may close these issues.

2 participants