Skip to content

polyfill: Update type definition to support exactOptionalPropertyTypes#3100

Merged
ptomato merged 2 commits intotc39:mainfrom
fabon-f:polyfill-dts-eop
Apr 17, 2025
Merged

polyfill: Update type definition to support exactOptionalPropertyTypes#3100
ptomato merged 2 commits intotc39:mainfrom
fabon-f:polyfill-dts-eop

Conversation

@fabon-f
Copy link
Copy Markdown
Contributor

@fabon-f fabon-f commented Mar 12, 2025

The exactOptionalPropertyTypes option in TypeScript bans passing undefined to optional properties unless undefined is allowed explicitly, because optional properties and undefined properties behave differently in JavaScript, to be precise.

https://www.typescriptlang.org/tsconfig/#exactOptionalPropertyTypes

function foo(
  dateTime: Temporal.ZonedDateTime,
  disambiguation?: "compatible" | "earlier" | "later" | "reject",
) {
  dateTime.with({ day: 1 }, { disambiguation });
}

For example, above code result in the error below with exactOptionalPropertyTypes.

Argument of type '{ disambiguation: "reject" | "compatible" | "earlier" | "later" | undefined; }' is not assignable to parameter of type 'Partial<AssignmentOptions & ToInstantOptions & OffsetDisambiguationOptions>' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
  Types of property 'disambiguation' are incompatible.
    Type '"reject" | "compatible" | "earlier" | "later" | undefined' is not assignable to type '"reject" | "compatible" | "earlier" | "later"'.
      Type 'undefined' is not assignable to type '"reject" | "compatible" | "earlier" | "later"'.

As I understand it, Temporal's API doesn't distinguish optional properties and properties with undefined in passed objects (XXLike objects and options) and passing undefined as a value is safe.

This pull request allows passing undefined to Temporal's built-in methods under the exactOptionalPropertyTypes option in TypeScript.

note: usage of Partial in the type definition is no longer necessary because original type contains only optional properties, so I removed it for clean-up. I'll revert it if there is a reason to leave Partial as is.

@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 12, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 96.05%. Comparing base (9999373) to head (711f935).
Report is 7 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #3100   +/-   ##
=======================================
  Coverage   96.05%   96.05%           
=======================================
  Files          21       21           
  Lines        9951     9951           
  Branches     1802     1802           
=======================================
  Hits         9558     9558           
  Misses        346      346           
  Partials       47       47           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Collaborator

@ptomato ptomato left a comment

Choose a reason for hiding this comment

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

Makes sense to me, though maybe @justingrant would like to take a look?

@justingrant
Copy link
Copy Markdown
Collaborator

This seems fine. I'd suggest we wait under all API-affecting normative changes are merged (or are we already there?) and then see if anything needs to be revised as a result of type changes from those normative changes.

@nekevss
Copy link
Copy Markdown

nekevss commented Mar 14, 2025

Apologies ahead of time for the interjection, but this seems a bit odd to me and peaked my curiosity.

I think I understand the premise, but the type definitions on the options already appear correct to me, and this adds an extra variant that is not totally true for the types. For instance, overflow can and should only ever be "constrain" or "reject", not "constrain", "reject", and "undefined". The options for a method (say PlainDate.prototype.add) may be "ArithmeticOverflow" or "undefined", but the option itself cannot be undefined. In other words, this feels like the change would be more ideal to the type in the function parameter and then resolved into the option.

Is this just a side effect of types on a TypeScript library API? Or does exactOptionalPropertyTypes also prevent this from being added to the function parameters?

EDIT: for relevant background, I'm obviously a bit more familiar with Rust library APIs than TypeScript library APIs. And so this could just be a type system difference, but thought I'd confirm

@justingrant
Copy link
Copy Markdown
Collaborator

overflow can and should only ever be "constrain" or "reject", not "constrain", "reject", and "undefined".

If a property or parameter is optional, then undefined is already allowed. This change is just about how this is represented in the TS declarations. It doesn't change any compile-time or runtime behavior using default TS options.

If a parameter or property is market optional but it's not optional, then you're correct and we should change types to make it non-optional.

@ptomato
Copy link
Copy Markdown
Collaborator

ptomato commented Apr 17, 2025

I think this is OK to merge as we don't expect any further API-affecting normative changes.

@ptomato ptomato merged commit 50489ad into tc39:main Apr 17, 2025
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.

4 participants