Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions tsc/internal/checker/emitresolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -1019,9 +1019,12 @@ func (r *EmitResolver) CreateLiteralConstValue(emitContext *printer.EmitContext,
case jsnum.Number:
if value.IsInf() {
if value > 0 {
return emitContext.Factory.NewIdentifier("Infinity")
return emitContext.Factory.NewNumericLiteral(jsnum.InfinityLiteralText, ast.TokenFlagsNone)
}
return emitContext.Factory.NewPrefixUnaryExpression(ast.KindMinusToken, emitContext.Factory.NewIdentifier("Infinity"))
return emitContext.Factory.NewPrefixUnaryExpression(
ast.KindMinusToken,
emitContext.Factory.NewNumericLiteral(jsnum.InfinityLiteralText, ast.TokenFlagsNone),
)
}
if value.IsNaN() {
return emitContext.Factory.NewIdentifier("NaN")
Expand Down
6 changes: 6 additions & 0 deletions tsc/internal/checker/nodebuilder_hover.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ func (b *NodeBuilderImpl) enumMemberInitializer(p *ast.Symbol) *ast.Node {
case string:
return b.f.NewStringLiteral(v, 0)
case jsnum.Number:
if v.IsInf() {
if v < 0 {
return b.f.NewPrefixUnaryExpression(ast.KindMinusToken, b.f.NewNumericLiteral(jsnum.InfinityLiteralText, 0))
}
return b.f.NewNumericLiteral(jsnum.InfinityLiteralText, 0)
}
return b.f.NewNumericLiteral(v.String(), 0)
}
return nil
Expand Down
14 changes: 10 additions & 4 deletions tsc/internal/checker/nodebuilderimpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -3311,11 +3311,17 @@ func (b *NodeBuilderImpl) typeToTypeNode(t *Type) *ast.TypeNode {
}
if t.flags&TypeFlagsNumberLiteral != 0 {
value := t.AsLiteralType().value.(jsnum.Number)
b.ctx.approximateLength += len(value.String())
if value < 0 {
return b.f.NewLiteralTypeNode(b.f.NewPrefixUnaryExpression(ast.KindMinusToken, b.f.NewNumericLiteral(value.String()[1:], ast.TokenFlagsNone)))
negative := value < 0
text := value.Abs().String()
if value.IsInf() {
text = jsnum.InfinityLiteralText
}
b.ctx.approximateLength += len(text)
if negative {
b.ctx.approximateLength++
return b.f.NewLiteralTypeNode(b.f.NewPrefixUnaryExpression(ast.KindMinusToken, b.f.NewNumericLiteral(text, ast.TokenFlagsNone)))
} else {
return b.f.NewLiteralTypeNode(b.f.NewNumericLiteral(value.String(), ast.TokenFlagsNone))
return b.f.NewLiteralTypeNode(b.f.NewNumericLiteral(text, ast.TokenFlagsNone))
}
}
if t.flags&TypeFlagsBigIntLiteral != 0 {
Expand Down
21 changes: 21 additions & 0 deletions tsc/internal/checker/nodecopy.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import (

"github.com/microsoft/TypeScript/tsc/internal/ast"
"github.com/microsoft/TypeScript/tsc/internal/core"
"github.com/microsoft/TypeScript/tsc/internal/jsnum"
"github.com/microsoft/TypeScript/tsc/internal/nodebuilder"
"github.com/microsoft/TypeScript/tsc/internal/printer"
"github.com/microsoft/TypeScript/tsc/internal/scanner"
)

func (b *NodeBuilderImpl) reuseNode(node *ast.Node) *ast.Node {
Expand All @@ -26,6 +28,14 @@ func (b *NodeBuilderImpl) reuseName(node *ast.Node, isMethod bool) *ast.Node {
if res == nil {
return res
}
if ast.IsComputedPropertyName(node) && ast.IsComputedPropertyName(res) {
sourceLiteral := numericLiteralOperand(node.AsComputedPropertyName().Expression)
reusedLiteral := numericLiteralOperand(res.AsComputedPropertyName().Expression)
if sourceLiteral != nil && reusedLiteral != nil && jsnum.FromString(sourceLiteral.Text()).IsInf() {
sourceFile := ast.GetSourceFileOfNode(sourceLiteral)
reusedLiteral.LiteralLikeData().Text = scanner.GetSourceTextOfNodeFromSourceFile(sourceFile, sourceLiteral, false)
}
}

text, ok := ast.TryGetTextOfPropertyName(res)
if !ok {
Expand Down Expand Up @@ -53,6 +63,17 @@ func (b *NodeBuilderImpl) reuseName(node *ast.Node, isMethod bool) *ast.Node {
return b.setTextRange(renamed, res)
}

func numericLiteralOperand(node *ast.Node) *ast.Node {
node = ast.SkipParentheses(node)
if ast.IsPrefixUnaryExpression(node) {
node = ast.SkipParentheses(node.AsPrefixUnaryExpression().Operand)
}
if ast.IsNumericLiteral(node) {
return node
}
return nil
}

func (b *NodeBuilderImpl) reuseTypeNode(node *ast.Node) *ast.Node {
if node == nil {
return node
Expand Down
32 changes: 31 additions & 1 deletion tsc/internal/checker/pseudotypenodebuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package checker
import (
"github.com/microsoft/TypeScript/tsc/internal/ast"
"github.com/microsoft/TypeScript/tsc/internal/debug"
"github.com/microsoft/TypeScript/tsc/internal/jsnum"
"github.com/microsoft/TypeScript/tsc/internal/nodebuilder"
"github.com/microsoft/TypeScript/tsc/internal/printer"
"github.com/microsoft/TypeScript/tsc/internal/pseudochecker"
"github.com/microsoft/TypeScript/tsc/internal/scanner"
)

// pseudoTypeToNodeWithCheckerFallback is like pseudoTypeToNode but when the top-level pseudo type
Expand Down Expand Up @@ -317,13 +319,41 @@ func (b *NodeBuilderImpl) pseudoTypeToNode(t *pseudochecker.PseudoType) *ast.Nod
return result
case pseudochecker.PseudoTypeKindStringLiteral, pseudochecker.PseudoTypeKindNumericLiteral, pseudochecker.PseudoTypeKindBigIntLiteral:
source := t.AsPseudoTypeLiteral().Node
return b.f.NewLiteralTypeNode(b.reuseNode(source))
reused := b.reuseNode(source)
if t.Kind == pseudochecker.PseudoTypeKindNumericLiteral {
sourceLiteral := source
reusedLiteral := reused
if ast.IsPrefixUnaryExpression(source) {
sourceLiteral = source.AsPrefixUnaryExpression().Operand
reusedLiteral = reused.AsPrefixUnaryExpression().Operand
}
if isDirectlyConstAssertedNumericLiteral(source) {
// Numeric literal text stores the rounded value, so recover the source spelling for reused const assertions.
sourceFile := ast.GetSourceFileOfNode(sourceLiteral)
reusedLiteral.LiteralLikeData().Text = scanner.GetSourceTextOfNodeFromSourceFile(sourceFile, sourceLiteral, false)
} else if jsnum.FromString(sourceLiteral.Text()).IsInf() {
reusedLiteral.LiteralLikeData().Text = jsnum.InfinityLiteralText
}
}
return b.f.NewLiteralTypeNode(reused)
default:
debug.AssertNever(t.Kind, "Unhandled pseudotype kind in pseudotype node construction")
return nil
}
}

func isDirectlyConstAssertedNumericLiteral(node *ast.Node) bool {
for parent := node.Parent; parent != nil; parent = parent.Parent {
if ast.IsConstAssertion(parent) {
return true
}
if !ast.IsPrefixUnaryExpression(parent) && !ast.IsParenthesizedExpression(parent) {
return false
}
}
return false
}

func (b *NodeBuilderImpl) pseudoParametersToNodeList(params []*pseudochecker.PseudoParameter) *ast.NodeList {
res := make([]*ast.Node, 0, len(params))
for _, p := range params {
Expand Down
2 changes: 2 additions & 0 deletions tsc/internal/jsnum/string.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (
"github.com/microsoft/TypeScript/tsc/internal/stringutil"
)

const InfinityLiteralText = "1e999"

// https://tc39.es/ecma262/2024/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-tostring
func (n Number) String() string {
switch {
Expand Down
8 changes: 6 additions & 2 deletions tsc/internal/ls/completions.go
Original file line number Diff line number Diff line change
Expand Up @@ -6636,10 +6636,14 @@ func (l *LanguageService) getExhaustiveCaseSnippets(
elements = append(elements, bigInt)
case jsnum.Number:
var number *ast.Node
text := v.Abs().String()
if v.IsInf() {
text = jsnum.InfinityLiteralText
}
if v < 0 {
number = factory.NewPrefixUnaryExpression(ast.KindMinusToken, factory.NewNumericLiteral(v.Abs().String(), ast.TokenFlagsNone))
number = factory.NewPrefixUnaryExpression(ast.KindMinusToken, factory.NewNumericLiteral(text, ast.TokenFlagsNone))
} else {
number = factory.NewNumericLiteral(v.String(), ast.TokenFlagsNone)
number = factory.NewNumericLiteral(text, ast.TokenFlagsNone)
}
elements = append(elements, number)
case string:
Expand Down
4 changes: 2 additions & 2 deletions tsc/internal/transformers/declarations/transform.go
Original file line number Diff line number Diff line change
Expand Up @@ -2286,9 +2286,9 @@ func (tx *DeclarationTransformer) transformEnumDeclaration(input *ast.EnumDeclar
case jsnum.Number:
if value.IsInf() {
if value > 0 {
newInitializer = tx.Factory().NewIdentifier("Infinity")
newInitializer = tx.Factory().NewNumericLiteral(jsnum.InfinityLiteralText, ast.TokenFlagsNone)
} else {
newInitializer = tx.Factory().NewPrefixUnaryExpression(ast.KindMinusToken, tx.Factory().NewIdentifier("Infinity"))
newInitializer = tx.Factory().NewPrefixUnaryExpression(ast.KindMinusToken, tx.Factory().NewNumericLiteral(jsnum.InfinityLiteralText, ast.TokenFlagsNone))
}
} else if value.IsNaN() {
newInitializer = tx.Factory().NewIdentifier("NaN")
Expand Down
4 changes: 2 additions & 2 deletions tsc/internal/transformers/inliners/constenum.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ func (tx *ConstEnumInliningTransformer) visit(node *ast.Node) *ast.Node {
case jsnum.Number:
if v.IsInf() {
if v.Abs() == v {
replacement = tx.Factory().NewIdentifier("Infinity")
replacement = tx.Factory().NewNumericLiteral(jsnum.InfinityLiteralText, ast.TokenFlagsNone)
} else {
replacement = tx.Factory().NewPrefixUnaryExpression(ast.KindMinusToken, tx.Factory().NewIdentifier("Infinity"))
replacement = tx.Factory().NewPrefixUnaryExpression(ast.KindMinusToken, tx.Factory().NewNumericLiteral(jsnum.InfinityLiteralText, ast.TokenFlagsNone))
}
} else if v.IsNaN() {
replacement = tx.Factory().NewIdentifier("NaN")
Expand Down
4 changes: 2 additions & 2 deletions tsc/internal/transformers/tstransforms/utilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ func constantExpression(value any, factory *printer.NodeFactory) *ast.Expression
case jsnum.Number:
if value.IsInf() {
if value > 0 {
return factory.NewIdentifier("Infinity")
return factory.NewNumericLiteral(jsnum.InfinityLiteralText, ast.TokenFlagsNone)
}
return factory.NewPrefixUnaryExpression(ast.KindMinusToken, factory.NewIdentifier("Infinity"))
return factory.NewPrefixUnaryExpression(ast.KindMinusToken, factory.NewNumericLiteral(jsnum.InfinityLiteralText, ast.TokenFlagsNone))
}
if value.IsNaN() {
return factory.NewIdentifier("NaN")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
constEnumInfinityShadow.ts(4,13): error TS2477: 'const' enum member initializer was evaluated to a non-finite value.


==== constEnumInfinityShadow.ts (1 errors) ====
const Infinity = 0;

const enum E {
value = 1e999,
~~~~~
!!! error TS2477: 'const' enum member initializer was evaluated to a non-finite value.
}

export const value = E.value;

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//// [tests/cases/compiler/constEnumInfinityShadow.ts] ////

//// [constEnumInfinityShadow.ts]
const Infinity = 0;

const enum E {
value = 1e999,
}

export const value = E.value;


//// [constEnumInfinityShadow.js]
const Infinity = 0;
export const value = 1e999 /* E.value */;


//// [constEnumInfinityShadow.d.ts]
declare const enum E {
value = 1e999
}
export declare const value = E.value;
export {};
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//// [tests/cases/compiler/constEnumInfinityShadow.ts] ////

=== constEnumInfinityShadow.ts ===
const Infinity = 0;
>Infinity : Symbol(Infinity, Decl(constEnumInfinityShadow.ts, 0, 5))

const enum E {
>E : Symbol(E, Decl(constEnumInfinityShadow.ts, 0, 19))

value = 1e999,
>value : Symbol(E.value, Decl(constEnumInfinityShadow.ts, 2, 14))
}

export const value = E.value;
>value : Symbol(value, Decl(constEnumInfinityShadow.ts, 6, 12))
>E.value : Symbol(E.value, Decl(constEnumInfinityShadow.ts, 2, 14))
>E : Symbol(E, Decl(constEnumInfinityShadow.ts, 0, 19))
>value : Symbol(E.value, Decl(constEnumInfinityShadow.ts, 2, 14))

Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//// [tests/cases/compiler/constEnumInfinityShadow.ts] ////

=== constEnumInfinityShadow.ts ===
const Infinity = 0;
>Infinity : 0
>0 : 0

const enum E {
>E : E

value = 1e999,
>value : E.value
>1e999 : 1e999
}

export const value = E.value;
>value : E.value
>E.value : E
>E : typeof E
>value : E

Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ export declare enum E {
export declare enum E {
A = NaN,
B = NaN,
C = Infinity,
D = -Infinity
C = 1e999,
D = -1e999
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//// [tests/cases/compiler/declarationEmitNegativeNumericConstLiterals.ts] ////

//// [declarationEmitNegativeNumericConstLiterals.ts]
declare function id<const T>(value: T): T;

export const a = -1e500 as const;
export const b = -123456789012345678901234567890 as const;
export const c = ((-1e500)) as const;
export const d = { value: -1e500 } as const;
export const e = [-1e500] as const;
export const f = id(-1e500);
export const g = -1e500;
export const h = 1e500;
export const i = { [-1e500]: 1 } as const;


//// [declarationEmitNegativeNumericConstLiterals.js]
export const a = -1e500;
export const b = -123456789012345678901234567890;
export const c = ((-1e500));
export const d = { value: -1e500 };
export const e = [-1e500];
export const f = id(-1e500);
export const g = -1e500;
export const h = 1e500;
export const i = { [-1e500]: 1 };


//// [declarationEmitNegativeNumericConstLiterals.d.ts]
export declare const a: -1e500;
export declare const b: -123456789012345678901234567890;
export declare const c: -1e500;
export declare const d: {
readonly value: -1e999;
};
export declare const e: readonly [-1e999];
export declare const f: -1e999;
export declare const g = -1e999;
export declare const h = 1e999;
export declare const i: {
readonly [-1e500]: 1;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//// [tests/cases/compiler/declarationEmitNegativeNumericConstLiterals.ts] ////

=== declarationEmitNegativeNumericConstLiterals.ts ===
declare function id<const T>(value: T): T;
>id : Symbol(id, Decl(declarationEmitNegativeNumericConstLiterals.ts, 0, 0))
>T : Symbol(T, Decl(declarationEmitNegativeNumericConstLiterals.ts, 0, 20))
>value : Symbol(value, Decl(declarationEmitNegativeNumericConstLiterals.ts, 0, 29))
>T : Symbol(T, Decl(declarationEmitNegativeNumericConstLiterals.ts, 0, 20))
>T : Symbol(T, Decl(declarationEmitNegativeNumericConstLiterals.ts, 0, 20))

export const a = -1e500 as const;
>a : Symbol(a, Decl(declarationEmitNegativeNumericConstLiterals.ts, 2, 12))
>const : Symbol(const)

export const b = -123456789012345678901234567890 as const;
>b : Symbol(b, Decl(declarationEmitNegativeNumericConstLiterals.ts, 3, 12))
>const : Symbol(const)

export const c = ((-1e500)) as const;
>c : Symbol(c, Decl(declarationEmitNegativeNumericConstLiterals.ts, 4, 12))
>const : Symbol(const)

export const d = { value: -1e500 } as const;
>d : Symbol(d, Decl(declarationEmitNegativeNumericConstLiterals.ts, 5, 12))
>value : Symbol(value, Decl(declarationEmitNegativeNumericConstLiterals.ts, 5, 18))
>const : Symbol(const)

export const e = [-1e500] as const;
>e : Symbol(e, Decl(declarationEmitNegativeNumericConstLiterals.ts, 6, 12))
>const : Symbol(const)

export const f = id(-1e500);
>f : Symbol(f, Decl(declarationEmitNegativeNumericConstLiterals.ts, 7, 12))
>id : Symbol(id, Decl(declarationEmitNegativeNumericConstLiterals.ts, 0, 0))

export const g = -1e500;
>g : Symbol(g, Decl(declarationEmitNegativeNumericConstLiterals.ts, 8, 12))

export const h = 1e500;
>h : Symbol(h, Decl(declarationEmitNegativeNumericConstLiterals.ts, 9, 12))

export const i = { [-1e500]: 1 } as const;
>i : Symbol(i, Decl(declarationEmitNegativeNumericConstLiterals.ts, 10, 12))
>[-1e500] : Symbol([-1e500], Decl(declarationEmitNegativeNumericConstLiterals.ts, 10, 18))
>const : Symbol(const)

Loading