Ensure optional arrays, arrays with defaults, and strings with defaults are supported (#8896)
Fixing issues with generated ts/js
diff --git a/src/idl_gen_ts.cpp b/src/idl_gen_ts.cpp
index 1eb6d81..9bd00d0 100644
--- a/src/idl_gen_ts.cpp
+++ b/src/idl_gen_ts.cpp
@@ -522,7 +522,16 @@
case BASE_TYPE_BOOL:
return value.constant == "0" ? "false" : "true";
- case BASE_TYPE_STRING:
+ case BASE_TYPE_STRING: {
+ // NOTE: Strings without a default value are parsed as "0" by
+ // the parser, so therefore we have to treat "0" as a null-signifying
+ // value.
+ if (value.constant == "0" || value.constant == "null") {
+ return "null";
+ } else {
+ return "\"" + value.constant + "\"";
+ }
+ }
case BASE_TYPE_UNION:
case BASE_TYPE_STRUCT: {
return null_keyword_;
@@ -1334,10 +1343,9 @@
// Emit a scalar field
const auto is_string = IsString(field.value.type);
if (IsScalar(field.value.type.base_type) || is_string) {
- const auto has_null_default = is_string || HasNullDefault(field);
-
- field_type += GenTypeName(imports, field, field.value.type, false,
- has_null_default);
+ field_type +=
+ GenTypeName(imports, field, field.value.type, false,
+ !HasDefaultValue(field) || HasNullDefault(field));
field_val = "this." + namer_.Method(field) + "()";
if (field.value.type.base_type != BASE_TYPE_STRING) {
@@ -1731,21 +1739,22 @@
offset_prefix = " const offset = " + GenBBAccess() +
".__offset(this.bb_pos, " +
NumToString(field.value.offset) + ");\n";
- offset_prefix += " return offset ? ";
+ offset_prefix += " return ";
}
// Emit a scalar field
const auto is_string = IsString(field.value.type);
if (IsScalar(field.value.type.base_type) || is_string) {
- const auto has_null_default = is_string || HasNullDefault(field);
+ const auto has_null_default =
+ !field.IsRequired() && !HasDefaultValue(field);
GenDocComment(field.doc_comment, code_ptr);
std::string prefix = namer_.Method(field) + "(";
if (is_string) {
- code += prefix + "):string|" + null_keyword_ + "\n";
+ code += prefix + "):" + (has_null_default ? "string|" + null_keyword_ : "string") + "\n";
code +=
prefix + "optionalEncoding:flatbuffers.Encoding" + "):" +
- GenTypeName(imports, struct_def, field.value.type, false, true) +
+ GenTypeName(imports, struct_def, field.value.type, false, has_null_default) +
"\n";
code += prefix + "optionalEncoding?:any";
} else {
@@ -1774,9 +1783,16 @@
if (is_string) {
index += ", optionalEncoding";
}
- code +=
- offset_prefix + GenGetter(field.value.type, "(" + index + ")");
- if (field.value.type.base_type != BASE_TYPE_ARRAY) {
+ if (field.IsRequired()) {
+ code +=
+ offset_prefix + GenGetter(field.value.type, "(" + index + ")");
+ ;
+ } else {
+ code += offset_prefix + "offset ? " +
+ GenGetter(field.value.type, "(" + index + ")");
+ }
+ if (field.value.type.base_type != BASE_TYPE_ARRAY &&
+ !field.IsRequired()) {
code += " : " + GenDefaultValue(field, imports);
}
code += ";\n";
@@ -1801,8 +1817,8 @@
code +=
MaybeAdd(field.value.offset) + ", " + GenBBAccess() + ");\n";
} else {
- code += offset_prefix + "(obj || " + GenerateNewExpression(type) +
- ").__init(";
+ code += offset_prefix + "offset ? (obj || " +
+ GenerateNewExpression(type) + ").__init(";
code += field.value.type.struct_def->fixed
? "this.bb_pos + offset"
: GenBBAccess() + ".__indirect(this.bb_pos + offset)";
@@ -1960,7 +1976,7 @@
code += "):" + vectortypename + "|" + null_keyword_ + " {\n";
if (vectortype.base_type == BASE_TYPE_STRUCT) {
- code += offset_prefix + "(obj || " +
+ code += offset_prefix + "offset ? (obj || " +
GenerateNewExpression(vectortypename);
code += ").__init(";
code += vectortype.struct_def->fixed
@@ -1973,7 +1989,8 @@
} else if (IsString(vectortype)) {
index += ", optionalEncoding";
}
- code += offset_prefix + GenGetter(vectortype, "(" + index + ")");
+ code += offset_prefix + "offset ? " +
+ GenGetter(vectortype, "(" + index + ")");
}
code += " : ";
if (field.value.type.element == BASE_TYPE_BOOL) {
@@ -2005,7 +2022,7 @@
" "
"{\n";
- code += offset_prefix +
+ code += offset_prefix + "offset ? " +
GenGetter(field.value.type, "(obj, this.bb_pos + offset)") +
" : " + null_keyword_ + ";\n";
break;
@@ -2058,7 +2075,7 @@
// Emit a length helper
GenDocComment(code_ptr);
code += namer_.Method(field, "Length");
- code += "():number {\n" + offset_prefix;
+ code += "():number {\n" + offset_prefix + "offset ? ";
code +=
GenBBAccess() + ".__vector_len(this.bb_pos + offset) : 0;\n}\n\n";
@@ -2069,15 +2086,30 @@
GenDocComment(code_ptr);
code += namer_.Method(field, "Array");
- code += "():" + GenType(vectorType) + "Array|" + null_keyword_ +
- " {\n" + offset_prefix;
- code += "new " + GenType(vectorType) + "Array(" + GenBBAccess() +
- ".bytes().buffer, " + GenBBAccess() +
- ".bytes().byteOffset + " + GenBBAccess() +
- ".__vector(this.bb_pos + offset), " + GenBBAccess() +
- ".__vector_len(this.bb_pos + offset)) : " + null_keyword_ +
- ";\n}\n\n";
+ std::string return_type =
+ (field.IsRequired() || HasDefaultValue(field))
+ ? "Array"
+ : ("Array|" + null_keyword_);
+ if (field.IsRequired()) {
+ code += "():" + GenType(vectorType) + return_type + " {\n" +
+ offset_prefix + "new " + GenType(vectorType) + "Array(" +
+ GenBBAccess() + ".bytes().buffer, " + GenBBAccess() +
+ ".bytes().byteOffset + " + GenBBAccess() +
+ ".__vector(this.bb_pos + offset), " + GenBBAccess() +
+ ".__vector_len(this.bb_pos + offset));\n}\n\n";
+ } else {
+ std::string value = HasDefaultValue(field)
+ ? "new " + GenType(vectorType) + "Array()"
+ : "null";
+ code += "():" + GenType(vectorType) + return_type + " {\n" +
+ offset_prefix + "offset ? new " + GenType(vectorType) +
+ "Array(" + GenBBAccess() + ".bytes().buffer, " +
+ GenBBAccess() + ".bytes().byteOffset + " + GenBBAccess() +
+ ".__vector(this.bb_pos + offset), " + GenBBAccess() +
+ ".__vector_len(this.bb_pos + offset)) : " + value +
+ ";\n}\n\n";
+ }
}
}
}
@@ -2308,6 +2340,34 @@
return field.IsOptional() && field.value.constant == "null";
}
+ static bool HasDefaultValue(const FieldDef& field) {
+ switch (field.value.type.base_type) {
+ // These types can't have defaults
+ case BASE_TYPE_UNION:
+ case BASE_TYPE_STRUCT: {
+ return false;
+ }
+
+ // Arrays always have a default (empty array)
+ case BASE_TYPE_ARRAY:
+ return true;
+
+ // The only supported default for vectors is []
+ case BASE_TYPE_VECTOR:
+ return field.value.constant == "[]";
+
+ // Even strings are presumed to be null-default if the default value is
+ // "0", this is intended behavior.
+ case BASE_TYPE_STRING: {
+ return field.value.constant != "0" && field.value.constant != "null";
+ }
+
+ default: {
+ return field.value.constant != "null";
+ }
+ }
+ }
+
std::string GetArgType(import_set& imports, const Definition& owner,
const FieldDef& field, bool allowNull) {
return GenTypeName(imports, owner, field.value.type, true,
diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp
index 569c767..caeca93 100644
--- a/src/idl_parser.cpp
+++ b/src/idl_parser.cpp
@@ -2809,7 +2809,8 @@
bool Parser::SupportsDefaultVectorsAndStrings() const {
static FLATBUFFERS_CONSTEXPR unsigned long supported_langs =
IDLOptions::kRust | IDLOptions::kSwift | IDLOptions::kNim |
- IDLOptions::kCpp | IDLOptions::kBinary | IDLOptions::kJson;
+ IDLOptions::kCpp | IDLOptions::kBinary | IDLOptions::kJson |
+ IDLOptions::kTs;
return !(opts.lang_to_generate & ~supported_langs);
}
diff --git a/tests/even_more_defaults.fbs b/tests/even_more_defaults.fbs
new file mode 100644
index 0000000..1b87ffa
--- /dev/null
+++ b/tests/even_more_defaults.fbs
@@ -0,0 +1,23 @@
+
+enum ABC: int { A, B, C }
+
+struct EvenMoreStruct {
+ g:[int64:2];
+}
+
+table EvenMoreDefaults {
+ str: EvenMoreStruct;
+ ints: [int] = [];
+ floats: [float] = [ ];
+ float_optional: [float];
+ bytes_optional: [ubyte];
+ floats_required: [float] (required);
+ empty_string: string = "";
+ some_string: string = "some";
+ zero_string: string = "0";
+ a_string:string (key);
+ required_string: string (required);
+ abcs: [ABC] = [];
+ bools: [bool] = [];
+ one_bool: bool = true;
+}
diff --git a/tests/ts/TypeScriptTest.py b/tests/ts/TypeScriptTest.py
index 957c612..9c1765f 100755
--- a/tests/ts/TypeScriptTest.py
+++ b/tests/ts/TypeScriptTest.py
@@ -88,6 +88,20 @@
esbuild("monster_test.ts", "monster_test_generated.cjs")
flatc(
+ options=[
+ "--ts",
+ "--reflect-names",
+ "--gen-name-strings",
+ "--gen-mutable",
+ "--gen-object-api",
+ "--ts-entry-points",
+ "--ts-flat-files",
+ ],
+ schema="../even_more_defaults.fbs",
+ include="../include_test",
+)
+
+flatc(
options=["--gen-object-api", "-b"],
schema="../monster_test.fbs",
include="../include_test",
diff --git a/tests/ts/my-game/example/monster.ts b/tests/ts/my-game/example/monster.ts
index 04ca1ac..dd7333c 100644
--- a/tests/ts/my-game/example/monster.ts
+++ b/tests/ts/my-game/example/monster.ts
@@ -81,11 +81,11 @@
return true;
}
-name():string|null
-name(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null
-name(optionalEncoding?:any):string|Uint8Array|null {
+name():string
+name(optionalEncoding:flatbuffers.Encoding):string|Uint8Array
+name(optionalEncoding?:any):string|Uint8Array {
const offset = this.bb!.__offset(this.bb_pos, 10);
- return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null;
+ return this.bb!.__string(this.bb_pos + offset, optionalEncoding);
}
inventory(index: number):number|null {