Skip to content

Recursive Node.js require #13245

@calebmer

Description

@calebmer

Recursive modules with exporting files in-between don’t work with TypeScript because export { ... } from '...' statements extract the exported property when the file first runs instead of using a getter to lazily extract the exported property as Babel does.

When Node.js detects recursive modules, they emit the module exports that have been initialized thus far. See module cycles documentation. This means an export may be undefined in the required module when a cycle is detected. However, that export will later be defined when both modules finish their initial execution. Because TypeScript does not use getters there is no way of getting the export when it is later defined.

Here is a concrete example to demonstrate the recursive problem in Node.js. In the bug template is the output expected for a given file. The below example would create an infinite recursive loop when a or b is called. This is the desired behavior. What actually happens is that either b or a is undefined and can’t be called.

./a/foo.ts

import { b } from '../b'

export function a () {
  b()
  return 'foo'
}

./a/index.ts

export { a } from './foo'

./b/bar.ts

import { a } from '../a'

export function b () {
  a()
  return 'bar'
}

./b/index.ts

export { b } from './bar'

Expected/Actual

TypeScript Version: 2.1.4

Code

export { a } from './foo'
export { b } from './bar'

Expected behavior:

Emits getters for CommonJS exports like Babel:

'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _foo = require('./foo');

Object.defineProperty(exports, 'a', {
  enumerable: true,
  get: function get() {
    return _foo.a;
  }
});

var _bar = require('./bar');

Object.defineProperty(exports, 'b', {
  enumerable: true,
  get: function get() {
    return _bar.b;
  }
});

Actual behavior:

Extracts the imported values on file execution so if the reference changes in the required module, that change does not propagate:

"use strict";
var foo_1 = require("./foo");
exports.a = foo_1.a;
var bar_1 = require("./bar");
exports.b = bar_1.b;
### Tasks

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugA bug in TypeScript

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions