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 {