microsoft / microsoft/TypeScript

TypeScript module not follow specification in some cases when compilerOptions.module = commonjs

Abierto
#39,977 13 comentarios 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

Docs
Lenguaje dominante
Go
Estrellas
111k
Forks
14.4k
Merge medio
1 d 19 h
PR fusionados (30 d)
117

Descripción

TypeScript Version: 3.9.7

When, in a index file, it is exported the content of another file, and tsconfig compilerOptions.module = commonJs, tsc transpiles it with read-only accessors, which is not complying with the behavior described in the official documentation

Search Terms: exports accessor, export readonly, export accessor readonly, export getter, compilerOptions module

Code
tsconfig file is configured for commonJs module output

{
  "compilerOptions": {
    "outDir": "dist",
    "rootDir": "src",
    "target": "es2018",
    "module": "commonjs",
    "strict": true
  }
}

File my-test.ts

export function myTest() {
  return 1;
}

File index.ts

export * from "./my-test";

export function myTest2() {
  return 3;
}

Supposing it is transpiled to a dist folder, you can have a javascript file with this code:

const test = require('./dist');
test.myTest = function () {
  return 2;
}

console.log(test.myTest());

Alternative:

import test = require("./index");
Object.defineProperty(test, "myTest", {
  get() {
    return 2;
  },
});

console.log(test.myTest());

This substitution is not production material, but a simulation of mocking/stubbing on unit tests. Libraries like sinon do something like this to achieve the mocking.
Notice that, in a real project, I'm using typescript register + mocha, not running directly the compiled code, but with this setup is easier to simulate the problem.

Expected behavior:
I expect that the js code prints 2;

Actual behavior:
In js alternative 1, It prints 1, in js alternative 2, it throws an error;
The problem happens because of how the index.js is generated. Since 3.9.2, it is generated like this:

 
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
    for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.myTest2 = void 0;
__exportStar(require("./my-test"), exports);
function myTest2() {
    return 3;
}
exports.myTest2 = myTest2;

Notice that, in the line 4 of this code, an accessor is defined.
On version 3.8.3, it generates like this:

"use strict";
function __export(m) {
    for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
Object.defineProperty(exports, "__esModule", { value: true });
__export(require("./my-test"));
function myTest2() {
    return 3;
}
exports.myTest2 = myTest2;

Notice that the function declared in the index.ts was exported correctly, following what is specified in the official documentation, but both generated codes actually are not in total compliance with it.
The problem is that, with the latest version, it also changes the behavior, making read-only the noncompliant exporting, which can break code that relies on it being read-write, like mocking mechanism in unit tests, for example. The unit tests could also have a distinct tsconfig using modules = commonJs just to have such behavior allowing mocking, while the main tsconfig not.

Playground Link: I'm unable to create a playground example as this issue only happens using multiple files, but i created a example project: https://github.com/Farenheith/issue-readonly-exporting-example

Related Issues: this issue but it was marked as work as intended, probably because I didn't notice that the transpiler was not following the compiler option module configuration the first time I opened it.

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Línea de trabajo

Reproduce el comportamiento con los archivos proporcionados tsconfig, index.ts y my-test.ts y, a continuación, compara la salida CommonJS de TypeScript 3.8.3 y 3.9.7. Inspecciona la ruta del compilador que emite __createBinding y __exportStar, usando como referencia el proyecto de ejemplo enlazado. Se considera terminado cuando el comportamiento CommonJS elegido está documentado y verificado con los casos de exportación y reasignación descritos en el issue.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
javascript, typescript
Área
compilers
Tipo de issue
Error
Dificultad
4/5
Tiempo estimado
3-5 días
Estado de actividad
Estancado
Claridad
Bastante claro
Aptitud para principiantes
25/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.