microsoft / microsoft/TypeScript

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

Đang mở
#39,977 13 bình luận 0 reaction 0 người được giao Xem trên GitHub

Chưa có ai nhận issue này.

Docs
Ngôn ngữ chính
Go
Star
111k
Fork
14.3k
Merge trung bình
2 ngày 4 giờ
Pull request đã merge (30 ngày)
132

Mô tả

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.

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Hướng nghiên cứu

Tái hiện hành vi bằng các tệp tsconfig, index.ts và my-test.ts được cung cấp, sau đó so sánh đầu ra CommonJS từ TypeScript 3.8.3 và 3.9.7. Kiểm tra đường dẫn của trình biên dịch phát ra __createBinding và __exportStar, sử dụng dự án ví dụ được liên kết làm tài liệu tham khảo. Công việc được xem là hoàn tất khi hành vi CommonJS đã chọn được ghi lại và được xác minh với các trường hợp export và gán lại được mô tả trong issue.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
javascript, typescript
Lĩnh vực
compilers
Loại issue
Lỗi
Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức độ hoạt động
Đình trệ
Độ rõ ràng
Khá rõ ràng
Mức phù hợp với người mới
25/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.