Skip to content

Commit 94d6b80

Browse files
Ensure optional arrays, arrays with defaults, and strings with defaults are supported (#8896)
Fixing issues with generated ts/js
1 parent fa70963 commit 94d6b80

5 files changed

Lines changed: 130 additions & 32 deletions

File tree

src/idl_gen_ts.cpp

Lines changed: 87 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -522,7 +522,16 @@ class TsGenerator : public BaseGenerator {
522522
case BASE_TYPE_BOOL:
523523
return value.constant == "0" ? "false" : "true";
524524

525-
case BASE_TYPE_STRING:
525+
case BASE_TYPE_STRING: {
526+
// NOTE: Strings without a default value are parsed as "0" by
527+
// the parser, so therefore we have to treat "0" as a null-signifying
528+
// value.
529+
if (value.constant == "0" || value.constant == "null") {
530+
return "null";
531+
} else {
532+
return "\"" + value.constant + "\"";
533+
}
534+
}
526535
case BASE_TYPE_UNION:
527536
case BASE_TYPE_STRUCT: {
528537
return null_keyword_;
@@ -1334,10 +1343,9 @@ class TsGenerator : public BaseGenerator {
13341343
// Emit a scalar field
13351344
const auto is_string = IsString(field.value.type);
13361345
if (IsScalar(field.value.type.base_type) || is_string) {
1337-
const auto has_null_default = is_string || HasNullDefault(field);
1338-
1339-
field_type += GenTypeName(imports, field, field.value.type, false,
1340-
has_null_default);
1346+
field_type +=
1347+
GenTypeName(imports, field, field.value.type, false,
1348+
!HasDefaultValue(field) || HasNullDefault(field));
13411349
field_val = "this." + namer_.Method(field) + "()";
13421350

13431351
if (field.value.type.base_type != BASE_TYPE_STRING) {
@@ -1731,21 +1739,22 @@ class TsGenerator : public BaseGenerator {
17311739
offset_prefix = " const offset = " + GenBBAccess() +
17321740
".__offset(this.bb_pos, " +
17331741
NumToString(field.value.offset) + ");\n";
1734-
offset_prefix += " return offset ? ";
1742+
offset_prefix += " return ";
17351743
}
17361744

17371745
// Emit a scalar field
17381746
const auto is_string = IsString(field.value.type);
17391747
if (IsScalar(field.value.type.base_type) || is_string) {
1740-
const auto has_null_default = is_string || HasNullDefault(field);
1748+
const auto has_null_default =
1749+
!field.IsRequired() && !HasDefaultValue(field);
17411750

17421751
GenDocComment(field.doc_comment, code_ptr);
17431752
std::string prefix = namer_.Method(field) + "(";
17441753
if (is_string) {
1745-
code += prefix + "):string|" + null_keyword_ + "\n";
1754+
code += prefix + "):" + (has_null_default ? "string|" + null_keyword_ : "string") + "\n";
17461755
code +=
17471756
prefix + "optionalEncoding:flatbuffers.Encoding" + "):" +
1748-
GenTypeName(imports, struct_def, field.value.type, false, true) +
1757+
GenTypeName(imports, struct_def, field.value.type, false, has_null_default) +
17491758
"\n";
17501759
code += prefix + "optionalEncoding?:any";
17511760
} else {
@@ -1774,9 +1783,16 @@ class TsGenerator : public BaseGenerator {
17741783
if (is_string) {
17751784
index += ", optionalEncoding";
17761785
}
1777-
code +=
1778-
offset_prefix + GenGetter(field.value.type, "(" + index + ")");
1779-
if (field.value.type.base_type != BASE_TYPE_ARRAY) {
1786+
if (field.IsRequired()) {
1787+
code +=
1788+
offset_prefix + GenGetter(field.value.type, "(" + index + ")");
1789+
;
1790+
} else {
1791+
code += offset_prefix + "offset ? " +
1792+
GenGetter(field.value.type, "(" + index + ")");
1793+
}
1794+
if (field.value.type.base_type != BASE_TYPE_ARRAY &&
1795+
!field.IsRequired()) {
17801796
code += " : " + GenDefaultValue(field, imports);
17811797
}
17821798
code += ";\n";
@@ -1801,8 +1817,8 @@ class TsGenerator : public BaseGenerator {
18011817
code +=
18021818
MaybeAdd(field.value.offset) + ", " + GenBBAccess() + ");\n";
18031819
} else {
1804-
code += offset_prefix + "(obj || " + GenerateNewExpression(type) +
1805-
").__init(";
1820+
code += offset_prefix + "offset ? (obj || " +
1821+
GenerateNewExpression(type) + ").__init(";
18061822
code += field.value.type.struct_def->fixed
18071823
? "this.bb_pos + offset"
18081824
: GenBBAccess() + ".__indirect(this.bb_pos + offset)";
@@ -1960,7 +1976,7 @@ class TsGenerator : public BaseGenerator {
19601976
code += "):" + vectortypename + "|" + null_keyword_ + " {\n";
19611977

19621978
if (vectortype.base_type == BASE_TYPE_STRUCT) {
1963-
code += offset_prefix + "(obj || " +
1979+
code += offset_prefix + "offset ? (obj || " +
19641980
GenerateNewExpression(vectortypename);
19651981
code += ").__init(";
19661982
code += vectortype.struct_def->fixed
@@ -1973,7 +1989,8 @@ class TsGenerator : public BaseGenerator {
19731989
} else if (IsString(vectortype)) {
19741990
index += ", optionalEncoding";
19751991
}
1976-
code += offset_prefix + GenGetter(vectortype, "(" + index + ")");
1992+
code += offset_prefix + "offset ? " +
1993+
GenGetter(vectortype, "(" + index + ")");
19771994
}
19781995
code += " : ";
19791996
if (field.value.type.element == BASE_TYPE_BOOL) {
@@ -2005,7 +2022,7 @@ class TsGenerator : public BaseGenerator {
20052022
" "
20062023
"{\n";
20072024

2008-
code += offset_prefix +
2025+
code += offset_prefix + "offset ? " +
20092026
GenGetter(field.value.type, "(obj, this.bb_pos + offset)") +
20102027
" : " + null_keyword_ + ";\n";
20112028
break;
@@ -2058,7 +2075,7 @@ class TsGenerator : public BaseGenerator {
20582075
// Emit a length helper
20592076
GenDocComment(code_ptr);
20602077
code += namer_.Method(field, "Length");
2061-
code += "():number {\n" + offset_prefix;
2078+
code += "():number {\n" + offset_prefix + "offset ? ";
20622079

20632080
code +=
20642081
GenBBAccess() + ".__vector_len(this.bb_pos + offset) : 0;\n}\n\n";
@@ -2069,15 +2086,30 @@ class TsGenerator : public BaseGenerator {
20692086
GenDocComment(code_ptr);
20702087

20712088
code += namer_.Method(field, "Array");
2072-
code += "():" + GenType(vectorType) + "Array|" + null_keyword_ +
2073-
" {\n" + offset_prefix;
2074-
2075-
code += "new " + GenType(vectorType) + "Array(" + GenBBAccess() +
2076-
".bytes().buffer, " + GenBBAccess() +
2077-
".bytes().byteOffset + " + GenBBAccess() +
2078-
".__vector(this.bb_pos + offset), " + GenBBAccess() +
2079-
".__vector_len(this.bb_pos + offset)) : " + null_keyword_ +
2080-
";\n}\n\n";
2089+
2090+
std::string return_type =
2091+
(field.IsRequired() || HasDefaultValue(field))
2092+
? "Array"
2093+
: ("Array|" + null_keyword_);
2094+
if (field.IsRequired()) {
2095+
code += "():" + GenType(vectorType) + return_type + " {\n" +
2096+
offset_prefix + "new " + GenType(vectorType) + "Array(" +
2097+
GenBBAccess() + ".bytes().buffer, " + GenBBAccess() +
2098+
".bytes().byteOffset + " + GenBBAccess() +
2099+
".__vector(this.bb_pos + offset), " + GenBBAccess() +
2100+
".__vector_len(this.bb_pos + offset));\n}\n\n";
2101+
} else {
2102+
std::string value = HasDefaultValue(field)
2103+
? "new " + GenType(vectorType) + "Array()"
2104+
: "null";
2105+
code += "():" + GenType(vectorType) + return_type + " {\n" +
2106+
offset_prefix + "offset ? new " + GenType(vectorType) +
2107+
"Array(" + GenBBAccess() + ".bytes().buffer, " +
2108+
GenBBAccess() + ".bytes().byteOffset + " + GenBBAccess() +
2109+
".__vector(this.bb_pos + offset), " + GenBBAccess() +
2110+
".__vector_len(this.bb_pos + offset)) : " + value +
2111+
";\n}\n\n";
2112+
}
20812113
}
20822114
}
20832115
}
@@ -2308,6 +2340,34 @@ class TsGenerator : public BaseGenerator {
23082340
return field.IsOptional() && field.value.constant == "null";
23092341
}
23102342

2343+
static bool HasDefaultValue(const FieldDef& field) {
2344+
switch (field.value.type.base_type) {
2345+
// These types can't have defaults
2346+
case BASE_TYPE_UNION:
2347+
case BASE_TYPE_STRUCT: {
2348+
return false;
2349+
}
2350+
2351+
// Arrays always have a default (empty array)
2352+
case BASE_TYPE_ARRAY:
2353+
return true;
2354+
2355+
// The only supported default for vectors is []
2356+
case BASE_TYPE_VECTOR:
2357+
return field.value.constant == "[]";
2358+
2359+
// Even strings are presumed to be null-default if the default value is
2360+
// "0", this is intended behavior.
2361+
case BASE_TYPE_STRING: {
2362+
return field.value.constant != "0" && field.value.constant != "null";
2363+
}
2364+
2365+
default: {
2366+
return field.value.constant != "null";
2367+
}
2368+
}
2369+
}
2370+
23112371
std::string GetArgType(import_set& imports, const Definition& owner,
23122372
const FieldDef& field, bool allowNull) {
23132373
return GenTypeName(imports, owner, field.value.type, true,

src/idl_parser.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2809,7 +2809,8 @@ bool Parser::SupportsOptionalScalars() const {
28092809
bool Parser::SupportsDefaultVectorsAndStrings() const {
28102810
static FLATBUFFERS_CONSTEXPR unsigned long supported_langs =
28112811
IDLOptions::kRust | IDLOptions::kSwift | IDLOptions::kNim |
2812-
IDLOptions::kCpp | IDLOptions::kBinary | IDLOptions::kJson;
2812+
IDLOptions::kCpp | IDLOptions::kBinary | IDLOptions::kJson |
2813+
IDLOptions::kTs;
28132814
return !(opts.lang_to_generate & ~supported_langs);
28142815
}
28152816

tests/even_more_defaults.fbs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
2+
enum ABC: int { A, B, C }
3+
4+
struct EvenMoreStruct {
5+
g:[int64:2];
6+
}
7+
8+
table EvenMoreDefaults {
9+
str: EvenMoreStruct;
10+
ints: [int] = [];
11+
floats: [float] = [ ];
12+
float_optional: [float];
13+
bytes_optional: [ubyte];
14+
floats_required: [float] (required);
15+
empty_string: string = "";
16+
some_string: string = "some";
17+
zero_string: string = "0";
18+
a_string:string (key);
19+
required_string: string (required);
20+
abcs: [ABC] = [];
21+
bools: [bool] = [];
22+
one_bool: bool = true;
23+
}

tests/ts/TypeScriptTest.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,20 @@ def esbuild(input, output):
8787
)
8888
esbuild("monster_test.ts", "monster_test_generated.cjs")
8989

90+
flatc(
91+
options=[
92+
"--ts",
93+
"--reflect-names",
94+
"--gen-name-strings",
95+
"--gen-mutable",
96+
"--gen-object-api",
97+
"--ts-entry-points",
98+
"--ts-flat-files",
99+
],
100+
schema="../even_more_defaults.fbs",
101+
include="../include_test",
102+
)
103+
90104
flatc(
91105
options=["--gen-object-api", "-b"],
92106
schema="../monster_test.fbs",

tests/ts/my-game/example/monster.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,11 @@ mutate_hp(value:number):boolean {
8181
return true;
8282
}
8383

84-
name():string|null
85-
name(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null
86-
name(optionalEncoding?:any):string|Uint8Array|null {
84+
name():string
85+
name(optionalEncoding:flatbuffers.Encoding):string|Uint8Array
86+
name(optionalEncoding?:any):string|Uint8Array {
8787
const offset = this.bb!.__offset(this.bb_pos, 10);
88-
return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;
88+
return this.bb!.__string(this.bb_pos + offset, optionalEncoding);
8989
}
9090

9191
inventory(index: number):number|null {

0 commit comments

Comments
 (0)