Skip to content

Add lib.esnext.temporal#62628

Merged
jakebailey merged 10 commits intomicrosoft:mainfrom
Renegade334:lib-esnext-temporal
Feb 7, 2026
Merged

Add lib.esnext.temporal#62628
jakebailey merged 10 commits intomicrosoft:mainfrom
Renegade334:lib-esnext-temporal

Conversation

@Renegade334
Copy link
Contributor

@Renegade334 Renegade334 commented Oct 19, 2025

Closes #60164.

No custom calendar support, as per the latest spec changes.

There are lots of places in the spec where property bags have "at least one of" constraints (eg. either month or monthCode is mandatory for a DateLikeObject), which can't really be straightforwardly expressed. Otherwise, appears to be playing well.

Temporal shipped in Firefox 139, and is staging in Chromium (shipping in 144).

@github-project-automation github-project-automation bot moved this to Not started in PR Backlog Oct 19, 2025
@typescript-bot typescript-bot added the For Uncommitted Bug PR for untriaged, rejected, closed or missing bug label Oct 19, 2025
@Renegade334 Renegade334 force-pushed the lib-esnext-temporal branch 2 times, most recently from f8fc33a to 9f45da2 Compare October 20, 2025 03:49
@jakebailey
Copy link
Member

We usually don't create tests for lib.d.ts like was done here; they're pretty annoying because they are typically very sensitive to tweaks, new lib.d.ts, etc. I think I missed this in the previous PR from you I merged, though.

Probably we should just delete them?

@typescript-bot test it

@typescript-bot
Copy link
Collaborator

typescript-bot commented Oct 20, 2025

Starting jobs; this comment will be updated as builds start and complete.

Command Status Results
test top400 ✅ Started ✅ Results
user test this ✅ Started ✅ Results
run dt ✅ Started ✅ Results
perf test this faster ✅ Started 👀 Results

@typescript-bot
Copy link
Collaborator

Hey @jakebailey, the results of running the DT tests are ready.

Everything looks the same!

You can check the log here.

@typescript-bot
Copy link
Collaborator

@jakebailey Here are the results of running the user tests with tsc comparing main and refs/pull/62628/merge:

There were infrastructure failures potentially unrelated to your change:

  • 1 instance of "Git clone failed"

Otherwise...

Everything looks good!

@Renegade334
Copy link
Contributor Author

Probably we should just delete them?

Dealer's choice. I needed them for testing anyway, but if they're of no value within the library then they can just go.

@typescript-bot
Copy link
Collaborator

@jakebailey
The results of the perf run you requested are in!

Here they are:

tsc

Comparison Report - baseline..pr
Metric baseline pr Delta Best Worst p-value
Compiler-Unions - node (v18.15.0, x64)
Errors 34 34 ~ ~ ~ p=1.000 n=6
Symbols 62,370 62,370 ~ ~ ~ p=1.000 n=6
Types 50,386 50,386 ~ ~ ~ p=1.000 n=6
Memory used 195,219k (± 0.91%) 194,013k (± 0.93%) ~ 192,756k 196,339k p=0.230 n=6
Parse Time 1.31s (± 0.42%) 1.30s (± 0.31%) ~ 1.30s 1.31s p=0.282 n=6
Bind Time 0.72s (± 0.57%) 0.72s (± 0.57%) ~ 0.72s 0.73s p=1.000 n=6
Check Time 9.76s (± 0.23%) 9.74s (± 0.28%) ~ 9.70s 9.78s p=0.465 n=6
Emit Time 2.74s (± 0.85%) 2.74s (± 0.64%) ~ 2.72s 2.77s p=0.622 n=6
Total Time 14.52s (± 0.18%) 14.52s (± 0.18%) ~ 14.48s 14.55s p=0.872 n=6
angular-1 - node (v18.15.0, x64)
Errors 2 2 ~ ~ ~ p=1.000 n=6
Symbols 955,117 955,752 +635 (+ 0.07%) ~ ~ p=0.001 n=6
Types 415,619 415,621 +2 (+ 0.00%) ~ ~ p=0.001 n=6
Memory used 1,254,564k (± 0.00%) 1,255,717k (± 0.00%) +1,153k (+ 0.09%) 1,255,689k 1,255,748k p=0.005 n=6
Parse Time 6.49s (± 0.18%) 6.52s (± 0.69%) ~ 6.48s 6.59s p=0.622 n=6
Bind Time 1.88s 1.90s (± 0.27%) +0.02s (+ 0.89%) 1.89s 1.90s p=0.002 n=6
Check Time 32.17s (± 0.26%) 32.16s (± 0.27%) ~ 32.01s 32.25s p=0.936 n=6
Emit Time 14.86s (± 0.35%) 14.87s (± 0.56%) ~ 14.73s 14.96s p=0.687 n=6
Total Time 55.39s (± 0.09%) 55.44s (± 0.27%) ~ 55.23s 55.67s p=0.423 n=6
mui-docs - node (v18.15.0, x64)
Errors 1 1 ~ ~ ~ p=1.000 n=6
Symbols 552,252 552,252 ~ ~ ~ p=1.000 n=6
Types 89 89 ~ ~ ~ p=1.000 n=6
Memory used 826,973k (± 0.00%) 826,987k (± 0.00%) ~ 826,975k 826,994k p=0.065 n=6
Parse Time 8.59s (± 0.39%) 8.59s (± 0.63%) ~ 8.54s 8.69s p=0.686 n=6
Bind Time 2.19s (± 0.47%) 2.20s (± 0.47%) ~ 2.18s 2.21s p=0.560 n=6
Check Time 0.00s 0.00s ~ ~ ~ p=1.000 n=6
Emit Time 0.27s (± 3.66%) 0.26s (± 3.10%) ~ 0.26s 0.28s p=0.336 n=6
Total Time 11.05s (± 0.32%) 11.05s (± 0.55%) ~ 11.00s 11.16s p=0.747 n=6
self-build-src - node (v18.15.0, x64)
Errors 0 0 ~ ~ ~ p=1.000 n=6
Symbols 1,237,232 1,237,239 +7 (+ 0.00%) ~ ~ p=0.001 n=6
Types 259,856 259,870 +14 (+ 0.01%) ~ ~ p=0.001 n=6
Memory used 2,486,212k (±11.93%) 2,607,270k (±14.39%) ~ 2,364,517k 3,092,349k p=0.378 n=6
Parse Time 5.19s (± 2.10%) 5.18s (± 1.84%) ~ 5.09s 5.33s p=1.000 n=6
Bind Time 1.76s (± 1.33%) 1.77s (± 1.04%) ~ 1.75s 1.79s p=0.803 n=6
Check Time 35.17s (± 0.57%) 35.26s (± 0.26%) ~ 35.14s 35.39s p=0.173 n=6
Emit Time 3.00s (± 1.81%) 3.00s (± 1.04%) ~ 2.97s 3.04s p=0.810 n=6
Total Time 45.13s (± 0.62%) 45.22s (± 0.34%) ~ 44.98s 45.39s p=0.173 n=6
self-build-src-public-api - node (v18.15.0, x64)
Errors 0 0 ~ ~ ~ p=1.000 n=6
Symbols 1,237,232 1,237,239 +7 (+ 0.00%) ~ ~ p=0.001 n=6
Types 259,856 259,870 +14 (+ 0.01%) ~ ~ p=0.001 n=6
Memory used 3,158,673k (± 0.04%) 3,158,859k (± 0.02%) ~ 3,157,774k 3,159,423k p=0.936 n=6
Parse Time 6.87s (± 0.83%) 6.88s (± 0.42%) ~ 6.83s 6.91s p=1.000 n=6
Bind Time 2.15s (± 1.75%) 2.16s (± 1.16%) ~ 2.14s 2.21s p=0.335 n=6
Check Time 42.63s (± 0.39%) 42.60s (± 0.34%) ~ 42.47s 42.83s p=0.575 n=6
Emit Time 3.51s (± 2.65%) 3.52s (± 1.41%) ~ 3.42s 3.55s p=0.688 n=6
Total Time 55.15s (± 0.36%) 55.18s (± 0.21%) ~ 55.03s 55.39s p=0.810 n=6
self-compiler - node (v18.15.0, x64)
Errors 0 0 ~ ~ ~ p=1.000 n=6
Symbols 262,534 262,541 +7 (+ 0.00%) ~ ~ p=0.001 n=6
Types 104,035 104,049 +14 (+ 0.01%) ~ ~ p=0.001 n=6
Memory used 440,683k (± 0.01%) 440,661k (± 0.01%) ~ 440,634k 440,700k p=0.173 n=6
Parse Time 3.51s (± 1.07%) 3.52s (± 1.10%) ~ 3.46s 3.56s p=0.935 n=6
Bind Time 1.32s (± 1.07%) 1.32s (± 0.74%) ~ 1.31s 1.33s p=0.869 n=6
Check Time 19.04s (± 0.32%) 19.01s (± 0.27%) ~ 18.94s 19.07s p=0.470 n=6
Emit Time 1.53s (± 1.42%) 1.52s (± 0.80%) ~ 1.51s 1.54s p=0.867 n=6
Total Time 25.39s (± 0.27%) 25.37s (± 0.25%) ~ 25.30s 25.45s p=0.469 n=6
ts-pre-modules - node (v18.15.0, x64)
Errors 72 72 ~ ~ ~ p=1.000 n=6
Symbols 225,367 226,002 +635 (+ 0.28%) ~ ~ p=0.001 n=6
Types 94,290 94,290 ~ ~ ~ p=1.000 n=6
Memory used 370,202k (± 0.05%) 371,083k (± 0.09%) +882k (+ 0.24%) 370,821k 371,762k p=0.005 n=6
Parse Time 2.83s (± 1.78%) 2.86s (± 0.72%) ~ 2.83s 2.89s p=0.413 n=6
Bind Time 1.60s (± 1.02%) 1.58s (± 0.65%) ~ 1.57s 1.60s p=0.157 n=6
Check Time 16.41s (± 0.39%) 16.45s (± 0.54%) ~ 16.34s 16.58s p=0.520 n=6
Emit Time 0.00s 0.00s ~ ~ ~ p=1.000 n=6
Total Time 20.84s (± 0.35%) 20.89s (± 0.43%) ~ 20.77s 21.00s p=0.375 n=6
vscode - node (v18.15.0, x64)
Errors 5 5 ~ ~ ~ p=1.000 n=6
Symbols 3,948,486 3,948,486 ~ ~ ~ p=1.000 n=6
Types 1,239,521 1,239,521 ~ ~ ~ p=1.000 n=6
Memory used 3,741,034k (± 0.01%) 3,741,057k (± 0.01%) ~ 3,740,702k 3,741,213k p=1.000 n=6
Parse Time 15.24s (± 0.65%) 15.29s (± 0.52%) ~ 15.19s 15.38s p=0.336 n=6
Bind Time 4.97s (± 0.67%) 5.31s (±15.07%) ~ 4.94s 6.94s p=0.376 n=6
Check Time 103.62s (± 4.59%) 102.96s (± 3.47%) ~ 97.72s 107.23s p=0.689 n=6
Emit Time 37.03s (±15.24%) 36.83s (± 7.55%) ~ 31.15s 38.15s p=0.689 n=6
Total Time 160.86s (± 2.48%) 160.38s (± 1.86%) ~ 157.62s 165.42s p=0.936 n=6
webpack - node (v18.15.0, x64)
Errors 40 40 ~ ~ ~ p=1.000 n=6
Symbols 372,686 372,686 ~ ~ ~ p=1.000 n=6
Types 163,203 163,203 ~ ~ ~ p=1.000 n=6
Memory used 529,022k (± 0.02%) 529,067k (± 0.02%) ~ 528,910k 529,168k p=0.471 n=6
Parse Time 4.50s (± 1.21%) 4.51s (± 0.54%) ~ 4.48s 4.55s p=0.686 n=6
Bind Time 1.92s (± 1.12%) 1.95s (± 0.68%) ~ 1.94s 1.97s p=0.056 n=6
Check Time 22.12s (± 0.22%) 22.11s (± 0.61%) ~ 21.90s 22.23s p=0.470 n=6
Emit Time 0.00s 0.00s ~ ~ ~ p=1.000 n=6
Total Time 28.55s (± 0.28%) 28.57s (± 0.42%) ~ 28.37s 28.68s p=0.470 n=6
xstate-main - node (v18.15.0, x64)
Errors 30 30 ~ ~ ~ p=1.000 n=6
Symbols 676,516 677,157 +641 (+ 0.09%) ~ ~ p=0.001 n=6
Types 203,680 203,682 +2 (+ 0.00%) ~ ~ p=0.001 n=6
Memory used 578,415k (± 0.03%) 579,239k (± 0.02%) +824k (+ 0.14%) 579,025k 579,349k p=0.005 n=6
Parse Time 4.20s (± 1.22%) 4.17s (± 0.60%) ~ 4.14s 4.21s p=0.292 n=6
Bind Time 1.34s (± 1.22%) 1.36s (± 0.60%) +0.02s (+ 1.36%) 1.35s 1.37s p=0.039 n=6
Check Time 20.51s (± 2.02%) 20.54s (± 1.81%) ~ 20.26s 21.03s p=0.688 n=6
Emit Time 0.00s 0.00s (±154.76%) ~ 0.00s 0.01s p=0.174 n=6
Total Time 26.05s (± 1.63%) 26.07s (± 1.41%) ~ 25.77s 26.56s p=1.000 n=6
System info unknown
Hosts
  • node (v18.15.0, x64)
Scenarios
  • Compiler-Unions - node (v18.15.0, x64)
  • angular-1 - node (v18.15.0, x64)
  • mui-docs - node (v18.15.0, x64)
  • self-build-src - node (v18.15.0, x64)
  • self-build-src-public-api - node (v18.15.0, x64)
  • self-compiler - node (v18.15.0, x64)
  • ts-pre-modules - node (v18.15.0, x64)
  • vscode - node (v18.15.0, x64)
  • webpack - node (v18.15.0, x64)
  • xstate-main - node (v18.15.0, x64)
Benchmark Name Iterations
Current pr 6
Baseline baseline 6

Developer Information:

Download Benchmarks

@bakkot
Copy link
Contributor

bakkot commented Oct 20, 2025

There are lot of places in the spec where property bags have "at least one of" constraints (eg. either month or monthCode is mandatory for a DateLikeObject), which can't really be expressed.

Well...

type DateLikeObjectBase = {
    year?: number | undefined;
    era?: string | undefined;
    eraYear?: number | undefined;
    month?: number | undefined;
    monthCode?: string | undefined;
    day: number;
    calendar?: string | undefined;
}
type DateLikeObject = DateLikeObjectBase & ({ month: number } | { monthCode: string });

Possibly it's not worth the additional complexity, though.

@typescript-bot
Copy link
Collaborator

@jakebailey Here are the results of running the top 400 repos with tsc comparing main and refs/pull/62628/merge:

Everything looks good!

@jakebailey
Copy link
Member

I know there are existing polyfills for Temporal out there in TS; are these types compatible / based off of those? Or fresh?

@Renegade334
Copy link
Contributor Author

The definitions are de novo, so no licensing issues.

As for compatibility:

  • temporal-polyfill declares a global Temporal namespace with divergent patterns (types instead of interfaces for dictionary definitions, ES6 classes instead of prototype/constructor interfaces, etc.) so will almost certainly never work with any lib.d.ts declaration in the same project.
  • @js-temporal/polyfill doesn't have interassignability with these definitions as things stand, not least due to some lib.d.ts patterns that aren't used there (| undefined with ExOPT, etc) – could run some proper tests if interoperability is a potential objective.

kbrilla added a commit to kbrilla/components that referenced this pull request Jan 17, 2026
- Replace custom TemporalDateType interface with union type of native
  Temporal.PlainDate | Temporal.PlainDateTime | Temporal.ZonedDateTime
- Update temporal.d.ts to match latest TC39 spec (no Calendar class)
- Adapter now uses global Temporal namespace without polyfill dependency
- Polyfill (@js-temporal/polyfill) is only used in tests

This prepares for when TypeScript adds lib.esnext.temporal support.
See: microsoft/TypeScript#62628
kbrilla added a commit to kbrilla/components that referenced this pull request Jan 17, 2026
- Replace custom TemporalDateType interface with union type of native
  Temporal.PlainDate | Temporal.PlainDateTime | Temporal.ZonedDateTime
- Update temporal.d.ts to match TC39 spec and TypeScript PR #62628
  (microsoft/TypeScript#62628)
- Types match MDN documentation and Chrome 144/Firefox 139 implementations
- Add _invalid marker to sentinel object for explicit invalid detection
- Adapter uses global Temporal namespace without polyfill dependency
- Polyfill (@js-temporal/polyfill) is only used in tests
@jakebailey
Copy link
Member

I'm reviewing this PR now; wish me luck...

No custom calendar support, as per the latest spec changes.

MDN I guess hasn't gotten the memo?

@jakebailey
Copy link
Member

One thing this PR is missing are updates to getScriptTargetFeatures, though not strictly required to compile.

@Renegade334 Renegade334 force-pushed the lib-esnext-temporal branch 2 times, most recently from ba7824e to a5ad09f Compare February 7, 2026 00:32
@github-project-automation github-project-automation bot moved this from Not started to Needs merge in PR Backlog Feb 7, 2026
@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented Feb 7, 2026

A few things:

  1. Would love to get docs for these before RC, but not a blocker.
  2. It would be really great if referencing Temporal with the wrong lib set gave you a good error message like referencing Date.toTemporalInstant.
  3. The coercion tests were fine to have, but don't push at this point - I think we are happy with where things are.

@jakebailey jakebailey enabled auto-merge February 7, 2026 01:01
@jakebailey jakebailey added this pull request to the merge queue Feb 7, 2026
Merged via the queue into microsoft:main with commit 5e4281f Feb 7, 2026
23 checks passed
@github-project-automation github-project-automation bot moved this from Needs merge to Done in PR Backlog Feb 7, 2026
@Renegade334
Copy link
Contributor Author

It would be really great if referencing Temporal with the wrong lib set gave you a good error message like referencing Date.toTemporalInstant.

I couldn't get this to work, at least not when the Temporal namespace itself is missing. If anyone else wants to have a look, feel free.

@Renegade334 Renegade334 deleted the lib-esnext-temporal branch February 7, 2026 01:35
nanoseconds?: number | undefined;
}

interface MonthDayLikeObject extends Omit<DateLikeObject, "era" | "eraYear"> {}
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe that this Omit is unnecessary because an era / eraYear pair is always accepted anywhere a year is also accepted. Here's an example from Chrome 144:

Temporal.PlainMonthDay.from({day: 1, month: 1, era: 'ad', eraYear: 2026, calendar: 'gregory'})
// => PlainMonthDay {calendarId: 'gregory', monthCode: 'M01', day: 1}
Temporal.PlainMonthDay.from({day: 1, month: 1, era: 'ad', eraYear: 2026, calendar: 'iso8601'})
// => PlainMonthDay {calendarId: 'iso8601', monthCode: 'M01', day: 1}
Temporal.PlainMonthDay.from({day: 1, month: 1, era: 'ad', eraYear: 2026})
// => PlainMonthDay {calendarId: 'iso8601', monthCode: 'M01', day: 1}

FYI @sffc @ptomato in case you want to weigh in on the differences between ECMA-262 and ECMA-402 around eras.

@justingrant
Copy link
Contributor

justingrant commented Feb 7, 2026

Thanks for landing Temporal types in TS! I wrote the original type declarations for the Temporal proposal many years ago, and it's great to see how the pros do it.

Sorry to find this PR too late (I saw it linked from Rob Palmer's tweet today) before merging. I did a quick review tonight and didn't see any obvious problems except I left one comment about what looks like a small mistake in one type.

There are lot of places in the spec where property bags have "at least one of" constraints (eg. either month or monthCode is mandatory for a DateLikeObject), which can't really be expressed.

Is there a reason why @bakkot's suggestion above (from #62628 (comment)) didn't work? Any date-like object in Temporal requires either month or month code, and "either year or an era / eraYear pair. It'd be a bummer if those requirements can't be enforced at compile time, because they're definitely not optional.

As for compatibility:

  • temporal-polyfill declares a global Temporal namespace with divergent patterns (types instead of interfaces for dictionary definitions, ES6 classes instead of prototype/constructor interfaces, etc.) so will almost certainly never work with any lib.d.ts declaration in the same project.
  • @js-temporal/polyfill doesn't have interassignability with these definitions as things stand, not least due to some lib.d.ts patterns that aren't used there (| undefined with ExOPT, etc) – could run some proper tests if interoperability is a potential objective.

FYI @arshaw (maintainer of temporal-polyfill) and @ptomato @12wrigja (my co-maintainers of js-temporal/polyfill).

@Renegade334 @jakebailey What would you recommend that we should do in the polyfills we maintain so that polyfills are maximally compatible with TS?

@DanielRosenwasser
Copy link
Member

Hey @justingrant (and other friends) thanks for taking a look - and of course for the work you've done on Temporal! People are largely out for the weekend, but we're open to making things more compatible where it makes sense.

I think in some ways this kind of raises a thought I had of whether it might have made sense for one polyfill's declaration files to be contributed as part of lib.d.ts. We didn't really know if that was something that polyfill authors would be open to. We didn't want to be presumptuous, but maybe that is silly in retrospect (or maybe that actually is presumptuous now - sorry if it is 😅).

On the other hand, for a variety of reasons, lib.d.ts declarations are often intentionally a little different, using a "static/instance interface decomposition pattern" so that people can easily use interface merging for new members (e.g. new constructors). At the very least, I think it was a miss on my part to not reach out and get some feedback from you all.

We are planning to go out with the beta pretty soon, and I don't think these are necessarily blockers for shipping 6.0 beta with temporal. We can take some time to iron out the differences after that. Our nightlies tend to have more usage than our beta releases (plus we have an RC release), so I think we'll still get decent feedback there.

@Renegade334
Copy link
Contributor Author

👋!

There are lot of places in the spec where property bags have "at least one of" constraints (eg. either month or monthCode is mandatory for a DateLikeObject), which can't really be expressed.

Is there a reason why bakkot's suggestion above #62628 (comment) didn't work? Any date-like object in Temporal requires either month or month code, and "either year or an era / eraYear pair. It'd be a bummer if those requirements can't be enforced at compile time, because they're definitely not optional.

Just some context from me, to expand on Daniel's comment.

Dictionaries as types in TS's lib.d.ts tend to be problematic because of the incremental nature of TS's built-in libraries. If these end up landing in, say ES2027, and the spec is then extended in a future edition to add to those property bags, then the existing library won't be updated – the new definitions will need to augment the existing ones from a different .d.ts location, which is possible for interfaces but not for types.

There are fudges, but none of them offer perfect forward extensibility.

  • Defining a core interface plus a type union to permute the required/optional relationships (the suggestion referenced) allows for the core interface to be augmented down the line, but those type unions themselves are fixed, so any similar properties with interdependent optionality relationships can't be added in the future.
  • Expressing the type unions in the method definitions (eg. with(item: DateLikeObject & { ... }) would have a similar issue, as even though future overloads can be added to express the new requirements, the old overloads would still exist, and permissively match without any of the new constraints.

Yes, this is precaution around a scenario that doesn't yet exist! I'm not opposed to different approaches, but the one used currently is the one consistent with lib.d.ts as a whole, for those reasons.

@justingrant
Copy link
Contributor

justingrant commented Feb 7, 2026

BTW, we're all so happy that TS is integrating Temporal! Didn't mean anything above to come off as criticism. Y'all are doing great work.

I think in some ways this kind of raises a thought I had of whether it might have made sense for one polyfill's declaration files to be contributed as part of lib.d.ts. We didn't really know if that was something that polyfill authors would be open to. We didn't want to be presumptuous, but maybe that is silly in retrospect (or maybe that actually is presumptuous now - sorry if it is 😅).

There's different conventions in lib.d.ts so I wouldn't expect you'd be able to use a polyfill's types as-is, but I suspect that almost all polyfill maintainers would be happy for you to to using polyfill types as a starting point. It would improve compatibility, make a smoother migration path from polyfill to native usage, and would mean less work from polyfill authors to adapt our own types to match yours.

Also, some proposals now include TS types as part of the proposal itself. In Temporal's case, we built TS types as part of the non-production polyfill we use to run tests. The production polyfill @js-temporal/polyfill just re-uses those types.

Even if you don't re-use anything (which is also fine, I don't think anyone is too picky about this), IMO a good habit would be to tag proposal champions when building the types so we could review and, if needed, comment about usage experience, future directions, etc. as you prioritize various concerns & tradeoffs.

  • Defining a core interface plus a type union to permute the required/optional relationships (the suggestion referenced) allows for the core interface to be augmented down the line, but those type unions themselves are fixed, so any similar properties with interdependent optionality relationships can't be added in the future.
  • Expressing the type unions in the method definitions (eg. with(item: DateLikeObject & { ... }) would have a similar issue, as even though future overloads can be added to express the new requirements, the old overloads would still exist, and permissively match without any of the new constraints.

Yeah, it's a hard problem with no perfect solution. Although I do have two suggestions:

The first is that tsc/tsgo are not the only game in town when it comes to enforcing type-aware usage. Linters usually pick up where TS leaves off, enforcing rules at design time that TS cannot enforce, like regex syntax. In Temporal's case, I assume that ESLint and others will be quick to implement rules for Temporal's "one of month / monthCode" and "one of year / era+eraYear" patterns if TS does not validate them.

But today each linter needs to decide for themselves what beyond-lib.d.ts rules to apply, which means extra work and misalignment across the type-aware ecosystem. What if TS could publish, along with each lib.d.ts update, a clearly-documented list of "constraints we wanted to apply but couldn't because of limitations in TS and/or backwards/forwards-compatibility concerns" ? Doing this would, at very low cost to you, make it easier for the ecosystem to both know what new linter rules are needed, and to align across linters. Also, with reasonably well-written docs, it'd likely be trivial for linter implementers to use AI tools to turn your docs into linter rules.

The second idea is that this problem is not unique to Temporal. I've faced the "interface declaration merging cannot remove members or overloads" many times before, and it's extremely frustrating. For example, I recently had to add type safety to an app using Vuex 3.x. What we wanted to do was to replace Vuex's store property with a type-safe store. But that was impossible due to the limitations of declaration merging, so we had to make a new storeTS property, change hundreds of source files to use it, and then write a custom ESLint rule to complain if anyone tries to use the untyped store property.

If there were a way for a later TS version to allow interface declarations to disable a member of that interface declared elsewhere, then it would in theory solve the forward-compatibility problem and many others. A naive solution could be something like this:

// foo2026.d.ts
interface Foo {
  bar(something: FooBar1): number;
}

// foo2028.d.ts
interface Foo {
  // invalidate member declarations that match exactly
  delete bar(something: FooBar1): number;

  // optionally replace the deleted member with a newer version.
  bar(something: FooBar2): number;
}

I'm not familiar enough to know if something like this would be workable, but satisfying the underlying requirement to disable merged interface members would be quite helpful!

@justingrant
Copy link
Contributor

justingrant commented Feb 8, 2026

No custom calendar support, as per the latest spec changes.

MDN I guess hasn't gotten the memo?

Oh one more thing: where in MDN are you seeing content about custom calendars? We'll want to let them know to remove it. @jakebailey

@jakebailey
Copy link
Member

No custom calendar support, as per the latest spec changes.

MDN I guess hasn't gotten the memo?

Oh one more thing: where in MDN are you seeing content about custom calendars? We'll want to let them know to remove it. @jakebailey

This is my bad; I confused custom calendars with non-Gregorian calendars.

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

Labels

For Uncommitted Bug PR for untriaged, rejected, closed or missing bug

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Add Temporal (Stage 3) types

8 participants