Repository navigation
Fix invalid declaration emit for constants whose value is Infinity or NaN - #64761
Open
Sid Chaudhary (sidcraftscode) wants to merge 1 commit into
Open
Sid Chaudhary (sidcraftscode) wants to merge 1 commit into
Sid Chaudhary (sidcraftscode) wants to merge 1 commit into
Conversation
… NaN A numeric literal that overflows, such as 1e400, has the number literal type Infinity. Declaration emit wrote these constants with their literal value, producing `declare const a = Infinity;`. That is not a valid ambient initializer (TS1254: it must be a string or numeric literal or a literal enum reference), so the emitted .d.ts did not type-check. The same happened for -Infinity, readonly class properties and `export default 1e400`. These declarations now get a `number` type annotation instead. Enum member references whose value is NaN or Infinity are unchanged, since `declare const x = E.A;` is valid. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Author
|
@microsoft-github-policy-service agree |
Copilot started reviewing on behalf of
Sid Chaudhary (sidcraftscode)
October 13, 2026 03:06
View session
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
The focused emit fix correctly handles non-finite values and is supported by comprehensive regression baselines.
0 open findings
What changed in this PR
Fixes invalid declaration emit for non-finite numeric literal values by falling back to number annotations.
Changes:
- Rejects
InfinityandNaNas ambient constant initializers. - Preserves valid enum-member references and finite literals.
- Adds comprehensive compiler regression coverage and baselines.
| File | Description |
|---|---|
tsc/internal/checker/emitresolver.go |
Handles non-finite literal values during declaration emit. |
tsc/testdata/tests/cases/compiler/declarationEmitNonFiniteNumberLiteral.ts |
Adds regression scenarios. |
tsc/testdata/baselines/reference/compiler/declarationEmitNonFiniteNumberLiteral.js |
Captures corrected declaration output. |
tsc/testdata/baselines/reference/compiler/declarationEmitNonFiniteNumberLiteral.types |
Records inferred types. |
tsc/testdata/baselines/reference/compiler/declarationEmitNonFiniteNumberLiteral.symbols |
Records symbol information. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #64760
Problem
A numeric literal that overflows, such as
1e400, has the number literal typeInfinity. Declaration emit writes constants of that type with their literal value, which produces declaration files that don't type-check:Each initializer is an error when the
.d.tsis checked:TS1254: A 'const' initializer in an ambient context must be a string or numeric literal or literal enum reference.Infinityis an identifier, not a literal.This isn't a TS 7 regression. TypeScript 6.0.3 (Playground) emits the same initializers, and
declare const _default: Infinity;for the default export.Fix
In
tsc/internal/checker/emitresolver.go:IsLiteralConstDeclarationreturns false when the declaration's type is a non-enum number literal whose value isInfinity,-InfinityorNaN, so the declaration gets a type annotation instead of an initializer.CreateLiteralConstValuereturns nil for those values. Theexport defaultpath calls it directly and already falls back to a type annotation on nil. This replaces the branches that built theInfinityandNaNidentifiers, which produced exactly the invalid initializers above.Output after the fix:
Enum members with
NaNorInfinityvalues are unaffected (declarationEmitEnumNaN.ts). So are references to them:export const g = E.Astill emits= E.A, which is a valid initializer.An alternative would be to reuse the source text (
= 1e400), which keeps the literal type. It needs a fallback anyway, since the type can come from an expression that isn't a numeric literal. I went withnumberto keep the change small. Happy to switch if you prefer.Related: #42905 (Infinity in type positions, fixed in 7.1) and #63831 (
-1e500 as constemitted as-Infinity). The type-position case in #63831 goes through the node builder and isn't touched here. #56272 discusses whether overflowing literals should have anInfinityliteral type at all. This fix doesn't depend on that outcome: either way, the emitted.d.tsmust type-check.Tests
declarationEmitNonFiniteNumberLiteral.ts. It coversconst, negative values, the smallest overflowing literal (1.7976931348623159e308), a large finite literal that must keep its value (1e308),static readonlyandreadonlyproperties,export default, and references to NaN and Infinity enum members. Without the fix, the harness's.d.tsre-check records 6 TS1254 errors (//// [DtsFileErrors]). With it, there are none.declarationEmit*, enum,fakeInfinity1andnanEqualitycompiler test is unchanged (599 tests).go test ./...intscpasses: 64 packages, no failures (golang:1-bookworm in Docker).internal/testrunnerwas run on its own with-parallel 4, because the Docker VM's 4 GB ran o