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: implement TS exactOptionalPropertyType rule #90

Merged
merged 20 commits into from
Mar 19, 2025

Conversation

BhumikP
Copy link
Contributor

@BhumikP BhumikP commented Feb 21, 2025

What

Implemention of "exactOptionalPropertyTypes": true rule

Why

stricter rules around how ts handles properties on type or interfaces which have a ? prefix.

Related Issue(s):

https://github.com/rtCamp/headless/issues/317

How

Testing Instructions

Screenshots

Additional Info

interface UserDefaults {
  colorThemeOverride?: "dark" | "light";
}
declare function getUserSettings(): UserDefaults;
// ---cut---
const settings = getUserSettings();
settings.colorThemeOverride = "dark";
settings.colorThemeOverride = "light";

// But not:
settings.colorThemeOverride = undefined;
//type 'undefined' is not assignable to type '"dark" | "light"' with 'exactOptionalPropertyTypes: true'

Without this flag enabled, there are three values which you can set colorThemeOverride to be: “dark”, “light” and undefined .

Checklist

  • I have read the Contribution Guidelines.
  • My code is tested to the best of my abilities.
  • My code passes all lints (ESLint, tsc, prettier etc.).
  • My code has detailed inline documentation.
  • I have added unit tests to verify the code works as intended.
  • I have updated the project documentation as needed.
  • I have added a changeset for this PR using npm run changeset.

Copy link

changeset-bot bot commented Feb 21, 2025

🦋 Changeset detected

Latest commit: 377f636

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 5 packages
Name Type
@snapwp/blocks Patch
@snapwp/query Patch
@snapwp/types Patch
@snapwp/core Patch
@snapwp/next Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@justlevine justlevine marked this pull request as ready for review February 25, 2025 15:36
Copy link
Collaborator

@justlevine justlevine left a comment

Choose a reason for hiding this comment

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

Lot of things I like here, but a lot of coercions that I don't think I understand.

As per usual, I only flagged the first instance of any issues I had (and not every repeat)


PS:

  1. I updated the PR title per Conventional Commits. Even though we use changesets for changelogs, git history is still important for people to navigate (ps: also why we squash PRs)
  2. I marked the PR as ready for review, since my understanding from today's sync is that it is ready for review, it just might possibly not be ready to be merged. Try and use draft PRs for things you're still actively working on , and use the PR description to indicate things like merge blockers, PR dependencies, etc.
    (Draft or normal PR, you still can assign individuals for review and dismiss stale/re-ping them for another review)

@justlevine justlevine changed the title refactor: implement ts exactoptionalPropertyType rule refactor: implement TS exactOptionalPropertyType rule Feb 26, 2025
@BhumikP BhumikP self-assigned this Feb 27, 2025
Copy link
Collaborator

@justlevine justlevine left a comment

Choose a reason for hiding this comment

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

Tl;dr

  • For passing props: whenever possible, we should avoid inlining logic inside <Components ... />, and instead assign that logic to a variable.
  • For our types: we should add | undefined to every single optional type unless there is an explicit reason to treat ?: different from an explicit undefined.
  • For our components: if there's a value that we need to coerce to a different fallback shape to satisfy a type, it's likely a sign that we need to fix the type of either the prop or the variable.
  • Always use propName={ ...( var && { propName: var } over propName={ varName || undefined }, since this rule means explicitly undefined props are not the same as optional ones.

PS: Don't forget to check previously unresolved discussions before requesting re-review.

@ayushnirwal ayushnirwal requested a review from justlevine March 6, 2025 06:51
ayushnirwal
ayushnirwal previously approved these changes Mar 6, 2025
Copy link
Collaborator

@justlevine justlevine left a comment

Choose a reason for hiding this comment

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

My previous review remains relevant; no need to ping me again for review until all the unresolved conversations have been addressed.

Copy link
Collaborator

@justlevine justlevine left a comment

Choose a reason for hiding this comment

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

Personally I use the GitHub Pull Requests extension for VSCode, but if you're on browser and have a doubt about what still needs to be done, a good place to start is the Conversations button.

image

@BhumikP BhumikP requested a review from justlevine March 17, 2025 06:36
Copy link
Collaborator

@justlevine justlevine left a comment

Choose a reason for hiding this comment

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

I got sick of repeating myself so I handled the open comments in 166fb4f

Only things remaining are:

  • @ayushnirwal should we be adding | undefined to all of our undefined types in @core/props or @types? (IMO yes, since unless we differentiate on behavior, we should absorb the headache for end-users who also want the exactOptionalPropertyType enforced locally, but I defer to you).
  • after a decision/change per the above (so the package diff is accurate), we need to npm run changeset

@ayushnirwal
Copy link
Collaborator

@ayushnirwal should we be adding | undefined to all of our undefined types in @core/props or @types? (IMO yes, since unless we differentiate on behavior, we should absorb the headache for end-users who also want the exactOptionalPropertyType enforced locally, but I defer to you).

Most of the types do not need | undefined according to my understanding. I see no cases where we need to set a prop (to a block renderer) to be undefined. It can be just left undefined. The same logic is applied to type describing env vars.
Other remaining types (related to block manager) may need | undefined but they are not fully "baked" yet. Decisions for them can be made when we do that.

Copy link
Collaborator

@justlevine justlevine left a comment

Choose a reason for hiding this comment

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

@ayushnirwal should we be adding | undefined to all of our undefined types in @core/props or @types? (IMO yes, since unless we differentiate on behavior, we should absorb the headache for end-users who also want the exactOptionalPropertyType enforced locally, but I defer to you).

Most of the types do not need | undefined according to my understanding. I see no cases where we need to set a prop (to a block renderer) to be undefined. It can be just left undefined. The same logic is applied to type describing env vars. Other remaining types (related to block manager) may need | undefined but they are not fully "baked" yet. Decisions for them can be made when we do that.

While I still disagree with the former:

  • (the only reason a user needs to care about explicit/implicit undefineds is because they are enabling exactOptionalPropertyType, which is a practice we want to encourage. So we should make it easy for them by ensuring we handle explicit/implicit undefineds in the components they might use. "Why" they might be passing an explicit undefined to one of our component props shouldn't matter to us or it risks becoming a source of tech debt.)

I very much agree with the latter:

  • we can deal with this incrementally as needed, and adding | undefined to a type is a nonbreaking change.

@justlevine justlevine merged commit c956f31 into rtCamp:develop Mar 19, 2025
8 checks passed
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.

3 participants