Squashed 'third_party/flatbuffers/' changes from e5f331db9..bc44fad35

bc44fad35 UnPackTo disable merge by default (#7527)
4fca4dc60 [TS/JS] Move TS tests to dedicated folder and deps upgrade (#7508)
036032373 Bump junit from 4.13 to 4.13.1 in /java (#7526)
89dfb43f3 Replace `bash JavaTest.sh` with `mvn test` (#7500)
c49aff4b6 enabled cpp17 tests in CI (#7524)
56e60223c prevent force_align attribute on enums (#7523)
89b1f5aa1 remove travis config (#7522)
b90159823 [Java][Flexbuffers] Add API to add nullables into the buffer. (#7521)
8cdc6a288 Install BuildFlatBuffers.cmake (#7519)
a67e35aff Moves all of the swift test code into tests/swift (#7509)
f124e41ae Updated Readme
4c954181c [Java][FlexBuffers] throwing exception for untyped fixed vectors (#7507)
7f7547737 [Android] Remove maven dependency of flatbuffers and use source folder (#7503)
a79d61ea8 Fixes issue with cocoapods failing to be published because of docc (#7505)
d465b39c3 [CMake]: fix breaking find_package change (#7499) (#7502)
c5a609dc2 [C#] Prepares for official Nuget release (#7496)
5634dc3d0 [ISSUE-6268] returns NaN insteadof nan (#7498)
37e37b8ca Updates cocoapods version (#7497)
8fd4534fb update android multidex setting (#7495)
d5427da52 Disable Android Build (#7494)
06c5c7ed0 FlatBuffers Version 2.0.8 (#7492)
b190ce11b Verifier Refinements (#7490)
bf5d23230 Namer applied to Typescript generator (#7488)
ce382d6dd [TS/JS] Add rollup and config to generate iife flatbuffers bundle (#7449)
41d9add7e C++: Add option to skip verifying nested flatbuffers (#7489)
6a8742754 [C++] support native_inline attribute for vector of tables (#7479)
694add668 Refactor test.cpp (#7487)
7edf8c908 Update scorecard to 1.1.2
b86387442 Fix typos (#7483)
e2eb5ee67 Include <array> head in stl_emulation.h (#7480)
994502b6d Version number in file package.json updated to 2.0.7 (#7476)
fa41e8367 [C++] Fixed crash when copying table with empty shared strings (#7477)
799cc8f7b Use type traits for specialization (#7475)
b7eb44147 Disable RTTI and rework use in idl_gen_ts.cpp (#7474)
8d01c5859 CMake project version detection made more robust (#7473)
237e8b71f Moved compiler warnings around (#7471)
eeb8fd60d Include builder.addOffset for vector of structs (#7470)
fef2ffc4d Use schema include name for keep-prefix (#7469)
8367664f1 Flatbuffers Version 2.0.7 (#7462)
d6f06c33f Reworked keep prefix (#7456)
627e8bf36 update grpc version (#7457)
883c42b7d disabling unpackto optimization (#7459)
7aae0af30 Remove old GRPC bash script and convert to python3 (#7454)
b057aa917 Grouped anonymous namespaces together, (#7455)
f1b26ff7f Change to GetTypeName (#7453)
9610a666b Generate SLSA signatures for Released zip files (#7450)
1e0f75a64 [WIP] speedup (#7452)
82b75407a Wrap types in namespace for --ts-flat-files and --gen-all (#7451)
f7c511957 Audit and fixups for GCC and Clang (#7212)
a66de58af Partial support for --ts-flat-files and --gen-all (#7446)
a3508f36d [Kotlin] Make sure namespace path exist for code generation (#7357)
137fec716 Stop using __has_trivial_copy on recent clang versions. (#7443)
214125e41 [C#] Rework how sorted vectors are looked up (#7441)
44a7dc999 Define minimum buffer size (#7440)
3cc2daa78 make_span overloads for pointer to vector (#7374) (#7435)
fa1174aa7 [TypeScript] Fix namespaceless schema generation (#7432)
83d4e2a10 Add checks to verifier (#7438)
8a09f3fb0 Fix FlexBuffers JS/TS bug https://github.com/google/flatbuffers/issues/6934 (#7434)
9dbe819ef Add flatc python tests to CI (#7437)
67c414958 Update TypeScriptTest.py to work better cross platform (#7436)
8b8c7dbdf Update gitingore to reflect name change (#7431)
2ee20a5f3 Remove auto including locale functions (#7430)
4be605604 [C++] Set StructDef::has_key property when deserializing from binary schema (#7386) (#7428)
fc5d86f1e [C++] Make template parameter in stl_emulation.h more explicit to avoid conflicts with cpprestsdk U macro (#7424)
9dce287ad Issue#6959 :Updated Automatically generated rust files. (#7425)
7798be3bb avoid zero-as-null-pointer warning (#7423)
966362e07 [C++] Vector of Tables equality (#7415)
a89c279ed [golang] Perform keyword escaping after case conversion (#7421)
a212b3c03 Turn of fail fast for C++ CI
9230f600d Remove stringop-overflow from error (#7422)
c79362156 [golang] Add support for text parsing with json struct tags (#7353)
ee2ced236 Moved TypeScriptTests to python script (#7411)
468c00a3f Rebased: grpc/compiler: Respect filename suffix and extension during code generation (#7414)
47c757f71 Add tests for flatc (#7405)
9a5ff8900 Add FLATBUFFERS_STRICT_MODE (#7408)
950444a34 [TS] Use TextEncoder and TextDecoder (#7400)
30d76198c Compilation issue msys2 #7399 (#7409)
cce3a66f0 Delete .travis directory
8d1cc6ac7 Revert "Compilation issue msys2 (#7403)" (#7407)
5b207639a Update readme.md
359e0f9d6 Revert "grpc/compiler: Respect filename suffix and extension during code generation (#7343)" (#7406)
ebbed0513 Delete cpp-linter.yml
aa395e5a5 (#7323) Rename CMake files according to project name (#7378)
32328075d Fix error msg format when generate GRPC failed (#7350)
97e89c5ac grpc/compiler: Respect filename suffix and extension during code generation (#7343)
5f6672be4 Fix Clang-Cl compile on Windows (#7308)
28e858c85 [TS/Bazel] Minor improvements to typescript.bzl (#7300)
987bebe67 [TS] fix incorrect reverse when writting array of structs (#7271)
ec0129369 Fix FlexBuffers Verifier tracking vectors reuse at wrong offset
50dd385b3 Add missing const (#7401)
da702cfd8 Compilation issue msys2 (#7403)
6e2791640 keep-prefix keeps relative pathing (#7394)
52fce5e53 fix(#7360): grpc used deprecated functions (#7361)
b7f13cd8e cpp_generator: comment out unused parameter to avoid warnings (#7381)
e42985e5a Updated Newtonsoft.Json to 13.0.1 (#7393)
0a8064637 Fix references to LICENSE file (#7377)
b9eea76a8 [Dart] Implement putBool to fix errors when serializing structs with bools (#7359)
1b9030015 Bump Newtonsoft.Json from 12.0.3 to 13.0.1 in /tests/FlatBuffers.Test (#7363)
83a43fc79 Reenable optional json (#7352)
5f0137602 Only include direct included filed (#7348)
9a1913a87 Revert "Implement optional scalars for JSON (#7322)" (#7351)
b4647beb8 Revert "Move reflection_ts_fbs into a separate directory (#7342)" (#7349)
d6060977a Remove asserting in verifier for flattests
987aa5b5e move -Wextra-semi to GCC 8.0+
42acdb63c [TS] Don't generate self-imports with --ts-flat-file (#7340)
0cc1aeb8c [golang] Create missing namespace directory structure (#7324) (#7325)
ba6c67170 [Kotlin] Remove download benchmark files dependency (#7314)
d2f33fc45 Disable Android on Linux CI build
0d1b72cbc [TS] fix ts import path issue (#7298)
9fce2fbf2 replace io/ioutil to os (#7281)
a18ea40d6 Implement optional scalars for JSON (#7322)
090caa280 Move reflection_ts_fbs into a separate directory (#7342)
49e1ea333 Implement optional scalars for Python (#7318)
11a198870 Started implementation for private flags in rust (#7269)
967df08b1 Adds full supposed for Wasm in the swift lib (#7328)
9aa08a429 Use keep case for Rust union discriminant type. (#7321)
9e8c758f5 Add explicit return types to lobster generated code (#7312)
74a25536b Add size check to fix out of bounds read risk (#7304)
12917af8a Update Rust docs page (#7296)
1ea2472f7 [swift] add had<ArrayName> property for arrays to check presence in a message (#7280)
0fe13cb28 Remove span ConstIterator/cbegin()/cend(). (#7295)
385dddc66 Namerkot (#7245)
750dde766 Make `flatc` generate Rust files not requiring `std` (#7273)
9917a168c [swift] Make swift module public (#7274)
76d3cca19 Rust: fix a name conflict when building with "no_std" feature (#7268)
c86e6d0e3 json inf parsing
d34dc32c2 fix include order
234d86c92 fixed off-by-one in parser
746c73b91 Add Annotations for Monster schema and example buffer
0bbfd4b2e fixes for annotator
716521953 Update readme.md (#7257)
a45f564cf [performance] Add aggressive systematic inlining in ByteBuffer and FlatBufferBuilder (#7253)
9d45a6403 more google merge fixes
ccfb4c20b Handle +/-inf in protos (#7256)
7bcd857b8 Specialize CreateVector with std::initializer_list (#7254)
23c8ab34c Swift update performance benchmark infrastructure (#7255)
70002dc5c various fixes for google merge
6e0e79f24 Add test for nested buffer verifier (#7252)
b856368d7 Turn off go modules temporary until we get a proper fix (#7251)
e37156a30 Keep the underlying storage capacity when clearing the FlatBufferBuilder. Gives a significant performance boost for serialisation of many small messages. (#7250)
a10b0e546 Java namer variable keep case (#7249)
275b73994 allow overriding FLATBUFFERS_MAX_ALIGNMENT
9d1ce9a10 Add parameter back to EndVector (#7246)
79afe6c3d Make Java namespaces keep case by default (#7243)
c6dbb2230 Add write permissions for labeller
18bacd3ea Expand test to make sure {}-initializers are properly understood by template. (#7242)
a2c913aec Add -Wnon-virtual-dtor
67b33b294 set workflows permissions to read-only (#7239)
7b5fd2bd0 [Kotlin] Fix key lookup returning null clashing with default value (#7237)
7181d7770 [Java] Fix key lookup returning null clashing with default value (#7236)
7f663b120 Allow CreateVectorOfStrings() to work with any string-type. (#7238)
173ebb694 Fixes a bug where the create function doesnt optional + required items (#7228)
d65823948 [Kotlin] Update gradle to 7.4.1 and simplify config files. (#7231)
ab4bf59e8 remove toascii (#7234)
eee44bbb2 disable cpp-linter (#7229)
a63fa51a1 Create cpp-linter.yml (#7208)
2049e5210 Adds a way to verify/exposes Entities ids (#7221)
832c618f5 Adds implementation flag for swift (#7202)
14615699f Started to migrate to target_compile_options (#7222)
20aad0c41 [C++] stl_emulation span::count_ is not const anymore (#7226) (#7227)
f083b33f2 code gen flexbuffer verifier (#7207)
bf17df346 [C++] generate sorted #include directives (#7213)
35281dedb Fix for [C++] flatc generates invalid Code in the default constructor for structs, when --cpp-field-case-style is used #7209 (#7211)
c9651b742 Add overloads for C# ByteBuffer/FlatBufferBuilder to allow adding vector blocks from ArraySegments or IntPtr (#7193)
26c3b3ada Update codeql.yml
da6e1b985 Update codeql.yml
ad27d751e Added Oss fuzz badge
0aab623cb Create codeql.yml
6a446bdd8 maximize parallel builds in CI (#7206)
21fb5cbbc Create scorecards.yml
0da6f9486 [C++] Static assert on Flatbuffers Version (#7203)
59e971308 reduce fuzzing time to 1 minute in CI
40866a892 fixed padding in struct for annotated binary (#7199)
b71d968fa Apply Namer prefix/suffix to other generators (#7197)
fac0d7be0 Apply Namer to Java. (#7194)
6c5603fd9 [C#] Fix collision of field name and type name (#7149)
2d21853a7 monster fuzzer fix for json default scalars
fec1a8d01 [swift] Add bazel configuration for Swift (#7195)
7fd857623 structured comments (#7192)
a4cb1599d Namerdart (#7187)
ae4ce7265 fuzzed binary annotator (#7188)
e2be0c0b0 Handle root offset and root table vtable invalidation (#7177)
2ad408697 [TS] Fix generation of struct members in object api (#7148)
4213d9105 VerifySizePrefixed (reflection::Schema) and GetAnySizePrefixedRoot added (#7181)
5a13f622c Correctly parse lists of enums in Dart generated code (#7157)
23a7e4e0b Adds no-includes flags to the swift code generator (#7182)
eeb49c275 Move flatbuffer_ts_library to typescript.bzl (#7183)
824763b31 Typo in flatc options (warning-as-errors instead of warnings-as-errors) (#7180)
d3aeee32b Annotated Flatbuffer Binary (#7174)
0bceba24d [Lua] Apply Namer to Lua (#7171)
b8c77d404 Make inclusion of header <optional> opt-out via macro (#7168)
8468eab83 Namersw (#7167)
2b2e8d4ae Nameroverloads (#7164)
b80b32bfa Use DESCRIPTION only if CMake version >= 3.9 (#7166)

Change-Id: Ic2681dabb1a798b7515e62753ee06aecb9933260
git-subtree-dir: third_party/flatbuffers
git-subtree-split: bc44fad35271e43fd7a79b4d691ac9e41708797f
Signed-off-by: Austin Schuh <austin.schuh@bluerivertech.com>
diff --git a/src/idl_gen_ts.cpp b/src/idl_gen_ts.cpp
index faf4345..32ab863 100644
--- a/src/idl_gen_ts.cpp
+++ b/src/idl_gen_ts.cpp
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-// independent from idl_parser, since this code is not needed for most clients
 #include <algorithm>
 #include <cassert>
 #include <unordered_map>
@@ -24,21 +23,67 @@
 #include "flatbuffers/flatbuffers.h"
 #include "flatbuffers/idl.h"
 #include "flatbuffers/util.h"
+#include "idl_namer.h"
 
 namespace flatbuffers {
-
+namespace {
 struct ImportDefinition {
   std::string name;
   std::string import_statement;
   std::string export_statement;
   std::string bare_file_path;
   std::string rel_file_path;
+  std::string object_name;
   const Definition *dependent = nullptr;
   const Definition *dependency = nullptr;
 };
 
+Namer::Config TypeScriptDefaultConfig() {
+  return { /*types=*/Case::kKeep,
+           /*constants=*/Case::kUnknown,
+           /*methods=*/Case::kLowerCamel,
+           /*functions=*/Case::kLowerCamel,
+           /*fields=*/Case::kLowerCamel,
+           /*variables=*/Case::kLowerCamel,
+           /*variants=*/Case::kKeep,
+           /*enum_variant_seperator=*/"::",
+           /*escape_keywords=*/Namer::Config::Escape::AfterConvertingCase,
+           /*namespaces=*/Case::kKeep,
+           /*namespace_seperator=*/"_",
+           /*object_prefix=*/"",
+           /*object_suffix=*/"T",
+           /*keyword_prefix=*/"",
+           /*keyword_suffix=*/"_",
+           /*filenames=*/Case::kDasher,
+           /*directories=*/Case::kDasher,
+           /*output_path=*/"",
+           /*filename_suffix=*/"_generated",
+           /*filename_extension=*/".ts" };
+}
+
+std::set<std::string> TypescriptKeywords() {
+  // List of keywords retrieved from here:
+  // https://github.com/microsoft/TypeScript/issues/2536
+  return {
+    "arguments", "break",    "case",      "catch",      "class",      "const",
+    "continue",  "debugger", "default",   "delete",     "do",         "else",
+    "enum",      "export",   "extends",   "false",      "finally",    "for",
+    "function",  "if",       "import",    "in",         "instanceof", "new",
+    "null",      "Object",   "return",    "super",      "switch",     "this",
+    "throw",     "true",     "try",       "typeof",     "var",        "void",
+    "while",     "with",     "as",        "implements", "interface",  "let",
+    "package",   "private",  "protected", "public",     "static",     "yield",
+  };
+}
+
 enum AnnotationType { kParam = 0, kType = 1, kReturns = 2 };
 
+template<typename T> struct SupportsObjectAPI : std::false_type {};
+
+template<> struct SupportsObjectAPI<StructDef> : std::true_type {};
+
+}  // namespace
+
 namespace ts {
 // Iterate through all definitions we haven't generate code for (enums, structs,
 // and tables) and output them to a single file.
@@ -48,83 +93,53 @@
 
   TsGenerator(const Parser &parser, const std::string &path,
               const std::string &file_name)
-      : BaseGenerator(parser, path, file_name, "", ".", "ts") {
-    // clang-format off
+      : BaseGenerator(parser, path, file_name, "", "_", "ts"),
+        namer_(WithFlagOptions(TypeScriptDefaultConfig(), parser.opts, path),
+               TypescriptKeywords()) {}
 
-    // List of keywords retrieved from here:
-    // https://github.com/microsoft/TypeScript/issues/2536
-    // One per line to ease comparisons to that list are easier
-    static const char *const keywords[] = {
-      "argument",
-      "break",
-      "case",
-      "catch",
-      "class",
-      "const",
-      "continue",
-      "debugger",
-      "default",
-      "delete",
-      "do",
-      "else",
-      "enum",
-      "export",
-      "extends",
-      "false",
-      "finally",
-      "for",
-      "function",
-      "if",
-      "import",
-      "in",
-      "instanceof",
-      "new",
-      "null",
-      "Object",
-      "return",
-      "super",
-      "switch",
-      "this",
-      "throw",
-      "true",
-      "try",
-      "typeof",
-      "var",
-      "void",
-      "while",
-      "with",
-      "as",
-      "implements",
-      "interface",
-      "let",
-      "package",
-      "private",
-      "protected",
-      "public",
-      "static",
-      "yield",
-      nullptr,
-      // clang-format on
-    };
-
-    for (auto kw = keywords; *kw; kw++) keywords_.insert(*kw);
-  }
   bool generate() {
-    if (parser_.opts.ts_flat_file && parser_.opts.generate_all) {
-      // Not implemented; warning message should have beem emitted by flatc.
-      return false;
-    }
     generateEnums();
     generateStructs();
     generateEntry();
     return true;
   }
 
+  bool IncludeNamespace() const {
+    // When generating a single flat file and all its includes, namespaces are
+    // important to avoid type name clashes.
+    return parser_.opts.ts_flat_file && parser_.opts.generate_all;
+  }
+
+  std::string GetTypeName(const EnumDef &def, const bool = false,
+                          const bool force_ns_wrap = false) {
+    if (IncludeNamespace() || force_ns_wrap) {
+      return namer_.NamespacedType(def);
+    }
+    return namer_.Type(def);
+  }
+
+  std::string GetTypeName(const StructDef &def, const bool object_api = false,
+                          const bool force_ns_wrap = false) {
+    if (object_api && parser_.opts.generate_object_based_api) {
+      if (IncludeNamespace() || force_ns_wrap) {
+        return namer_.NamespacedObjectType(def);
+      } else {
+        return namer_.ObjectType(def);
+      }
+    } else {
+      if (IncludeNamespace() || force_ns_wrap) {
+        return namer_.NamespacedType(def);
+      } else {
+        return namer_.Type(def);
+      }
+    }
+  }
+
   // Save out the generated code for a single class while adding
   // declaration boilerplate.
-  bool SaveType(const Definition &definition, const std::string &classcode,
+  bool SaveType(const Definition &definition, const std::string &class_code,
                 import_set &imports, import_set &bare_imports) {
-    if (!classcode.length()) return true;
+    if (!class_code.length()) return true;
 
     std::string code;
 
@@ -144,25 +159,24 @@
       if (!imports.empty()) code += "\n\n";
     }
 
-    code += classcode;
-    auto filename =
-        NamespaceDir(*definition.defined_namespace, true) +
-        ConvertCase(definition.name, Case::kDasher, Case::kUpperCamel) + ".ts";
+    code += class_code;
+
     if (parser_.opts.ts_flat_file) {
       flat_file_ += code;
+      flat_file_ += "\n";
       flat_file_definitions_.insert(&definition);
       return true;
     } else {
-      return SaveFile(filename.c_str(), code, false);
+      auto dirs = namer_.Directories(*definition.defined_namespace);
+      EnsureDirExists(dirs);
+      auto basename = dirs + namer_.File(definition, SkipFile::Suffix);
+
+      return SaveFile(basename.c_str(), code, false);
     }
   }
 
  private:
-  std::unordered_set<std::string> keywords_;
-
-  std::string EscapeKeyword(const std::string &name) const {
-    return keywords_.find(name) == keywords_.end() ? name : name + "_";
-  }
+  IdlNamer namer_;
 
   import_set imports_all_;
 
@@ -177,6 +191,10 @@
   // This maps from import names to types to import.
   std::map<std::string, std::map<std::string, std::string>>
       flat_file_import_declarations_;
+  // For flat file codegen, tracks whether we need to import the flatbuffers
+  // library itself (not necessary for files that solely consist of enum
+  // definitions).
+  bool import_flatbuffers_lib_ = false;
 
   // Generate code for all enums.
   void generateEnums() {
@@ -200,6 +218,7 @@
       import_set bare_imports;
       import_set imports;
       AddImport(bare_imports, "* as flatbuffers", "flatbuffers");
+      import_flatbuffers_lib_ = true;
       auto &struct_def = **it;
       std::string declcode;
       GenStruct(parser_, struct_def, &declcode, imports);
@@ -210,38 +229,45 @@
 
   // Generate code for a single entry point module.
   void generateEntry() {
-    std::string code;
+    std::string code =
+        "// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n";
     if (parser_.opts.ts_flat_file) {
-      code += "import * as flatbuffers from 'flatbuffers';\n";
-      for (const auto &it : flat_file_import_declarations_) {
-        // Note that we do end up generating an import for ourselves, which
-        // should generally be harmless.
-        // TODO: Make it so we don't generate a self-import; this will also
-        // require modifying AddImport to ensure that we don't use
-        // namespace-prefixed names anywhere...
-        std::string file = it.first;
-        if (file.empty()) {
-          continue;
-        }
-        std::string noext = flatbuffers::StripExtension(file);
-        std::string basename = flatbuffers::StripPath(noext);
-        std::string include_file = GeneratedFileName(
-            parser_.opts.include_prefix,
-            parser_.opts.keep_include_path ? noext : basename, parser_.opts);
-        // TODO: what is the right behavior when different include flags are
-        // specified here? Should we always be adding the "./" for a relative
-        // path or turn it off if --include-prefix is specified, or something
-        // else?
-        std::string include_name = "./" + flatbuffers::StripExtension(include_file);
-        code += "import {";
-        for (const auto &pair : it.second) {
-          code += EscapeKeyword(pair.first) + " as " +
-                  EscapeKeyword(pair.second) + ", ";
-        }
-        code.resize(code.size() - 2);
-        code += "} from  '" + include_name + "';\n";
+      if (import_flatbuffers_lib_) {
+        code += "import * as flatbuffers from 'flatbuffers';\n";
+        code += "\n";
       }
-      code += "\n\n";
+      // Only include import statements when not generating all.
+      if (!parser_.opts.generate_all) {
+        for (const auto &it : flat_file_import_declarations_) {
+          // Note that we do end up generating an import for ourselves, which
+          // should generally be harmless.
+          // TODO: Make it so we don't generate a self-import; this will also
+          // require modifying AddImport to ensure that we don't use
+          // namespace-prefixed names anywhere...
+          std::string file = it.first;
+          if (file.empty()) { continue; }
+          std::string noext = flatbuffers::StripExtension(file);
+          std::string basename = flatbuffers::StripPath(noext);
+          std::string include_file = GeneratedFileName(
+              parser_.opts.include_prefix,
+              parser_.opts.keep_prefix ? noext : basename, parser_.opts);
+          // TODO: what is the right behavior when different include flags are
+          // specified here? Should we always be adding the "./" for a relative
+          // path or turn it off if --include-prefix is specified, or something
+          // else?
+          std::string include_name =
+              "./" + flatbuffers::StripExtension(include_file);
+          code += "import {";
+          for (const auto &pair : it.second) {
+            code += namer_.EscapeKeyword(pair.first) + " as " +
+                    namer_.EscapeKeyword(pair.second) + ", ";
+          }
+          code.resize(code.size() - 2);
+          code += "} from '" + include_name + ".js';\n";
+        }
+        code += "\n";
+      }
+
       code += flat_file_;
       const std::string filename =
           GeneratedFileName(path_, file_name_, parser_.opts);
@@ -250,7 +276,8 @@
       for (auto it = imports_all_.begin(); it != imports_all_.end(); it++) {
         code += it->second.export_statement + "\n";
       }
-      std::string path = "./" + path_ + file_name_ + ".ts";
+      const std::string path =
+          GeneratedFileName(path_, file_name_, parser_.opts);
       SaveFile(path.c_str(), code, false);
     }
   }
@@ -286,7 +313,9 @@
     if (reverse) return;  // FIXME.
     std::string &code = *code_ptr;
     GenDocComment(enum_def.doc_comment, code_ptr);
-    code += "export enum " + EscapeKeyword(enum_def.name) + "{\n";
+    code += "export enum ";
+    code += GetTypeName(enum_def);
+    code += " {\n";
     for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
       auto &ev = **it;
       if (!ev.doc_comment.empty()) {
@@ -294,15 +323,13 @@
         GenDocComment(ev.doc_comment, code_ptr, "  ");
       }
 
-      const std::string escaped_name = EscapeKeyword(ev.name);
-
       // Generate mapping between EnumName: EnumValue(int)
       if (reverse) {
         code += "  '" + enum_def.ToString(ev) + "'";
         code += " = ";
-        code += "'" + escaped_name + "'";
+        code += "'" + namer_.Variant(ev) + "'";
       } else {
-        code += "  " + escaped_name;
+        code += "  " + namer_.Variant(ev);
         code += " = ";
         // Unfortunately, because typescript does not support bigint enums,
         // for 64-bit enums, we instead map the enum names to strings.
@@ -324,7 +351,7 @@
       code += GenUnionConvFunc(enum_def.underlying_type, imports);
     }
 
-    code += "\n\n";
+    code += "\n";
   }
 
   static std::string GenType(const Type &type) {
@@ -359,8 +386,8 @@
         return GenBBAccess() + ".__union_with_string" + arguments;
       case BASE_TYPE_VECTOR: return GenGetter(type.VectorType(), arguments);
       default: {
-        auto getter = GenBBAccess() + ".read" +
-                      ConvertCase(GenType(type), Case::kUpperCamel) + arguments;
+        auto getter = GenBBAccess() + "." +
+                      namer_.Method("read_" + GenType(type)) + arguments;
         if (type.base_type == BASE_TYPE_BOOL) { getter = "!!" + getter; }
         return getter;
       }
@@ -385,9 +412,10 @@
         }
         default: {
           if (auto val = value.type.enum_def->FindByValue(value.constant)) {
-            return EscapeKeyword(AddImport(imports, *value.type.enum_def,
-                                           *value.type.enum_def)) +
-                   "." + EscapeKeyword(val->name);
+            return AddImport(imports, *value.type.enum_def,
+                             *value.type.enum_def)
+                       .name +
+                   "." + namer_.Variant(*val);
           } else {
             return value.constant;
           }
@@ -411,7 +439,9 @@
         return "BigInt('" + value.constant + "')";
       }
 
-      default: return value.constant;
+      default:
+        if (value.constant == "nan") { return "NaN"; }
+        return value.constant;
     }
   }
 
@@ -424,7 +454,7 @@
         if (IsString(type)) {
           name = "string|Uint8Array";
         } else {
-          name = EscapeKeyword(AddImport(imports, owner, *type.struct_def));
+          name = AddImport(imports, owner, *type.struct_def).name;
         }
         return allowNull ? (name + "|null") : name;
       }
@@ -437,7 +467,8 @@
       default:
         if (IsScalar(type.base_type)) {
           if (type.enum_def) {
-            const auto enum_name = AddImport(imports, owner, *type.enum_def);
+            const auto enum_name =
+                AddImport(imports, owner, *type.enum_def).name;
             return allowNull ? (enum_name + "|null") : enum_name;
           }
           return allowNull ? "number|null" : "number";
@@ -447,7 +478,7 @@
   }
 
   // Returns the method name for use with add/put calls.
-  static std::string GenWriteMethod(const Type &type) {
+  std::string GenWriteMethod(const Type &type) {
     // Forward to signed versions since unsigned versions don't exist
     switch (type.base_type) {
       case BASE_TYPE_UTYPE:
@@ -458,9 +489,8 @@
       default: break;
     }
 
-    return IsScalar(type.base_type)
-               ? ConvertCase(GenType(type), Case::kUpperCamel)
-               : (IsStruct(type) ? "Struct" : "Offset");
+    return IsScalar(type.base_type) ? namer_.Type(GenType(type))
+                                    : (IsStruct(type) ? "Struct" : "Offset");
   }
 
   template<typename T> static std::string MaybeAdd(T value) {
@@ -490,8 +520,8 @@
     }
   }
 
-  static void GenStructBody(const StructDef &struct_def, std::string *body,
-                            const std::string &nameprefix) {
+  void GenStructBody(const StructDef &struct_def, std::string *body,
+                     const std::string &nameprefix) {
     *body += "  builder.prep(";
     *body += NumToString(struct_def.minalign) + ", ";
     *body += NumToString(struct_def.bytesize) + ");\n";
@@ -517,7 +547,7 @@
   }
 
   std::string GenerateNewExpression(const std::string &object_name) {
-    return "new " + EscapeKeyword(object_name) + "()";
+    return "new " + namer_.Type(object_name) + "()";
   }
 
   void GenerateRootAccessor(StructDef &struct_def, std::string *code_ptr,
@@ -563,16 +593,6 @@
     }
   }
 
-  static std::string GetObjApiClassName(const StructDef &sd,
-                                        const IDLOptions &opts) {
-    return GetObjApiClassName(sd.name, opts);
-  }
-
-  static std::string GetObjApiClassName(const std::string &name,
-                                        const IDLOptions &opts) {
-    return opts.object_prefix + name + opts.object_suffix;
-  }
-
   bool UnionHasStringType(const EnumDef &union_enum) {
     return std::any_of(union_enum.Vals().begin(), union_enum.Vals().end(),
                        [](const EnumVal *ev) {
@@ -601,7 +621,7 @@
       if (IsString(ev.union_type)) {
         type = "string";  // no need to wrap string type in namespace
       } else if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
-        type = AddImport(imports, union_enum, *ev.union_type.struct_def);
+        type = AddImport(imports, union_enum, *ev.union_type.struct_def).name;
       } else {
         FLATBUFFERS_ASSERT(false);
       }
@@ -615,156 +635,140 @@
     return ret;
   }
 
-  std::string AddImport(import_set &imports, const Definition &dependent,
-                        const StructDef &dependency) {
-    std::string ns;
-    const auto &depc_comps = dependency.defined_namespace->components;
-    for (auto it = depc_comps.begin(); it != depc_comps.end(); it++) {
-      ns += *it;
-    }
-    std::string unique_name = ns + dependency.name;
-    std::string import_name = dependency.name;
-    std::string long_import_name;
-    if (imports.find(unique_name) != imports.end())
-      return imports.find(unique_name)->second.name;
+  static bool CheckIfNameClashes(const import_set &imports,
+                                 const std::string &name) {
+    // TODO: this would be better as a hashset.
     for (auto it = imports.begin(); it != imports.end(); it++) {
-      if (it->second.name == import_name) {
-        long_import_name = ns + import_name;
-        break;
-      }
+      if (it->second.name == name) { return true; }
     }
-
-    if (parser_.opts.ts_flat_file) {
-      std::string file = dependency.declaration_file == nullptr
-                                   ? dependency.file
-                                   : dependency.declaration_file->substr(2);
-      file = RelativeToRootPath(StripFileName(AbsolutePath(dependent.file)),
-                                dependency.file).substr(2);
-      long_import_name = ns + import_name;
-      flat_file_import_declarations_[file][import_name] = long_import_name;
-      if (parser_.opts.generate_object_based_api) {
-        flat_file_import_declarations_[file][import_name + "T"] = long_import_name + "T";
-      }
-    }
-
-    std::string import_statement;
-    std::string export_statement;
-    import_statement += "import { ";
-    export_statement += "export { ";
-    std::string symbols_expression;
-    if (long_import_name.empty()) {
-      symbols_expression += EscapeKeyword(import_name);
-      if (parser_.opts.generate_object_based_api)
-        symbols_expression += ", " + import_name + "T";
-    } else {
-      symbols_expression += EscapeKeyword(dependency.name) + " as " +
-                            EscapeKeyword(long_import_name);
-      if (parser_.opts.generate_object_based_api)
-        symbols_expression +=
-            ", " + dependency.name + "T as " + long_import_name + "T";
-    }
-    import_statement += symbols_expression + " } from '";
-    export_statement += symbols_expression + " } from '";
-    std::string bare_file_path;
-    std::string rel_file_path;
-    const auto &dep_comps = dependent.defined_namespace->components;
-    for (size_t i = 0; i < dep_comps.size(); i++)
-      rel_file_path += i == 0 ? ".." : (kPathSeparator + std::string(".."));
-    if (dep_comps.size() == 0) rel_file_path += ".";
-    for (auto it = depc_comps.begin(); it != depc_comps.end(); it++)
-      bare_file_path +=
-          kPathSeparator + ConvertCase(*it, Case::kDasher, Case::kUpperCamel);
-    bare_file_path +=
-        kPathSeparator +
-        ConvertCase(dependency.name, Case::kDasher, Case::kUpperCamel);
-    rel_file_path += bare_file_path;
-    import_statement += rel_file_path + "';";
-    export_statement += "." + bare_file_path + "';";
-    ImportDefinition import;
-    import.name = long_import_name.empty() ? import_name : long_import_name;
-    import.bare_file_path = bare_file_path;
-    import.rel_file_path = rel_file_path;
-    import.import_statement = import_statement;
-    import.export_statement = export_statement;
-    import.dependency = &dependency;
-    import.dependent = &dependent;
-    imports.insert(std::make_pair(unique_name, import));
-    return import.name;
+    return false;
   }
 
-  // TODO: largely (but not identical) duplicated code from above couln't find a
-  // good way to refactor
-  std::string AddImport(import_set &imports, const Definition &dependent,
-                        const EnumDef &dependency) {
-    std::string ns;
-    const auto &depc_comps = dependency.defined_namespace->components;
-    for (auto it = depc_comps.begin(); it != depc_comps.end(); it++) {
-      ns += *it;
-    }
-    std::string unique_name = ns + dependency.name;
-    std::string import_name = EscapeKeyword(dependency.name);
-    std::string long_import_name;
-    if (imports.find(unique_name) != imports.end()) {
-      return imports.find(unique_name)->second.name;
-    }
-    for (auto it = imports.begin(); it != imports.end(); it++) {
-      if (it->second.name == import_name) {
-        long_import_name = ns + import_name;
-        break;
+  std::string GenSymbolExpression(const StructDef &struct_def,
+                                  const bool has_name_clash,
+                                  const std::string &import_name,
+                                  const std::string &name,
+                                  const std::string &object_name) {
+    std::string symbols_expression;
+
+    if (has_name_clash) {
+      // We have a name clash
+      symbols_expression += import_name + " as " + name;
+
+      if (parser_.opts.generate_object_based_api) {
+        symbols_expression += ", " +
+                              GetTypeName(struct_def, /*object_api =*/true) +
+                              " as " + object_name;
+      }
+    } else {
+      // No name clash, use the provided name
+      symbols_expression += name;
+
+      if (parser_.opts.generate_object_based_api) {
+        symbols_expression += ", " + object_name;
       }
     }
 
-    if (parser_.opts.ts_flat_file) {
-      std::string file = dependency.declaration_file == nullptr
-                                   ? dependency.file
-                                   : dependency.declaration_file->substr(2);
-      file = RelativeToRootPath(StripFileName(AbsolutePath(dependent.file)),
-                                dependency.file).substr(2);
-      long_import_name = ns + import_name;
-      flat_file_import_declarations_[file][import_name] = long_import_name;
+    return symbols_expression;
+  }
+
+  std::string GenSymbolExpression(const EnumDef &enum_def,
+                                  const bool has_name_clash,
+                                  const std::string &import_name,
+                                  const std::string &name,
+                                  const std::string &) {
+    std::string symbols_expression;
+    if (has_name_clash) {
+      symbols_expression += import_name + " as " + name;
+    } else {
+      symbols_expression += name;
     }
 
-    std::string import_statement;
-    std::string export_statement;
-    import_statement += "import { ";
-    export_statement += "export { ";
-    std::string symbols_expression;
-    if (long_import_name.empty())
-      symbols_expression += import_name;
-    else
-      symbols_expression += EscapeKeyword(dependency.name) + " as " +
-                            EscapeKeyword(long_import_name);
-    if (dependency.is_union) {
-      symbols_expression += ", unionTo" + import_name;
-      symbols_expression += ", unionListTo" + import_name;
+    if (enum_def.is_union) {
+      symbols_expression += ", unionTo" + name;
+      symbols_expression += ", unionListTo" + name;
     }
-    import_statement += symbols_expression + " } from '";
-    export_statement += symbols_expression + " } from '";
+
+    return symbols_expression;
+  }
+
+  template<typename DefinitionT>
+  ImportDefinition AddImport(import_set &imports, const Definition &dependent,
+                             const DefinitionT &dependency) {
+    // The unique name of the dependency, fully qualified in its namespace.
+    const std::string unique_name = GetTypeName(
+        dependency, /*object_api = */ false, /*force_ns_wrap=*/true);
+
+    // Look if we have already added this import and return its name if found.
+    const auto import_pair = imports.find(unique_name);
+    if (import_pair != imports.end()) { return import_pair->second; }
+
+    // Check if this name would have a name clash with another type. Just use
+    // the "base" name (properly escaped) without any namespacing applied.
+    const std::string import_name = GetTypeName(dependency);
+    const bool has_name_clash = CheckIfNameClashes(imports, import_name);
+
+    // If we have a name clash, use the unique name, otherwise use simple name.
+    std::string name = has_name_clash ? unique_name : import_name;
+
+    const std::string object_name =
+        GetTypeName(dependency, /*object_api=*/true, has_name_clash);
+
+    if (parser_.opts.ts_flat_file) {
+      // In flat-file generation, do not attempt to import things from ourselves
+      // *and* do not wrap namespaces (note that this does override the logic
+      // above, but since we force all non-self-imports to use namespace-based
+      // names in flat file generation, it's fine).
+      if (dependent.file == dependency.file) {
+        name = import_name;
+      } else {
+        const std::string file =
+            RelativeToRootPath(StripFileName(AbsolutePath(dependent.file)),
+                               dependency.file)
+                // Strip the leading //
+                .substr(2);
+        flat_file_import_declarations_[file][import_name] = name;
+
+        if (parser_.opts.generate_object_based_api &&
+            SupportsObjectAPI<DefinitionT>::value) {
+          flat_file_import_declarations_[file][import_name + "T"] = object_name;
+        }
+      }
+    }
+
+    const std::string symbols_expression = GenSymbolExpression(
+        dependency, has_name_clash, import_name, name, object_name);
+
     std::string bare_file_path;
     std::string rel_file_path;
     const auto &dep_comps = dependent.defined_namespace->components;
-    for (size_t i = 0; i < dep_comps.size(); i++)
+    for (size_t i = 0; i < dep_comps.size(); i++) {
       rel_file_path += i == 0 ? ".." : (kPathSeparator + std::string(".."));
-    if (dep_comps.size() == 0) rel_file_path += ".";
-    for (auto it = depc_comps.begin(); it != depc_comps.end(); it++)
-      bare_file_path +=
-          kPathSeparator + ConvertCase(*it, Case::kDasher, Case::kUpperCamel);
+    }
+    if (dep_comps.size() == 0) { rel_file_path += "."; }
+
     bare_file_path +=
         kPathSeparator +
-        ConvertCase(dependency.name, Case::kDasher, Case::kUpperCamel);
+        namer_.Directories(dependency.defined_namespace->components,
+                           SkipDir::OutputPath) +
+        namer_.File(dependency, SkipFile::SuffixAndExtension);
     rel_file_path += bare_file_path;
-    import_statement += rel_file_path + "';";
-    export_statement += "." + bare_file_path + "';";
+
     ImportDefinition import;
-    import.name = long_import_name.empty() ? import_name : long_import_name;
+    import.name = name;
+    import.object_name = object_name;
     import.bare_file_path = bare_file_path;
     import.rel_file_path = rel_file_path;
-    import.import_statement = import_statement;
-    import.export_statement = export_statement;
+    import.import_statement =
+        "import { " + symbols_expression + " } from '" + rel_file_path + ".js';";
+    import.export_statement =
+        "export { " + symbols_expression + " } from '." + bare_file_path + ".js';";
     import.dependency = &dependency;
     import.dependent = &dependent;
+
     imports.insert(std::make_pair(unique_name, import));
-    return import.name;
+
+    return import;
   }
 
   void AddImport(import_set &imports, std::string import_name,
@@ -777,7 +781,9 @@
   }
 
   // Generate a TS union type based on a union's enum
-  std::string GenObjApiUnionTypeTS(import_set &imports, const IDLOptions &opts,
+  std::string GenObjApiUnionTypeTS(import_set &imports,
+                                   const StructDef &dependent,
+                                   const IDLOptions &,
                                    const EnumDef &union_enum) {
     std::string ret = "";
     std::set<std::string> type_list;
@@ -791,8 +797,8 @@
       if (IsString(ev.union_type)) {
         type = "string";  // no need to wrap string type in namespace
       } else if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
-        type = GetObjApiClassName(
-            AddImport(imports, union_enum, *ev.union_type.struct_def), opts);
+        type = AddImport(imports, dependent, *ev.union_type.struct_def)
+                   .object_name;
       } else {
         FLATBUFFERS_ASSERT(false);
       }
@@ -809,11 +815,11 @@
   }
 
   std::string GenUnionConvFuncName(const EnumDef &enum_def) {
-    return "unionTo" + enum_def.name;
+    return namer_.Function("unionTo", enum_def);
   }
 
   std::string GenUnionListConvFuncName(const EnumDef &enum_def) {
-    return "unionListTo" + enum_def.name;
+    return namer_.Function("unionListTo", enum_def);
   }
 
   std::string GenUnionConvFunc(const Type &union_type, import_set &imports) {
@@ -824,12 +830,12 @@
       const auto valid_union_type_with_null = valid_union_type + "|null";
 
       auto ret = "\n\nexport function " + GenUnionConvFuncName(enum_def) +
-                 "(\n  type: " + enum_def.name +
+                 "(\n  type: " + GetTypeName(enum_def) +
                  ",\n  accessor: (obj:" + valid_union_type + ") => " +
                  valid_union_type_with_null +
                  "\n): " + valid_union_type_with_null + " {\n";
 
-      const auto enum_type = AddImport(imports, enum_def, enum_def);
+      const auto enum_type = AddImport(imports, enum_def, enum_def).name;
 
       const auto union_enum_loop = [&](const std::string &accessor_str) {
         ret += "  switch(" + enum_type + "[type]) {\n";
@@ -840,13 +846,13 @@
           const auto &ev = **it;
           if (ev.IsZero()) { continue; }
 
-          ret += "    case '" + ev.name + "': ";
+          ret += "    case '" + namer_.Variant(ev) + "': ";
 
           if (IsString(ev.union_type)) {
             ret += "return " + accessor_str + "'') as string;";
           } else if (ev.union_type.base_type == BASE_TYPE_STRUCT) {
             const auto type =
-                AddImport(imports, enum_def, *ev.union_type.struct_def);
+                AddImport(imports, enum_def, *ev.union_type.struct_def).name;
             ret += "return " + accessor_str + "new " + type + "())! as " +
                    type + ";";
           } else {
@@ -863,7 +869,7 @@
       ret += "}";
 
       ret += "\n\nexport function " + GenUnionListConvFuncName(enum_def) +
-             "(\n  type: " + enum_def.name +
+             "(\n  type: " + GetTypeName(enum_def) +
              ", \n  accessor: (index: number, obj:" + valid_union_type +
              ") => " + valid_union_type_with_null +
              ", \n  index: number\n): " + valid_union_type_with_null + " {\n";
@@ -879,12 +885,13 @@
   // Used for generating a short function that returns the correct class
   // based on union enum type. Assume the context is inside the non object api
   // type
-  std::string GenUnionValTS(import_set &imports, const std::string &field_name,
+  std::string GenUnionValTS(import_set &imports, const StructDef &dependent,
+                            const std::string &field_name,
                             const Type &union_type,
                             const bool is_array = false) {
     if (union_type.enum_def) {
       const auto &enum_def = *union_type.enum_def;
-      const auto enum_type = AddImport(imports, enum_def, enum_def);
+      const auto enum_type = AddImport(imports, dependent, enum_def).name;
       const std::string union_accessor = "this." + field_name;
 
       const auto union_has_string = UnionHasStringType(enum_def);
@@ -894,11 +901,11 @@
 
       if (!is_array) {
         const auto conversion_function = GenUnionConvFuncName(enum_def);
-        const auto target_enum = "this." + field_name + "Type()";
 
         ret = "(() => {\n";
-        ret += "      let temp = " + conversion_function + "(" + target_enum +
-               ", " + field_binded_method + ");\n";
+        ret += "      let temp = " + conversion_function + "(this." +
+               namer_.Method(field_name, "Type") + "(), " +
+               field_binded_method + ");\n";
         ret += "      if(temp === null) { return null; }\n";
         ret += union_has_string
                    ? "      if(typeof temp === 'string') { return temp; }\n"
@@ -907,17 +914,15 @@
         ret += "  })()";
       } else {
         const auto conversion_function = GenUnionListConvFuncName(enum_def);
-        const auto target_enum_accesor = "this." + field_name + "Type";
-        const auto target_enum_length = target_enum_accesor + "Length()";
 
         ret = "(() => {\n";
         ret += "    let ret = [];\n";
-        ret += "    for(let targetEnumIndex = 0; targetEnumIndex < " +
-               target_enum_length +
+        ret += "    for(let targetEnumIndex = 0; targetEnumIndex < this." +
+               namer_.Method(field_name, "TypeLength") + "()" +
                "; "
                "++targetEnumIndex) {\n";
-        ret += "      let targetEnum = " + target_enum_accesor +
-               "(targetEnumIndex);\n";
+        ret += "      let targetEnum = this." +
+               namer_.Method(field_name, "Type") + "(targetEnumIndex);\n";
         ret += "      if(targetEnum === null || " + enum_type +
                "[targetEnum!] === 'NONE') { "
                "continue; }\n\n";
@@ -956,15 +961,20 @@
          it != struct_def.fields.vec.end(); ++it) {
       auto &field = **it;
 
-      const auto curr_member_accessor =
-          prefix + "." + ConvertCase(field.name, Case::kLowerCamel);
+      auto curr_member_accessor = prefix + "." + namer_.Method(field);
+      if (prefix != "this") {
+        curr_member_accessor = prefix + "?." + namer_.Method(field);
+      }
       if (IsStruct(field.value.type)) {
         ret += GenStructMemberValueTS(*field.value.type.struct_def,
                                       curr_member_accessor, delimiter);
       } else {
         if (nullCheck) {
-          ret +=
-              "(" + prefix + " === null ? 0 : " + curr_member_accessor + "!)";
+          std::string nullValue = "0";
+          if (field.value.type.base_type == BASE_TYPE_BOOL) {
+            nullValue = "false";
+          }
+          ret += "(" + curr_member_accessor + " ?? " + nullValue + ")";
         } else {
           ret += curr_member_accessor;
         }
@@ -979,7 +989,7 @@
   void GenObjApi(const Parser &parser, StructDef &struct_def,
                  std::string &obj_api_unpack_func, std::string &obj_api_class,
                  import_set &imports) {
-    const auto class_name = GetObjApiClassName(struct_def, parser.opts);
+    const auto class_name = GetTypeName(struct_def, /*object_api=*/true);
 
     std::string unpack_func = "\nunpack(): " + class_name +
                               " {\n  return new " + class_name + "(" +
@@ -999,8 +1009,7 @@
     std::string pack_func_offset_decl;
     std::string pack_func_create_call;
 
-    const auto struct_name =
-        EscapeKeyword(AddImport(imports, struct_def, struct_def));
+    const auto struct_name = AddImport(imports, struct_def, struct_def).name;
 
     if (has_create) {
       pack_func_create_call = "  return " + struct_name + ".create" +
@@ -1023,10 +1032,10 @@
       auto &field = **it;
       if (field.deprecated) continue;
 
-      const auto field_name = ConvertCase(field.name, Case::kLowerCamel);
-      const auto field_name_escaped = EscapeKeyword(field_name);
+      const auto field_method = namer_.Method(field);
+      const auto field_field = namer_.Field(field);
       const std::string field_binded_method =
-          "this." + field_name + ".bind(this)";
+          "this." + field_method + ".bind(this)";
 
       std::string field_val;
       std::string field_type;
@@ -1046,14 +1055,14 @@
 
         field_type += GenTypeName(imports, field, field.value.type, false,
                                   has_null_default);
-        field_val = "this." + field_name + "()";
+        field_val = "this." + namer_.Method(field) + "()";
 
         if (field.value.type.base_type != BASE_TYPE_STRING) {
-          field_offset_val = "this." + field_name_escaped;
+          field_offset_val = "this." + namer_.Field(field);
         } else {
           field_offset_decl = GenNullCheckConditional(
-              "this." + field_name_escaped,
-              "builder.createString(this." + field_name_escaped + "!)", "0");
+              "this." + namer_.Field(field),
+              "builder.createString(this." + field_field + "!)", "0");
         }
       }
 
@@ -1063,17 +1072,15 @@
         switch (field.value.type.base_type) {
           case BASE_TYPE_STRUCT: {
             const auto &sd = *field.value.type.struct_def;
-            field_type += GetObjApiClassName(AddImport(imports, struct_def, sd),
-                                             parser.opts);
+            field_type += AddImport(imports, struct_def, sd).object_name;
 
             const std::string field_accessor =
-                "this." + field_name_escaped + "()";
+                "this." + namer_.Method(field) + "()";
             field_val = GenNullCheckConditional(field_accessor,
                                                 field_accessor + "!.unpack()");
             auto packing = GenNullCheckConditional(
-                "this." + field_name_escaped,
-                "this." + field_name_escaped + "!.pack(builder)",
-                "0");
+                "this." + field_field,
+                "this." + field_field + "!.pack(builder)", "0");
 
             if (sd.fixed) {
               field_offset_val = std::move(packing);
@@ -1095,28 +1102,25 @@
             switch (vectortype.base_type) {
               case BASE_TYPE_STRUCT: {
                 const auto &sd = *field.value.type.struct_def;
-                field_type += GetObjApiClassName(sd, parser.opts);
+                field_type += GetTypeName(sd, /*object_api=*/true);
+                ;
                 field_type += ")[]";
 
                 field_val = GenBBAccess() + ".createObjList(" +
-                            field_binded_method + ", this." + field_name +
-                            "Length())";
+                            field_binded_method + ", this." +
+                            namer_.Method(field, "Length") + "())";
 
                 if (sd.fixed) {
                   field_offset_decl =
-                      "builder.createStructOffsetList(this." +
-                      field_name_escaped + ", " +
-                      EscapeKeyword(
-                          AddImport(imports, struct_def, struct_def)) +
-                      ".start" + ConvertCase(field_name, Case::kUpperCamel) +
-                      "Vector)";
+                      "builder.createStructOffsetList(this." + field_field +
+                      ", " + AddImport(imports, struct_def, struct_def).name +
+                      "." + namer_.Method("start", field, "Vector") + ")";
                 } else {
                   field_offset_decl =
-                      EscapeKeyword(
-                          AddImport(imports, struct_def, struct_def)) +
-                      ".create" + ConvertCase(field_name, Case::kUpperCamel) +
-                      "Vector(builder, builder.createObjectOffsetList(" +
-                      "this." + field_name_escaped + "))";
+                      AddImport(imports, struct_def, struct_def).name + "." +
+                      namer_.Method("create", field, "Vector") +
+                      "(builder, builder.createObjectOffsetList(" + "this." +
+                      field_field + "))";
                 }
 
                 break;
@@ -1125,28 +1129,28 @@
               case BASE_TYPE_STRING: {
                 field_type += "string)[]";
                 field_val = GenBBAccess() + ".createScalarList(" +
-                            field_binded_method + ", this." + field_name +
-                            "Length())";
+                            field_binded_method + ", this." +
+                            namer_.Field(field, "Length") + "())";
                 field_offset_decl =
-                    EscapeKeyword(AddImport(imports, struct_def, struct_def)) +
-                    ".create" + ConvertCase(field_name, Case::kUpperCamel) +
-                    "Vector(builder, builder.createObjectOffsetList(" +
-                    "this." + field_name_escaped + "))";
+                    AddImport(imports, struct_def, struct_def).name + "." +
+                    namer_.Method("create", field, "Vector") +
+                    "(builder, builder.createObjectOffsetList(" + "this." +
+                    namer_.Field(field) + "))";
                 break;
               }
 
               case BASE_TYPE_UNION: {
-                field_type += GenObjApiUnionTypeTS(imports, parser.opts,
-                                                   *(vectortype.enum_def));
+                field_type += GenObjApiUnionTypeTS(
+                    imports, struct_def, parser.opts, *(vectortype.enum_def));
                 field_type += ")[]";
-                field_val =
-                    GenUnionValTS(imports, field_name, vectortype, true);
+                field_val = GenUnionValTS(imports, struct_def, field_method,
+                                          vectortype, true);
 
                 field_offset_decl =
-                    EscapeKeyword(AddImport(imports, struct_def, struct_def)) +
-                    ".create" + ConvertCase(field_name, Case::kUpperCamel) +
-                    "Vector(builder, builder.createObjectOffsetList(" +
-                    "this." + field_name_escaped + "))";
+                    AddImport(imports, struct_def, struct_def).name + "." +
+                    namer_.Method("create", field, "Vector") +
+                    "(builder, builder.createObjectOffsetList(" + "this." +
+                    namer_.Field(field) + "))";
 
                 break;
               }
@@ -1159,13 +1163,13 @@
                 }
                 field_type += ")[]";
                 field_val = GenBBAccess() + ".createScalarList(" +
-                            field_binded_method + ", this." + field_name +
-                            "Length())";
+                            field_binded_method + ", this." +
+                            namer_.Method(field, "Length") + "())";
 
                 field_offset_decl =
-                    EscapeKeyword(AddImport(imports, struct_def, struct_def)) +
-                    ".create" + ConvertCase(field_name, Case::kUpperCamel) +
-                    "Vector(builder, this." + field_name_escaped + ")";
+                    AddImport(imports, struct_def, struct_def).name + "." +
+                    namer_.Method("create", field, "Vector") +
+                    "(builder, this." + field_field + ")";
 
                 break;
               }
@@ -1175,12 +1179,13 @@
           }
 
           case BASE_TYPE_UNION: {
-            field_type += GenObjApiUnionTypeTS(imports, parser.opts,
+            field_type += GenObjApiUnionTypeTS(imports, struct_def, parser.opts,
                                                *(field.value.type.enum_def));
 
-            field_val = GenUnionValTS(imports, field_name, field.value.type);
+            field_val = GenUnionValTS(imports, struct_def, field_method,
+                                      field.value.type);
             field_offset_decl =
-                "builder.createObjectOffset(this." + field_name_escaped + ")";
+                "builder.createObjectOffset(this." + field_field + ")";
             break;
           }
 
@@ -1193,18 +1198,17 @@
 
       if (!field_offset_decl.empty()) {
         field_offset_decl =
-            "  const " + field_name_escaped + " = " + field_offset_decl + ";";
+            "  const " + field_field + " = " + field_offset_decl + ";";
       }
-      if (field_offset_val.empty()) { field_offset_val = field_name_escaped; }
+      if (field_offset_val.empty()) { field_offset_val = field_field; }
 
       unpack_func += "    " + field_val;
-      unpack_to_func += "  _o." + field_name_escaped + " = " + field_val + ";";
+      unpack_to_func += "  _o." + field_field + " = " + field_val + ";";
 
-      // FIXME: if field_type and field_name_escaped are identical, then
+      // FIXME: if field_type and field_field are identical, then
       // this generates invalid typescript.
-      constructor_func += "  public " + field_name_escaped + ": " + field_type +
-                          " = " +
-                          field_default_val;
+      constructor_func += "  public " + field_field + ": " + field_type +
+                          " = " + field_default_val;
 
       if (!struct_def.fixed) {
         if (!field_offset_decl.empty()) {
@@ -1215,11 +1219,12 @@
           pack_func_create_call += field_offset_val;
         } else {
           if (field.IsScalarOptional()) {
-            pack_func_create_call += "  if (" + field_offset_val + " !== null)\n  ";
+            pack_func_create_call +=
+                "  if (" + field_offset_val + " !== null)\n  ";
           }
-          pack_func_create_call += "  " + struct_name + ".add" +
-                                   ConvertCase(field.name, Case::kUpperCamel) +
-                                   "(builder, " + field_offset_val + ");\n";
+          pack_func_create_call += "  " + struct_name + "." +
+                                   namer_.Method("add", field) + "(builder, " +
+                                   field_offset_val + ");\n";
         }
       }
 
@@ -1252,10 +1257,10 @@
       pack_func_create_call += "return " + struct_name + ".end" +
                                GetPrefixedName(struct_def) + "(builder);";
     }
-
-    obj_api_class = "\nexport class " +
-                    GetObjApiClassName(struct_def, parser.opts) + " {\n";
-
+    obj_api_class = "\n";
+    obj_api_class += "export class ";
+    obj_api_class += GetTypeName(struct_def, /*object_api=*/true);
+    obj_api_class += " {\n";
     obj_api_class += constructor_func;
     obj_api_class += pack_func_prototype + pack_func_offset_decl +
                      pack_func_create_call + "\n}";
@@ -1286,13 +1291,18 @@
     if (struct_def.generated) return;
     std::string &code = *code_ptr;
 
-    std::string object_name;
-    std::string object_namespace = GetNameSpace(struct_def);
+    // Special case for the root struct, since no one will necessarily reference
+    // it, we have to explicitly add it to the import list.
+    if (&struct_def == parser_.root_struct_def_) {
+      AddImport(imports, struct_def, struct_def);
+    }
+
+    const std::string object_name = GetTypeName(struct_def);
 
     // Emit constructor
-    object_name = EscapeKeyword(struct_def.name);
     GenDocComment(struct_def.doc_comment, code_ptr);
-    code += "export class " + object_name;
+    code += "export class ";
+    code += object_name;
     code += " {\n";
     code += "  bb: flatbuffers.ByteBuffer|null = null;\n";
     code += "  bb_pos = 0;\n";
@@ -1300,7 +1310,7 @@
     // Generate the __init method that sets the field in a pre-existing
     // accessor object. This is to allow object reuse.
     code +=
-        "__init(i:number, bb:flatbuffers.ByteBuffer):" + object_name + " {\n";
+        "  __init(i:number, bb:flatbuffers.ByteBuffer):" + object_name + " {\n";
     code += "  this.bb_pos = i;\n";
     code += "  this.bb = bb;\n";
     code += "  return this;\n";
@@ -1337,7 +1347,7 @@
         const auto has_null_default = is_string || HasNullDefault(field);
 
         GenDocComment(field.doc_comment, code_ptr);
-        std::string prefix = ConvertCase(field.name, Case::kLowerCamel) + "(";
+        std::string prefix = namer_.Method(field) + "(";
         if (is_string) {
           code += prefix + "):string|null\n";
           code +=
@@ -1380,10 +1390,11 @@
       else {
         switch (field.value.type.base_type) {
           case BASE_TYPE_STRUCT: {
-            const auto type = EscapeKeyword(
-                AddImport(imports, struct_def, *field.value.type.struct_def));
+            const auto type =
+                AddImport(imports, struct_def, *field.value.type.struct_def)
+                    .name;
             GenDocComment(field.doc_comment, code_ptr);
-            code += ConvertCase(field.name, Case::kLowerCamel);
+            code += namer_.Method(field);
             code += "(obj?:" + type + "):" + type + "|null {\n";
 
             if (struct_def.fixed) {
@@ -1423,7 +1434,7 @@
               default: ret_type = vectortypename;
             }
             GenDocComment(field.doc_comment, code_ptr);
-            std::string prefix = ConvertCase(field.name, Case::kLowerCamel);
+            std::string prefix = namer_.Method(field);
             // TODO: make it work without any
             // if (is_union) { prefix += "<T extends flatbuffers.Table>"; }
             if (is_union) { prefix += ""; }
@@ -1483,7 +1494,7 @@
 
           case BASE_TYPE_UNION: {
             GenDocComment(field.doc_comment, code_ptr);
-            code += ConvertCase(field.name, Case::kLowerCamel);
+            code += namer_.Method(field);
 
             const auto &union_enum = *(field.value.type.enum_def);
             const auto union_type = GenUnionGenericTypeTS(union_enum);
@@ -1508,12 +1519,15 @@
         std::string type =
             GenTypeName(imports, struct_def, field.value.type, true);
 
-        code += "mutate_" + field.name + "(value:" + type + "):boolean {\n";
+        code += namer_.LegacyTsMutateMethod(field) + "(value:" + type +
+                "):boolean {\n";
+
+        const std::string write_method =
+            "." + namer_.Method("write", GenType(field.value.type));
 
         if (struct_def.fixed) {
-          code += "  " + GenBBAccess() + ".write" +
-                  ConvertCase(GenType(field.value.type), Case::kUpperCamel) +
-                  "(this.bb_pos + " + NumToString(field.value.offset) + ", ";
+          code += "  " + GenBBAccess() + write_method + "(this.bb_pos + " +
+                  NumToString(field.value.offset) + ", ";
         } else {
           code += "  const offset = " + GenBBAccess() +
                   ".__offset(this.bb_pos, " + NumToString(field.value.offset) +
@@ -1523,9 +1537,8 @@
           code += "  }\n\n";
 
           // special case for bools, which are treated as uint8
-          code += "  " + GenBBAccess() + ".write" +
-                  ConvertCase(GenType(field.value.type), Case::kUpperCamel) +
-                  "(this.bb_pos + offset, ";
+          code +=
+              "  " + GenBBAccess() + write_method + "(this.bb_pos + offset, ";
           if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; }
         }
 
@@ -1538,8 +1551,8 @@
       if (IsVector(field.value.type)) {
         // Emit a length helper
         GenDocComment(code_ptr);
-        code += ConvertCase(field.name, Case::kLowerCamel);
-        code += "Length():number {\n" + offset_prefix;
+        code += namer_.Method(field, "Length");
+        code += "():number {\n" + offset_prefix;
 
         code +=
             GenBBAccess() + ".__vector_len(this.bb_pos + offset) : 0;\n}\n\n";
@@ -1549,9 +1562,9 @@
         if (IsScalar(vectorType.base_type) && !IsLong(vectorType.base_type)) {
           GenDocComment(code_ptr);
 
-          code += ConvertCase(field.name, Case::kLowerCamel);
-          code += "Array():" + GenType(vectorType) + "Array|null {\n" +
-                  offset_prefix;
+          code += namer_.Method(field, "Array");
+          code +=
+              "():" + GenType(vectorType) + "Array|null {\n" + offset_prefix;
 
           code += "new " + GenType(vectorType) + "Array(" + GenBBAccess() +
                   ".bytes().buffer, " + GenBBAccess() +
@@ -1610,7 +1623,7 @@
 
         // Generate the field insertion method
         GenDocComment(code_ptr);
-        code += "static add" + ConvertCase(field.name, Case::kUpperCamel);
+        code += "static " + namer_.Method("add", field);
         code += "(builder:flatbuffers.Builder, " + argname + ":" +
                 GetArgType(imports, struct_def, field, false) + ") {\n";
         code += "  builder.addField" + GenWriteMethod(field.value.type) + "(";
@@ -1641,8 +1654,8 @@
             GenDocComment(code_ptr);
 
             const std::string sig_begin =
-                "static create" + ConvertCase(field.name, Case::kUpperCamel) +
-                "Vector(builder:flatbuffers.Builder, data:";
+                "static " + namer_.Method("create", field, "Vector") +
+                "(builder:flatbuffers.Builder, data:";
             const std::string sig_end = "):flatbuffers.Offset";
             std::string type =
                 GenTypeName(imports, struct_def, vector_type, true) + "[]";
@@ -1679,8 +1692,9 @@
           // after
           GenDocComment(code_ptr);
 
-          code += "static start" + ConvertCase(field.name, Case::kUpperCamel);
-          code += "Vector(builder:flatbuffers.Builder, numElems:number) {\n";
+          code += "static ";
+          code += namer_.Method("start", field, "Vector");
+          code += "(builder:flatbuffers.Builder, numElems:number) {\n";
           code += "  builder.startVector(" + NumToString(elem_size);
           code += ", numElems, " + NumToString(alignment) + ");\n";
           code += "}\n\n";
@@ -1723,8 +1737,8 @@
         }
 
         code += "):flatbuffers.Offset {\n";
-        code += "  " + object_name + ".start" +
-                GetPrefixedName(struct_def) + "(builder);\n";
+        code += "  " + object_name + ".start" + GetPrefixedName(struct_def) +
+                "(builder);\n";
 
         std::string methodPrefix = object_name;
         for (auto it = struct_def.fields.vec.begin();
@@ -1738,8 +1752,7 @@
             code += "  if (" + arg_name + " !== null)\n  ";
           }
 
-          code += "  " + methodPrefix + ".add" +
-                  ConvertCase(field.name, Case::kUpperCamel) + "(";
+          code += "  " + methodPrefix + "." + namer_.Method("add", field) + "(";
           code += "builder, " + arg_name + ");\n";
         }
 
@@ -1757,10 +1770,9 @@
       code += "}\n";
 
       code += "\n";
-      code += "static deserialize(buffer: Uint8Array):" + EscapeKeyword(name) +
-              " {\n";
-      code += "  return " +
-              EscapeKeyword(AddImport(imports, struct_def, struct_def)) +
+      code += "static deserialize(buffer: Uint8Array):" +
+              namer_.EscapeKeyword(name) + " {\n";
+      code += "  return " + AddImport(imports, struct_def, struct_def).name +
               ".getRootAs" + name + "(new flatbuffers.ByteBuffer(buffer))\n";
       code += "}\n";
     }
@@ -1788,12 +1800,8 @@
   }
 
   std::string GetArgName(const FieldDef &field) {
-    auto argname = ConvertCase(field.name, Case::kLowerCamel);
-    if (!IsScalar(field.value.type.base_type)) {
-      argname += "Offset";
-    } else {
-      argname = EscapeKeyword(argname);
-    }
+    auto argname = namer_.Variable(field);
+    if (!IsScalar(field.value.type.base_type)) { argname += "Offset"; }
     return argname;
   }