Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tsc needlessly emits Object.defineproperty() line when "import" keyword is used. #47563

Closed
KumanekoSakura opened this issue Jan 23, 2022 · 10 comments
Labels
Question An issue which isn't directly actionable in code

Comments

@KumanekoSakura
Copy link

KumanekoSakura commented Jan 23, 2022

Bug Report

I want to share interface definitions between "a .ts file for server side" (say, server.ts) and "a .ts file for client side" (say, client.ts) using a shared .ts file (say, interfaces.ts) in order to be able to catch interface definition mismatch errors. I want to use interfaces.ts as if a .h file in C language.

server.js (which runs using Node.js + Express) emits a HTML response which contains

  <script type='text/javascript' src='client.js'></script>

in order to let web browser parse client.js file.

But tsc command needlessly emits

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

line into client.js (which causes

  Uncaught ReferenceError: exports is not defined

error when parsed by a web browser) if I use "import" keyword from client.ts file.

As a result, currently I have to embed output of

  sed -e 's/export //' interfaces.ts

into client.ts file.

Maybe this error could be avoided by specifying non-default values in my tsconfig.json (generated by "tsc --init"), but I am not familiar with JavaScript/TypeScript specifications.

🕗 Version & Regression Information

tsc 4.5.4

💻 Code

In C language, I can do like below.

------ include.h start ------
struct struct1 {
  int key;
  char *value;
};
------ include.h end ------

------ server.c start ------
#include "include.h"

// Do something using "struct struct1" here.
------ server.c end ------

------ client.c start ------
#include "include.h"

// Do something using "struct struct1" here.
------ client.c end ------

In TypeScript language, I tried like below.

------ interfaces.ts start ------
"use strict";
export interface struct1 {
  key: number;
  value: string;
}
------ interfaces.ts end ------

as include.h and

------ server.ts start ------
"use strict";
import { struct1 } from './interfaces';

// Do something using "interface struct1" here.
------ server.ts end ------

------ client.ts start ------
"use strict";
import { struct1 } from './interfaces';

// Do something using "interface struct1" here.
------ client.ts end ------

🙁 Actual behavior

tsc emits

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

line (and causes runtime error on the browser side) when "import" keyword is used.

🙂 Expected behavior

tsc does not emit

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

line. (Or, tsc makes sure that

  Uncaught ReferenceError: exports is not defined

error does not happen.)

@MartinJohns
Copy link
Contributor

This is working as intended. As soon as you export or import something you're dealing with a module, and modules get this statement.

@KumanekoSakura
Copy link
Author

Then, is there an alternative way?

The #include directive in C language does not cause build/runtime failures.
I just want to share interface definitions between two .ts files.
It is inconvenient that we get runtime failures. If tsc can be updated to emit like

if (typeof exports !== `undefined`)
  Object.defineproperty(exports, "__esModule", { value: true });

we can use tsc like what I want to do.

@fatcerberus
Copy link

If you want to use import in the browser, look into targeting ES modules (ESM). What you’re getting right now are CommonJS modules, which don’t work natively in browsers.

@Josh-Cena
Copy link
Contributor

Duplicate of #42371 and a bunch of others?

@KumanekoSakura
Copy link
Author

KumanekoSakura commented Jan 23, 2022

If you want to use import in the browser, look into targeting ES modules (ESM). What you’re getting right now are CommonJS modules, which don’t work natively in browsers.

Thanks. But in my case, server.ts is for Node.js and client.ts is for browser. Can we specify different targets for server.ts and client.ts ?

If I change "module": in my tsconfig.json from "commonjs" to "es2015" so that client.js does not contain the Object.defineproperty() line, server.js starts failing to run.

If I also add "type": "module" to my package.json, "node server.js" complains

  SyntaxError: Named export 'Pool' not found. The requested module 'pg' is a CommonJS module, which may not support all module.exports as named exports.

at

  import { Pool } from 'pg';

line.

My server.ts contains

  import { Pool, PoolClient, QueryResult } from 'pg';

line, and I was able to change like

  import pg from 'pg';
  const { Pool } = pg;

for "Pool" as suggested, but I can't do similar for "PoolClient" and "QueryResult", and tsc complains about "PoolClient" and "QueryResult".
Since I'm a newbie, I don't know how to solve this...

Duplicate of #42371 and a bunch of others?

It seems yes. And just adding

if (typeof exports !== `undefined`)

would solve this problem?

@Josh-Cena
Copy link
Contributor

@KumanekoSakura For your particular use-case, you may just consider using type Pool = import('pg').Pool instead?

I also feel like it's quite annoying that import type is used as the heuristic for module/script discrimination.

@KumanekoSakura
Copy link
Author

Although I wish tsc to emit

if (typeof exports !== `undefined`)

line immediately before

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

line, I found that modifying server.ts file to emit

<script type='text/javascript'>
'use strict';
const exports = new Object();
</script>

lines immediately before

<script type='text/javascript' src='client.js'></script>

line is a simple workaround which I can choose.

@MartinJohns
Copy link
Contributor

They're discussing to remove this export: #47470 (comment)

@RyanCavanaugh RyanCavanaugh added the Question An issue which isn't directly actionable in code label Jan 24, 2022
@typescript-bot
Copy link
Collaborator

This issue has been marked as 'Question' and has seen no recent activity. It has been automatically closed for house-keeping purposes. If you're still waiting on a response, questions are usually better suited to stackoverflow or the TypeScript Discord community.

@Edgarmejiav
Copy link

Hi, I had the same problem, it was solved with this video
https://www.youtube.com/watch?v=C1ueaJ6M4nk

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Question An issue which isn't directly actionable in code
Projects
None yet
Development

No branches or pull requests

7 participants