typia 14.0.3 → 14.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/native/cmd/ttsc-typia/bigint_literal_precision_transform_test.go +135 -0
- package/native/cmd/ttsc-typia/recursive_container_helper_index_transform_test.go +206 -0
- package/native/cmd/ttsc-typia/reflect_literals_bigint_transform_test.go +148 -0
- package/native/cmd/ttsc-typia/reflect_literals_non_literal_rejection_transform_test.go +24 -18
- package/native/cmd/ttsc-typia/reflect_schema_bigint_transform_test.go +173 -0
- package/native/cmd/ttsc-typia/wrong_type_tag_target_diagnostic_test.go +88 -0
- package/native/core/factories/ExpressionFactory.go +11 -1
- package/native/core/factories/LiteralFactory.go +3 -0
- package/native/core/factories/MetadataTypeTagFactory.go +27 -6
- package/native/core/factories/internal/metadata/iterate_metadata_constant.go +3 -0
- package/native/core/programmers/RandomProgrammer.go +10 -4
- package/native/core/programmers/internal/CheckerProgrammer.go +14 -6
- package/native/core/programmers/json/JsonStringifyProgrammer.go +14 -6
- package/native/core/programmers/notations/NotationGeneralProgrammer.go +14 -6
- package/native/core/programmers/plain/PlainClassifyProgrammer.go +14 -6
- package/native/core/programmers/plain/PlainCloneProgrammer.go +14 -6
- package/native/core/programmers/plain/PlainPruneProgrammer.go +30 -10
- package/native/core/programmers/reflect/ReflectLiteralsProgrammer.go +10 -27
- package/native/core/schemas/metadata/MetadataBigint.go +37 -0
- package/native/core/schemas/metadata/metadata_bigint_is_comparable_test.go +65 -0
- package/native/transform/features/reflect/ReflectSchemaTransformer.go +15 -46
- package/package.json +3 -3
- package/native/cmd/ttsc-typia/reflect_literals_empty_union_transform_test.go +0 -161
|
@@ -96,12 +96,16 @@ func plainCloneProgrammer_write_array_functions(props struct {
|
|
|
96
96
|
}) []*shimast.Node {
|
|
97
97
|
f := nativecontext.EmitFactoryOf(plainCloneProgrammer_factory, props.Context.Emit)
|
|
98
98
|
output := []*shimast.Node{}
|
|
99
|
-
for
|
|
99
|
+
for _, typ := range props.Collection.Arrays() {
|
|
100
100
|
if typ.Recursive == false {
|
|
101
101
|
continue
|
|
102
102
|
}
|
|
103
|
+
index := 0
|
|
104
|
+
if typ.Index != nil {
|
|
105
|
+
index = *typ.Index
|
|
106
|
+
}
|
|
103
107
|
output = append(output, nativefactories.StatementFactory.Constant(nativefactories.StatementFactory_ConstantProps{
|
|
104
|
-
Name: fmt.Sprintf("%sa%d", props.Config.Prefix,
|
|
108
|
+
Name: fmt.Sprintf("%sa%d", props.Config.Prefix, index),
|
|
105
109
|
Value: f.NewArrowFunction(
|
|
106
110
|
nil,
|
|
107
111
|
nil,
|
|
@@ -114,7 +118,7 @@ func plainCloneProgrammer_write_array_functions(props struct {
|
|
|
114
118
|
nil,
|
|
115
119
|
f.NewToken(shimast.KindEqualsGreaterThanToken),
|
|
116
120
|
nativeinternal.FeatureProgrammer.VisitGuardRebuild(
|
|
117
|
-
nativeinternal.FeatureProgrammer.VisitKey(props.Config.Prefix, "a",
|
|
121
|
+
nativeinternal.FeatureProgrammer.VisitKey(props.Config.Prefix, "a", index),
|
|
118
122
|
true,
|
|
119
123
|
plainCloneProgrammer_decode_array_inline(plainCloneProgrammer_decodeArrayProps{
|
|
120
124
|
Context: props.Context,
|
|
@@ -148,12 +152,16 @@ func plainCloneProgrammer_write_tuple_functions(props struct {
|
|
|
148
152
|
}) []*shimast.Node {
|
|
149
153
|
f := nativecontext.EmitFactoryOf(plainCloneProgrammer_factory, props.Context.Emit)
|
|
150
154
|
output := []*shimast.Node{}
|
|
151
|
-
for
|
|
155
|
+
for _, tuple := range props.Collection.Tuples() {
|
|
152
156
|
if tuple.Recursive == false {
|
|
153
157
|
continue
|
|
154
158
|
}
|
|
159
|
+
index := 0
|
|
160
|
+
if tuple.Index != nil {
|
|
161
|
+
index = *tuple.Index
|
|
162
|
+
}
|
|
155
163
|
output = append(output, nativefactories.StatementFactory.Constant(nativefactories.StatementFactory_ConstantProps{
|
|
156
|
-
Name: fmt.Sprintf("%st%d", props.Config.Prefix,
|
|
164
|
+
Name: fmt.Sprintf("%st%d", props.Config.Prefix, index),
|
|
157
165
|
Value: f.NewArrowFunction(
|
|
158
166
|
nil,
|
|
159
167
|
nil,
|
|
@@ -166,7 +174,7 @@ func plainCloneProgrammer_write_tuple_functions(props struct {
|
|
|
166
174
|
nil,
|
|
167
175
|
f.NewToken(shimast.KindEqualsGreaterThanToken),
|
|
168
176
|
nativeinternal.FeatureProgrammer.VisitGuardRebuild(
|
|
169
|
-
nativeinternal.FeatureProgrammer.VisitKey(props.Config.Prefix, "t",
|
|
177
|
+
nativeinternal.FeatureProgrammer.VisitKey(props.Config.Prefix, "t", index),
|
|
170
178
|
true,
|
|
171
179
|
plainCloneProgrammer_decode_tuple_inline(plainCloneProgrammer_decodeTupleInlineProps{
|
|
172
180
|
Context: props.Context,
|
|
@@ -93,12 +93,16 @@ func plainPruneProgrammer_write_array_functions(props struct {
|
|
|
93
93
|
}) []*shimast.Node {
|
|
94
94
|
f := nativecontext.EmitFactoryOf(plainPruneProgrammer_factory, props.Context.Emit)
|
|
95
95
|
output := []*shimast.Node{}
|
|
96
|
-
for
|
|
96
|
+
for _, typ := range props.Collection.Arrays() {
|
|
97
97
|
if typ.Recursive == false {
|
|
98
98
|
continue
|
|
99
99
|
}
|
|
100
|
+
index := 0
|
|
101
|
+
if typ.Index != nil {
|
|
102
|
+
index = *typ.Index
|
|
103
|
+
}
|
|
100
104
|
output = append(output, nativefactories.StatementFactory.Constant(nativefactories.StatementFactory_ConstantProps{
|
|
101
|
-
Name: fmt.Sprintf("%sa%d", props.Config.Prefix,
|
|
105
|
+
Name: fmt.Sprintf("%sa%d", props.Config.Prefix, index),
|
|
102
106
|
Value: f.NewArrowFunction(
|
|
103
107
|
nil,
|
|
104
108
|
nil,
|
|
@@ -111,7 +115,7 @@ func plainPruneProgrammer_write_array_functions(props struct {
|
|
|
111
115
|
nil,
|
|
112
116
|
f.NewToken(shimast.KindEqualsGreaterThanToken),
|
|
113
117
|
nativeinternal.FeatureProgrammer.VisitGuardSkip(
|
|
114
|
-
nativeinternal.FeatureProgrammer.VisitKey(props.Config.Prefix, "a",
|
|
118
|
+
nativeinternal.FeatureProgrammer.VisitKey(props.Config.Prefix, "a", index),
|
|
115
119
|
plainPruneProgrammer_decode_array_inline(plainPruneProgrammer_decodeArrayProps{
|
|
116
120
|
Context: props.Context,
|
|
117
121
|
Config: props.Config,
|
|
@@ -144,12 +148,16 @@ func plainPruneProgrammer_write_tuple_functions(props struct {
|
|
|
144
148
|
}) []*shimast.Node {
|
|
145
149
|
f := nativecontext.EmitFactoryOf(plainPruneProgrammer_factory, props.Context.Emit)
|
|
146
150
|
output := []*shimast.Node{}
|
|
147
|
-
for
|
|
151
|
+
for _, tuple := range props.Collection.Tuples() {
|
|
148
152
|
if tuple.Recursive == false {
|
|
149
153
|
continue
|
|
150
154
|
}
|
|
155
|
+
index := 0
|
|
156
|
+
if tuple.Index != nil {
|
|
157
|
+
index = *tuple.Index
|
|
158
|
+
}
|
|
151
159
|
output = append(output, nativefactories.StatementFactory.Constant(nativefactories.StatementFactory_ConstantProps{
|
|
152
|
-
Name: fmt.Sprintf("%st%d", props.Config.Prefix,
|
|
160
|
+
Name: fmt.Sprintf("%st%d", props.Config.Prefix, index),
|
|
153
161
|
Value: f.NewArrowFunction(
|
|
154
162
|
nil,
|
|
155
163
|
nil,
|
|
@@ -162,7 +170,7 @@ func plainPruneProgrammer_write_tuple_functions(props struct {
|
|
|
162
170
|
nil,
|
|
163
171
|
f.NewToken(shimast.KindEqualsGreaterThanToken),
|
|
164
172
|
nativeinternal.FeatureProgrammer.VisitGuardSkip(
|
|
165
|
-
nativeinternal.FeatureProgrammer.VisitKey(props.Config.Prefix, "t",
|
|
173
|
+
nativeinternal.FeatureProgrammer.VisitKey(props.Config.Prefix, "t", index),
|
|
166
174
|
plainPruneProgrammer_decode_tuple_inline(plainPruneProgrammer_decodeTupleInlineProps{
|
|
167
175
|
Context: props.Context,
|
|
168
176
|
Config: props.Config,
|
|
@@ -805,6 +813,14 @@ func plainPruneProgrammer_initializer(props nativeinternal.FeatureProgrammer_Ini
|
|
|
805
813
|
}
|
|
806
814
|
|
|
807
815
|
func plainPruneProgrammer_filter(metadata *schemametadata.MetadataSchema) bool {
|
|
816
|
+
return plainPruneProgrammer_filter_visited(metadata, map[*schemametadata.MetadataSchema]bool{})
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
func plainPruneProgrammer_filter_visited(metadata *schemametadata.MetadataSchema, visited map[*schemametadata.MetadataSchema]bool) bool {
|
|
820
|
+
if visited[metadata] {
|
|
821
|
+
return false
|
|
822
|
+
}
|
|
823
|
+
visited[metadata] = true
|
|
808
824
|
if metadata.Any {
|
|
809
825
|
return false
|
|
810
826
|
}
|
|
@@ -812,12 +828,12 @@ func plainPruneProgrammer_filter(metadata *schemametadata.MetadataSchema) bool {
|
|
|
812
828
|
return true
|
|
813
829
|
}
|
|
814
830
|
for _, tuple := range metadata.Tuples {
|
|
815
|
-
if
|
|
831
|
+
if plainPruneProgrammer_tuple_filter_visited(tuple, visited) {
|
|
816
832
|
return true
|
|
817
833
|
}
|
|
818
834
|
}
|
|
819
835
|
for _, array := range metadata.Arrays {
|
|
820
|
-
if
|
|
836
|
+
if plainPruneProgrammer_filter_visited(array.Type.Value, visited) {
|
|
821
837
|
return true
|
|
822
838
|
}
|
|
823
839
|
}
|
|
@@ -825,11 +841,15 @@ func plainPruneProgrammer_filter(metadata *schemametadata.MetadataSchema) bool {
|
|
|
825
841
|
}
|
|
826
842
|
|
|
827
843
|
func plainPruneProgrammer_tuple_filter(tuple *schemametadata.MetadataTuple) bool {
|
|
844
|
+
return plainPruneProgrammer_tuple_filter_visited(tuple, map[*schemametadata.MetadataSchema]bool{})
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
func plainPruneProgrammer_tuple_filter_visited(tuple *schemametadata.MetadataTuple, visited map[*schemametadata.MetadataSchema]bool) bool {
|
|
828
848
|
return len(tuple.Type.Elements) != 0 && plainPruneProgrammer_some_schema(tuple.Type.Elements, func(elem *schemametadata.MetadataSchema) bool {
|
|
829
849
|
if elem.Rest != nil {
|
|
830
|
-
return
|
|
850
|
+
return plainPruneProgrammer_filter_visited(elem.Rest, visited)
|
|
831
851
|
}
|
|
832
|
-
return
|
|
852
|
+
return plainPruneProgrammer_filter_visited(elem, visited)
|
|
833
853
|
})
|
|
834
854
|
}
|
|
835
855
|
|
|
@@ -29,24 +29,12 @@ func (reflectLiteralsProgrammerNamespace) Write(props ReflectLiteralsProgrammer_
|
|
|
29
29
|
Escape: true,
|
|
30
30
|
Constant: true,
|
|
31
31
|
Absorb: true,
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
// instead of collapsing onto `NO`.
|
|
39
|
-
//
|
|
40
|
-
// Equal counts with no member at all is an uninhabited argument: the
|
|
41
|
-
// `never` keyword, an alias or `Exclude`/`Extract` that filters
|
|
42
|
-
// everything away, or an intersection whose every distributed member
|
|
43
|
-
// prunes to never. Its literal set is empty and
|
|
44
|
-
// `literals<never>(): never[]` has exactly one inhabitant, so it emits
|
|
45
|
-
// `[]` rather than a diagnostic (issue #2377). `undefined` and `void`
|
|
46
|
-
// coalesce to the same empty metadata, but neither satisfies the public
|
|
47
|
-
// `T extends Atomic.Type | null` bound, and typia refuses to transform
|
|
48
|
-
// at all without `strictNullChecks` (`transform/transform.go`), so
|
|
49
|
-
// neither reaches this predicate.
|
|
32
|
+
// Only a constant literal, the `boolean` atomic (which stands for the
|
|
33
|
+
// `true | false` constants), and `null` beside them are members this
|
|
34
|
+
// operation can hand back. Anything else -- an unconstrained atomic, a
|
|
35
|
+
// template, a bucket of any other kind -- is rejected, and so is an
|
|
36
|
+
// argument carrying no member at all: `never` has no literal to list, so
|
|
37
|
+
// it is a compile error rather than an empty array (issue #2377).
|
|
50
38
|
Validate: func(next struct {
|
|
51
39
|
Metadata *nativemetadata.MetadataSchema
|
|
52
40
|
Explore nativefactories.MetadataFactory_IExplore
|
|
@@ -61,18 +49,13 @@ func (reflectLiteralsProgrammerNamespace) Write(props ReflectLiteralsProgrammer_
|
|
|
61
49
|
length++
|
|
62
50
|
}
|
|
63
51
|
}
|
|
64
|
-
size := next.Metadata.Size()
|
|
65
|
-
if next.Metadata.Nullable {
|
|
66
|
-
length++
|
|
67
|
-
size++
|
|
68
|
-
}
|
|
69
|
-
if size == length {
|
|
70
|
-
return []string{}
|
|
71
|
-
}
|
|
72
52
|
if length == 0 {
|
|
73
53
|
return []string{string(ReflectLiteralsProgrammer_ErrorMessages_NO)}
|
|
74
54
|
}
|
|
75
|
-
|
|
55
|
+
if next.Metadata.Size() != length {
|
|
56
|
+
return []string{string(ReflectLiteralsProgrammer_ErrorMessages_ONLY)}
|
|
57
|
+
}
|
|
58
|
+
return []string{}
|
|
76
59
|
},
|
|
77
60
|
},
|
|
78
61
|
Components: nativemetadata.NewMetadataCollection(),
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
package metadata
|
|
2
|
+
|
|
3
|
+
// MetadataBigint is the value of a `bigint` constant.
|
|
4
|
+
//
|
|
5
|
+
// typescript-go reports a bigint literal as a `jsnum.PseudoBigInt`, a struct in
|
|
6
|
+
// an internal package the shim does not re-export. Nothing downstream could
|
|
7
|
+
// name it, so consumers that had to render the value reflected its fields
|
|
8
|
+
// instead and emitted `{ base10Value: "2", negative: false }` where the caller
|
|
9
|
+
// declared `bigint`. This is the nameable stand-in.
|
|
10
|
+
//
|
|
11
|
+
// It is a comparable struct on purpose. Every other value a
|
|
12
|
+
// `MetadataConstantValue` carries -- `string`, `bool`, the number -- is
|
|
13
|
+
// comparable, and the factories compare those values with `==`; the
|
|
14
|
+
// intersection tag assigner is one such site. A pointer type such as
|
|
15
|
+
// `*math/big.Int` would compare identity there and, for bigints alone,
|
|
16
|
+
// silently drop whatever the comparison decides.
|
|
17
|
+
type MetadataBigint struct {
|
|
18
|
+
// Text is the exact value in base 10, prefixed with `-` when negative. A
|
|
19
|
+
// bigint exists to hold what a float64 cannot, so the digits are the only
|
|
20
|
+
// representation that stays exact at every magnitude.
|
|
21
|
+
Text string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// String renders the base-10 digits, which is what every consumer that lowers a
|
|
25
|
+
// bigint into emitted code reads through `fmt.Sprint`.
|
|
26
|
+
func (obj MetadataBigint) String() string {
|
|
27
|
+
return obj.Text
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// MarshalJSON writes the digits unquoted, so metadata marshaled by a
|
|
31
|
+
// downstream tool carries the same JSON number a plain integer would.
|
|
32
|
+
func (obj MetadataBigint) MarshalJSON() ([]byte, error) {
|
|
33
|
+
if obj.Text == "" {
|
|
34
|
+
return []byte("0"), nil
|
|
35
|
+
}
|
|
36
|
+
return []byte(obj.Text), nil
|
|
37
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
package metadata
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"encoding/json"
|
|
5
|
+
"fmt"
|
|
6
|
+
"testing"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
// TestMetadataBigintIsComparable pins the property the whole representation
|
|
10
|
+
// rests on: two independently built values of the same bigint are `==`.
|
|
11
|
+
//
|
|
12
|
+
// A `MetadataConstantValue.Value` is compared with `==` inside the factories --
|
|
13
|
+
// the intersection tag assigner matches a child's constant against the merged
|
|
14
|
+
// parent's that way -- and every other value it can hold (`string`, `bool`, the
|
|
15
|
+
// number) is a comparable value. A pointer stand-in such as `*math/big.Int`
|
|
16
|
+
// builds a fresh allocation per literal, so those comparisons would test
|
|
17
|
+
// identity and, for bigints alone, silently decide "not the same constant":
|
|
18
|
+
// `(1n | 2n) & tags.Type<"int64">` lost its tag that way. This is the guard,
|
|
19
|
+
// held one level below the transform so a future change to the representation
|
|
20
|
+
// fails here rather than as a dropped tag three packages away.
|
|
21
|
+
//
|
|
22
|
+
// 1. Build the same value twice, independently, and compare with `==`.
|
|
23
|
+
// 2. Compare a different value, and a negative against its positive.
|
|
24
|
+
// 3. Check the two renderings every consumer reads: `fmt.Sprint` for the emit
|
|
25
|
+
// and `encoding/json` for metadata a downstream tool marshals.
|
|
26
|
+
func TestMetadataBigintIsComparable(t *testing.T) {
|
|
27
|
+
const digits = "9007199254740993"
|
|
28
|
+
left := MetadataBigint{Text: digits}
|
|
29
|
+
right := MetadataBigint{Text: digits}
|
|
30
|
+
if left != right {
|
|
31
|
+
t.Fatalf("two values of the same bigint must be ==: %v vs %v", left, right)
|
|
32
|
+
}
|
|
33
|
+
var boxed any = left
|
|
34
|
+
if boxed != any(right) {
|
|
35
|
+
t.Fatalf("the same bigint must stay == once boxed in the `any` a constant value holds")
|
|
36
|
+
}
|
|
37
|
+
if left == (MetadataBigint{Text: "9007199254740992"}) {
|
|
38
|
+
t.Fatalf("distinct bigints must not compare equal")
|
|
39
|
+
}
|
|
40
|
+
if left == (MetadataBigint{Text: "-" + digits}) {
|
|
41
|
+
t.Fatalf("a negative must not compare equal to its positive")
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if got := fmt.Sprint(left); got != digits {
|
|
45
|
+
t.Fatalf("fmt.Sprint must render the base-10 digits, got %q", got)
|
|
46
|
+
}
|
|
47
|
+
if got := fmt.Sprint(MetadataBigint{Text: "-5"}); got != "-5" {
|
|
48
|
+
t.Fatalf("a negative must keep its sign, got %q", got)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
encoded, err := json.Marshal(left)
|
|
52
|
+
if err != nil {
|
|
53
|
+
t.Fatalf("marshal bigint: %v", err)
|
|
54
|
+
}
|
|
55
|
+
if string(encoded) != digits {
|
|
56
|
+
t.Fatalf("marshaled bigint must be the unquoted digits, got %s", encoded)
|
|
57
|
+
}
|
|
58
|
+
zero, err := json.Marshal(MetadataBigint{})
|
|
59
|
+
if err != nil {
|
|
60
|
+
t.Fatalf("marshal zero bigint: %v", err)
|
|
61
|
+
}
|
|
62
|
+
if string(zero) != "0" {
|
|
63
|
+
t.Fatalf("the zero value must marshal as 0, got %s", zero)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
package reflect
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
-
"encoding/json"
|
|
5
4
|
"strings"
|
|
6
|
-
"unicode"
|
|
7
|
-
"unicode/utf8"
|
|
8
5
|
|
|
9
6
|
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
10
7
|
shimprinter "github.com/microsoft/typescript-go/shim/printer"
|
|
@@ -67,50 +64,22 @@ func (reflectSchemaTransformerNamespace) Transform(props nativetransform.ITransf
|
|
|
67
64
|
}, props.Context.Emit)
|
|
68
65
|
}
|
|
69
66
|
|
|
67
|
+
// reflectTransformer_literal lowers a metadata tree into its object literal.
|
|
68
|
+
//
|
|
69
|
+
// This used to round-trip the tree through `encoding/json` and then lowercase
|
|
70
|
+
// each key's initial, which is exactly what `LiteralFactory` already does when
|
|
71
|
+
// it reflects a struct. The round trip was not merely redundant: JSON has no
|
|
72
|
+
// bigint, so a `bigint` constant -- the one value in the tree that is neither
|
|
73
|
+
// a string nor a JSON number -- could not survive it. It came back as the
|
|
74
|
+
// object its fields happened to spell, and `IMetadataSchema.IValue` declares
|
|
75
|
+
// `bigint` there.
|
|
76
|
+
//
|
|
77
|
+
// Writing the tree directly leaves every other member identical (diffed over a
|
|
78
|
+
// metadata tree spanning objects, optional and nullable members, tags, arrays,
|
|
79
|
+
// tuples, sets, maps, natives, functions, aliases, and unions: the bigint
|
|
80
|
+
// values were the only difference) and lets a bigint stay a bigint.
|
|
70
81
|
func reflectTransformer_literal(value any, emit ...*shimprinter.EmitContext) *shimast.Node {
|
|
71
|
-
return nativefactories.LiteralFactory.Write(
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
func reflectTransformer_toPrimitive(value any) any {
|
|
75
|
-
data, err := json.Marshal(value)
|
|
76
|
-
if err != nil {
|
|
77
|
-
return nil
|
|
78
|
-
}
|
|
79
|
-
var decoded any
|
|
80
|
-
if err := json.Unmarshal(data, &decoded); err != nil {
|
|
81
|
-
return nil
|
|
82
|
-
}
|
|
83
|
-
return reflectTransformer_lowerKeys(decoded)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
func reflectTransformer_lowerKeys(value any) any {
|
|
87
|
-
switch v := value.(type) {
|
|
88
|
-
case map[string]any:
|
|
89
|
-
output := map[string]any{}
|
|
90
|
-
for key, elem := range v {
|
|
91
|
-
output[reflectTransformer_lowerInitial(key)] = reflectTransformer_lowerKeys(elem)
|
|
92
|
-
}
|
|
93
|
-
return output
|
|
94
|
-
case []any:
|
|
95
|
-
output := make([]any, 0, len(v))
|
|
96
|
-
for _, elem := range v {
|
|
97
|
-
output = append(output, reflectTransformer_lowerKeys(elem))
|
|
98
|
-
}
|
|
99
|
-
return output
|
|
100
|
-
default:
|
|
101
|
-
return value
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
func reflectTransformer_lowerInitial(str string) string {
|
|
106
|
-
if str == "" {
|
|
107
|
-
return str
|
|
108
|
-
}
|
|
109
|
-
r, size := utf8.DecodeRuneInString(str)
|
|
110
|
-
if r == utf8.RuneError && size == 0 {
|
|
111
|
-
return str
|
|
112
|
-
}
|
|
113
|
-
return string(unicode.ToLower(r)) + str[size:]
|
|
82
|
+
return nativefactories.LiteralFactory.Write(value, emit...)
|
|
114
83
|
}
|
|
115
84
|
|
|
116
85
|
func reflectTransformer_errors(errors []nativefactories.MetadataFactory_IError) []nativetransform.TransformerError_MetadataFactory_IError {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "typia",
|
|
3
|
-
"version": "14.0.
|
|
3
|
+
"version": "14.0.5",
|
|
4
4
|
"description": "Superfast runtime validators with only one line",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
"homepage": "https://typia.io",
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"randexp": "^0.5.3",
|
|
46
|
-
"@typia/interface": "^14.0.
|
|
47
|
-
"@typia/utils": "^14.0.
|
|
46
|
+
"@typia/interface": "^14.0.5",
|
|
47
|
+
"@typia/utils": "^14.0.5"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
50
|
"ttsc": ">=0.19.2"
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
package main
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"os"
|
|
5
|
-
"os/exec"
|
|
6
|
-
"path/filepath"
|
|
7
|
-
"testing"
|
|
8
|
-
)
|
|
9
|
-
|
|
10
|
-
// TestReflectLiteralsEmptyUnionTransform pins issue #2377: the literal set of an
|
|
11
|
-
// empty union is the empty array, not a transform error.
|
|
12
|
-
//
|
|
13
|
-
// The programmer admits a type argument by counting the members it can render,
|
|
14
|
-
// and it renders three kinds -- constant literals, the `boolean` atomic, and
|
|
15
|
-
// `null`. `null` is a `MetadataSchema` flag rather than a bucket, so it was
|
|
16
|
-
// missing from the count, which made two admissible arguments look like "no
|
|
17
|
-
// constant literal type found": `never`, whose union is empty on both sides of
|
|
18
|
-
// the comparison, and `null`, which the public
|
|
19
|
-
// `literals<T extends Atomic.Type | null>()` signature documents. `never` is
|
|
20
|
-
// vacuously literal-only and `literals<never>(): never[]` has exactly one
|
|
21
|
-
// inhabitant, so both must transform and run.
|
|
22
|
-
//
|
|
23
|
-
// 1. Transform a module whose `reflect.literals` arguments span every
|
|
24
|
-
// uninhabited spelling a caller reaches -- the keyword, an alias, an
|
|
25
|
-
// exhaustive `Exclude`, an empty `Extract`, a TypeScript-collapsed
|
|
26
|
-
// intersection, and one typia prunes itself -- plus `null`, a fully mixed
|
|
27
|
-
// literal union, and the plain `boolean` atomic.
|
|
28
|
-
// 2. Execute the emitted CommonJS.
|
|
29
|
-
// 3. Assert `never` yields `[]`, `null` yields `[null]`, and the arguments that
|
|
30
|
-
// already worked keep their exact members and order, including the sort the
|
|
31
|
-
// guide documents.
|
|
32
|
-
func TestReflectLiteralsEmptyUnionTransform(t *testing.T) {
|
|
33
|
-
node, err := exec.LookPath("node")
|
|
34
|
-
if err != nil {
|
|
35
|
-
t.Skip("node executable not found")
|
|
36
|
-
}
|
|
37
|
-
root := ttscTypiaTestRepoRoot(t)
|
|
38
|
-
base := filepath.Join(root, "packages", "typia", "native", ".tmp-ttsc-typia-tests")
|
|
39
|
-
if err := os.MkdirAll(base, 0o755); err != nil {
|
|
40
|
-
t.Fatalf("mkdir temp base: %v", err)
|
|
41
|
-
}
|
|
42
|
-
dir, err := os.MkdirTemp(base, "reflect-literals-empty-")
|
|
43
|
-
if err != nil {
|
|
44
|
-
t.Fatalf("create temp fixture: %v", err)
|
|
45
|
-
}
|
|
46
|
-
t.Cleanup(func() { _ = os.RemoveAll(dir) })
|
|
47
|
-
src := filepath.Join(dir, "src")
|
|
48
|
-
if err := os.MkdirAll(src, 0o755); err != nil {
|
|
49
|
-
t.Fatalf("mkdir fixture src: %v", err)
|
|
50
|
-
}
|
|
51
|
-
if err := os.WriteFile(filepath.Join(dir, "tsconfig.json"), []byte(atomicIntersectionSchemaTSConfig), 0o644); err != nil {
|
|
52
|
-
t.Fatalf("write tsconfig: %v", err)
|
|
53
|
-
}
|
|
54
|
-
if err := os.WriteFile(filepath.Join(src, "main.ts"), []byte(reflectLiteralsEmptyUnionSource), 0o644); err != nil {
|
|
55
|
-
t.Fatalf("write source: %v", err)
|
|
56
|
-
}
|
|
57
|
-
ttscTypiaTestTypecheck(t, dir)
|
|
58
|
-
|
|
59
|
-
out, errText, code := ttscTypiaTestCapture(func() int {
|
|
60
|
-
return runTransform([]string{
|
|
61
|
-
"--cwd", dir,
|
|
62
|
-
"--tsconfig", "tsconfig.json",
|
|
63
|
-
"--file", "src/main.ts",
|
|
64
|
-
"--output", "js",
|
|
65
|
-
})
|
|
66
|
-
})
|
|
67
|
-
if code != 0 {
|
|
68
|
-
t.Fatalf("reflect.literals empty union transform failed: code=%d stderr=\n%s", code, errText)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
runtimeDir := filepath.Join(dir, "runtime")
|
|
72
|
-
if err := os.MkdirAll(runtimeDir, 0o755); err != nil {
|
|
73
|
-
t.Fatalf("mkdir runtime dir: %v", err)
|
|
74
|
-
}
|
|
75
|
-
ttscTypiaTestWriteCommonRuntimeStubs(t, runtimeDir)
|
|
76
|
-
runtimeJS := ttscTypiaTestRewriteCommonJS(t, out)
|
|
77
|
-
if err := os.WriteFile(filepath.Join(runtimeDir, "main.cjs"), []byte(runtimeJS), 0o644); err != nil {
|
|
78
|
-
t.Fatalf("write runtime module: %v", err)
|
|
79
|
-
}
|
|
80
|
-
runner := filepath.Join(runtimeDir, "run.cjs")
|
|
81
|
-
if err := os.WriteFile(runner, []byte(reflectLiteralsEmptyUnionRunner), 0o644); err != nil {
|
|
82
|
-
t.Fatalf("write runtime runner: %v", err)
|
|
83
|
-
}
|
|
84
|
-
cmd := exec.Command(node, runner)
|
|
85
|
-
cmd.Dir = runtimeDir
|
|
86
|
-
output, err := cmd.CombinedOutput()
|
|
87
|
-
if err != nil {
|
|
88
|
-
t.Fatalf("reflect.literals empty union runtime cases failed: %v\n%s", err, output)
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const reflectLiteralsEmptyUnionSource = `import typia from "typia";
|
|
93
|
-
|
|
94
|
-
type Color = "red" | "green" | "blue";
|
|
95
|
-
type Empty = never;
|
|
96
|
-
|
|
97
|
-
// The empty union: no member on either side of the admission comparison. Every
|
|
98
|
-
// spelling a caller reaches it through has to transform, not only the keyword:
|
|
99
|
-
// an alias hides it behind a name, and a conditional or an exhaustive Exclude
|
|
100
|
-
// is how a generic caller produces one without writing "never" at all.
|
|
101
|
-
export const empty = typia.reflect.literals<never>();
|
|
102
|
-
export const emptyAlias = typia.reflect.literals<Empty>();
|
|
103
|
-
export const emptyExclude = typia.reflect.literals<Exclude<Color, Color>>();
|
|
104
|
-
export const emptyExtract = typia.reflect.literals<Extract<Color, "cyan">>();
|
|
105
|
-
|
|
106
|
-
// Uninhabited without the keyword: TypeScript collapses one to never itself,
|
|
107
|
-
// while the other stays an intersection whose every distributed member typia
|
|
108
|
-
// prunes away. Both are empty on both sides of the comparison, so the rule the
|
|
109
|
-
// keyword exercises has to cover them too.
|
|
110
|
-
export const emptyCollapsed = typia.reflect.literals<string & number>();
|
|
111
|
-
export const emptyPruned = typia.reflect.literals<(string | number) & { data: number }>();
|
|
112
|
-
|
|
113
|
-
// A union whose only member is the nullable flag, which carries no bucket.
|
|
114
|
-
export const onlyNull = typia.reflect.literals<null>();
|
|
115
|
-
|
|
116
|
-
// Controls that already transformed: the flag must join the count without
|
|
117
|
-
// displacing a constant, a boolean atomic, or their order.
|
|
118
|
-
export const mixed = typia.reflect.literals<"a" | 1 | true | null>();
|
|
119
|
-
export const booleanAtomic = typia.reflect.literals<boolean>();
|
|
120
|
-
export const booleanAtomicNull = typia.reflect.literals<boolean | null>();
|
|
121
|
-
|
|
122
|
-
// Declaration order deliberately disagrees with the emitted order: values of
|
|
123
|
-
// one primitive type are sorted (iterate_metadata_sort), which is the behavior
|
|
124
|
-
// the reflect.literals guide states.
|
|
125
|
-
export const sorted = typia.reflect.literals<"b" | "a" | 10 | 2>();
|
|
126
|
-
`
|
|
127
|
-
|
|
128
|
-
const reflectLiteralsEmptyUnionRunner = `const mod = require("./main.cjs");
|
|
129
|
-
|
|
130
|
-
const check = (label, actual, expected) => {
|
|
131
|
-
if (Array.isArray(actual) === false) {
|
|
132
|
-
throw new Error(label + ": expected an array, got " + JSON.stringify(actual));
|
|
133
|
-
}
|
|
134
|
-
if (
|
|
135
|
-
actual.length !== expected.length ||
|
|
136
|
-
actual.some((item, index) => item !== expected[index])
|
|
137
|
-
) {
|
|
138
|
-
throw new Error(
|
|
139
|
-
label +
|
|
140
|
-
": expected " +
|
|
141
|
-
JSON.stringify(expected) +
|
|
142
|
-
", got " +
|
|
143
|
-
JSON.stringify(actual),
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
check("never", mod.empty, []);
|
|
149
|
-
check("never alias", mod.emptyAlias, []);
|
|
150
|
-
check("exhaustive Exclude", mod.emptyExclude, []);
|
|
151
|
-
check("empty Extract", mod.emptyExtract, []);
|
|
152
|
-
check("collapsed intersection", mod.emptyCollapsed, []);
|
|
153
|
-
check("pruned intersection", mod.emptyPruned, []);
|
|
154
|
-
check("null", mod.onlyNull, [null]);
|
|
155
|
-
check("mixed", mod.mixed, ["a", 1, true, null]);
|
|
156
|
-
check("booleanAtomic", mod.booleanAtomic, [true, false]);
|
|
157
|
-
check("booleanAtomicNull", mod.booleanAtomicNull, [true, false, null]);
|
|
158
|
-
check("sorted", mod.sorted, ["a", "b", 2, 10]);
|
|
159
|
-
|
|
160
|
-
console.log("ok");
|
|
161
|
-
`
|