diff --git a/tslang/lib/TypeScript/DeclarationPrinter.cpp b/tslang/lib/TypeScript/DeclarationPrinter.cpp index 3fb0a84a0..e7571125f 100644 --- a/tslang/lib/TypeScript/DeclarationPrinter.cpp +++ b/tslang/lib/TypeScript/DeclarationPrinter.cpp @@ -669,6 +669,27 @@ namespace typescript newline(); } + // index signature ([x: T]: U;) - mirrors the class printer's fix + // (see cross-module-class-indexer-shared-gap-fix memory): without + // this, a reimporting module's InterfaceInfo::indexes stays empty + // and `obj[i]` through the interface fails with "indexer is not + // declared" / "Interface member '.index' can't be found". + // + // Unlike a class's index signature (a plain (arg)->result FunctionType), + // an interface's is built via getInterfaceMethodNameAndType with + // funcGenContext.thisType set, which prepends an opaque `this` input - + // so the real index-argument type is input(1), not input(0) (matching + // getIndexSignatureArgumentAndResultTypes's "first parameter is Opaque" + // branch in MLIRTypeHelper.h). + for (auto indexInfo : interfaceType->indexes) + { + if (!indexInfo.indexSignature || indexInfo.indexSignature.getNumResults() == 0) + continue; + + auto argIndex = indexInfo.indexSignature.getNumInputs() > 1 ? 1 : 0; + printIndexer(indexInfo.indexSignature.getInput(argIndex), indexInfo.indexSignature.getResult(0)); + } + // methods (including static) auto opaqueType = mlir_ts::OpaqueType::get(interfaceType->interfaceType.getContext()); for (auto method : interfaceType->methods) diff --git a/tslang/test/tester/CMakeLists.txt b/tslang/test/tester/CMakeLists.txt index ff4a28e74..9ebc986af 100644 --- a/tslang/test/tester/CMakeLists.txt +++ b/tslang/test/tester/CMakeLists.txt @@ -886,6 +886,7 @@ add_test(NAME test-compile-export-import-class-static COMMAND test-runner "${PRO add_test(NAME test-compile-export-import-class-generic COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/import_class_generic.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_class_generic.ts") add_test(NAME test-compile-export-import-class-accessor COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/import_class_accessor.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_class_accessor.ts") add_test(NAME test-compile-export-import-class-indexer COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/import_class_indexer.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_class_indexer.ts") +add_test(NAME test-compile-export-import-interface-indexer COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/import_interface_indexer.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_interface_indexer.ts") add_test(NAME test-compile-export-import-class-implements-interface-multilevel COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/import_class_implements_interface_multilevel.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_class_implements_interface_multilevel.ts") add_test(NAME test-compile-export-import-class-implements-interface-optional COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/import_class_implements_interface_optional.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_class_implements_interface_optional.ts") add_test(NAME test-compile-export-import-class-implements-interface-abstract COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/import_class_implements_interface_abstract.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_class_implements_interface_abstract.ts") @@ -961,6 +962,7 @@ add_test(NAME test-compile-shared-export-import-class-generic COMMAND test-runne # in DeclarationPrinter.cpp. add_test(NAME test-compile-shared-export-import-class-accessor COMMAND test-runner -shared "${PROJECT_SOURCE_DIR}/test/tester/tests/import_class_accessor.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_class_accessor.ts") add_test(NAME test-compile-shared-export-import-class-indexer COMMAND test-runner -shared "${PROJECT_SOURCE_DIR}/test/tester/tests/import_class_indexer.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_class_indexer.ts") +add_test(NAME test-compile-shared-export-import-interface-indexer COMMAND test-runner -shared "${PROJECT_SOURCE_DIR}/test/tester/tests/import_interface_indexer.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_interface_indexer.ts") add_test(NAME test-compile-shared-export-import-object-literal-with-interface COMMAND test-runner -shared -gctors-as-method "${PROJECT_SOURCE_DIR}/test/tester/tests/import_object_literal_with_interface.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_object_literal_with_interface.ts") add_test(NAME test-compile-shared-export-import-object-literal-untyped COMMAND test-runner -shared -gctors-as-method "${PROJECT_SOURCE_DIR}/test/tester/tests/import_object_literal_untyped.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_object_literal_untyped.ts") add_test(NAME test-compile-shared-export-import-object-literal-untyped-multi-method COMMAND test-runner -shared -gctors-as-method "${PROJECT_SOURCE_DIR}/test/tester/tests/import_object_literal_untyped_multi_method.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_object_literal_untyped_multi_method.ts") @@ -1002,6 +1004,7 @@ add_test(NAME test-jit-shared-export-import-class-generic COMMAND test-runner -j # FIXED: see the matching test-compile-shared-export-import-class-accessor comment above (2026-07-22). add_test(NAME test-jit-shared-export-import-class-accessor COMMAND test-runner -jit -shared "${PROJECT_SOURCE_DIR}/test/tester/tests/import_class_accessor.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_class_accessor.ts") add_test(NAME test-jit-shared-export-import-class-indexer COMMAND test-runner -jit -shared "${PROJECT_SOURCE_DIR}/test/tester/tests/import_class_indexer.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_class_indexer.ts") +add_test(NAME test-jit-shared-export-import-interface-indexer COMMAND test-runner -jit -shared "${PROJECT_SOURCE_DIR}/test/tester/tests/import_interface_indexer.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_interface_indexer.ts") add_test(NAME test-jit-shared-export-import-object-literal-with-interface COMMAND test-runner -jit -shared -gctors-as-method "${PROJECT_SOURCE_DIR}/test/tester/tests/import_object_literal_with_interface.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_object_literal_with_interface.ts") add_test(NAME test-jit-shared-export-import-object-literal-untyped COMMAND test-runner -jit -shared -gctors-as-method "${PROJECT_SOURCE_DIR}/test/tester/tests/import_object_literal_untyped.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_object_literal_untyped.ts") add_test(NAME test-jit-shared-export-import-object-literal-untyped-multi-method COMMAND test-runner -jit -shared -gctors-as-method "${PROJECT_SOURCE_DIR}/test/tester/tests/import_object_literal_untyped_multi_method.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_object_literal_untyped_multi_method.ts") diff --git a/tslang/test/tester/tests/export_interface_indexer.ts b/tslang/test/tester/tests/export_interface_indexer.ts new file mode 100644 index 000000000..8b9b6eb6e --- /dev/null +++ b/tslang/test/tester/tests/export_interface_indexer.ts @@ -0,0 +1,13 @@ +namespace M { + + // Cross-module counterpart to 00interface_indexer.ts - an exported + // interface with an index signature, cast-to from an importer-side class + // implementation. See cross-module-class-indexer-shared-gap-fix memory: + // DeclarationPrinter never re-emits a class's index signature for + // -shared reimport; interfaces may share the same gap since their + // print() function also never mentions `indexes`/InterfaceIndexInfo. + + export interface Storage { + [index: number]: string; + } +} diff --git a/tslang/test/tester/tests/import_interface_indexer.ts b/tslang/test/tester/tests/import_interface_indexer.ts new file mode 100644 index 000000000..8ec16c8aa --- /dev/null +++ b/tslang/test/tester/tests/import_interface_indexer.ts @@ -0,0 +1,25 @@ +import './export_interface_indexer' + +class Impl { + #val: string = ""; + + [index: number]: string; + + get(index: number): string { + return this.#val; + } + + set(index: number, value: string) { + this.#val = value; + } +} + +function main() { + const impl = new Impl(); + const s: M.Storage = impl; + + s[0] = "hi"; + assert(s[0] == "hi"); + + print("done."); +}