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

allow voluntary .ts suffix for import paths #37582

Closed
5 tasks done
timreichen opened this issue Mar 25, 2020 · 110 comments · Fixed by #51669
Closed
5 tasks done

allow voluntary .ts suffix for import paths #37582

timreichen opened this issue Mar 25, 2020 · 110 comments · Fixed by #51669
Assignees
Labels
Fix Available A PR has been opened for this issue Suggestion An idea for TypeScript

Comments

@timreichen
Copy link

timreichen commented Mar 25, 2020

Search Terms

.ts
suffix
imports
extension

Suggestion

Typescript doesn't recognize file imports with .ts suffix.
Allow voluntary .ts to be added to import paths.

Use Cases

It seems right to be able to use a correct path to a file without magic resolution.
This would help to align with deno which uses mandatory suffixes o files.

Examples

let
import a from "path/to/a.ts"
behave the same as
import a from "path/to/a"

Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.
@RyanCavanaugh RyanCavanaugh added In Discussion Not yet reached consensus Suggestion An idea for TypeScript labels Mar 27, 2020
@timreichen
Copy link
Author

timreichen commented Jun 3, 2020

After some more research I wonder if that behavior could even be more guided by a compiler option like --noImplicitSuffix.
That would be in alignment with --noImplicitAny and give the developer the choice, yet push for a valid, not "magical" resolved path if not set.

What are your thoughts on that?

@Jack-Works
Copy link
Contributor

#35148

@zacnomore
Copy link

zacnomore commented Aug 14, 2020

#35148

Read through the above and it seems to boil down to multiple build targets with different file extensions throws a wrench in things when supporting using the file extensions on imports. Where it diverges in my mind, and hopefully @Jack-Works you can illuminate this a bit, is that we'd be importing .ts extensions which would have to be rewritten anyways.

@resynth1943
Copy link

resynth1943 commented Dec 12, 2020

So... where are we actually at with this?

Scattered issues / feature requests don't help, and the confusing (and somewhat untimely) response(s) from official TypeScript members haven't helped me understand how this feature will be implemented (it will have to be, eventually).

All I can ascertain is that this is has yet to be resolved.

Can someone explain in plain English when and how these multiple issues will be fixed, please?

rivy added a commit to rivy/js.os-paths that referenced this issue Jan 7, 2021
- TypeScript fails to correctly generate an ESM module with correct line endings
  - ref: <microsoft/TypeScript#18442 (comment)>
  - ref: <microsoft/TypeScript#18442>
  - ref: <microsoft/TypeScript#37582>
  - ref: <microsoft/TypeScript#35148>
rivy added a commit to rivy/js.os-paths that referenced this issue Jan 7, 2021
- TypeScript fails to correctly generate an ESM module with correct line endings
  - ref: <microsoft/TypeScript#18442 (comment)>
  - ref: <microsoft/TypeScript#18442>
  - ref: <microsoft/TypeScript#37582>
  - ref: <microsoft/TypeScript#35148>
rivy added a commit to rivy/js.os-paths that referenced this issue Jan 7, 2021
- TypeScript fails to correctly generate an ESM module with correct line endings
  - ref: <microsoft/TypeScript#18442 (comment)>
  - ref: <microsoft/TypeScript#18442>
  - ref: <microsoft/TypeScript#37582>
  - ref: <microsoft/TypeScript#35148>
rivy added a commit to rivy/js.os-paths that referenced this issue Jan 7, 2021
- TypeScript fails to correctly generate an ESM module with correct line endings
  - ref: <microsoft/TypeScript#18442 (comment)>
  - ref: <microsoft/TypeScript#18442>
  - ref: <microsoft/TypeScript#37582>
  - ref: <microsoft/TypeScript#35148>
rivy added a commit to rivy/js.os-paths that referenced this issue Jan 10, 2021
- TypeScript fails to correctly generate an ESM module with correct line endings
  - ref: <microsoft/TypeScript#18442 (comment)>
  - ref: <microsoft/TypeScript#18442>
  - ref: <microsoft/TypeScript#37582>
  - ref: <microsoft/TypeScript#35148>
rivy added a commit to rivy/js.os-paths that referenced this issue Jan 11, 2021
- TypeScript fails to correctly generate an ESM module with correct line endings
  - ref: <microsoft/TypeScript#18442 (comment)>
  - ref: <microsoft/TypeScript#18442>
  - ref: <microsoft/TypeScript#37582>
  - ref: <microsoft/TypeScript#35148>
rivy added a commit to rivy/js.os-paths that referenced this issue Jan 11, 2021
- TypeScript fails to correctly generate an ESM module with correct line endings
  - ref: <microsoft/TypeScript#18442 (comment)>
  - ref: <microsoft/TypeScript#18442>
  - ref: <microsoft/TypeScript#37582>
  - ref: <microsoft/TypeScript#35148>
rivy added a commit to rivy/js.os-paths that referenced this issue Jan 11, 2021
- TypeScript fails to correctly generate an ESM module with correct line endings
  - ref: <microsoft/TypeScript#18442 (comment)>
  - ref: <microsoft/TypeScript#18442>
  - ref: <microsoft/TypeScript#37582>
  - ref: <microsoft/TypeScript#35148>
rivy added a commit to rivy/js.os-paths that referenced this issue Jan 11, 2021
- TypeScript fails to correctly generate an ESM module with correct line endings
  - ref: <microsoft/TypeScript#18442 (comment)>
  - ref: <microsoft/TypeScript#18442>
  - ref: <microsoft/TypeScript#37582>
  - ref: <microsoft/TypeScript#35148>
rivy added a commit to rivy/js.os-paths that referenced this issue Jan 11, 2021
- TypeScript fails to correctly generate an ESM module with correct line endings
  - ref: <microsoft/TypeScript#18442 (comment)>
  - ref: <microsoft/TypeScript#18442>
  - ref: <microsoft/TypeScript#37582>
  - ref: <microsoft/TypeScript#35148>
rivy added a commit to rivy/js.os-paths that referenced this issue Jan 11, 2021
- TypeScript fails to correctly generate an ESM module with correct line endings
  - ref: <microsoft/TypeScript#18442 (comment)>
  - ref: <microsoft/TypeScript#18442>
  - ref: <microsoft/TypeScript#37582>
  - ref: <microsoft/TypeScript#35148>
rivy added a commit to rivy/js.os-paths that referenced this issue Jan 11, 2021
- TypeScript fails to correctly generate an ESM module with correct line endings
  - ref: <microsoft/TypeScript#18442 (comment)>
  - ref: <microsoft/TypeScript#18442>
  - ref: <microsoft/TypeScript#37582>
  - ref: <microsoft/TypeScript#35148>
rivy added a commit to rivy/js.os-paths that referenced this issue Jan 11, 2021
- TypeScript fails to correctly generate an ESM module with correct line endings
  - ref: <microsoft/TypeScript#18442 (comment)>
  - ref: <microsoft/TypeScript#18442>
  - ref: <microsoft/TypeScript#37582>
  - ref: <microsoft/TypeScript#35148>
rivy added a commit to rivy/js.os-paths that referenced this issue Jan 11, 2021
- TypeScript fails to correctly generate an ESM module with correct line endings
  - ref: <microsoft/TypeScript#18442 (comment)>
  - ref: <microsoft/TypeScript#18442>
  - ref: <microsoft/TypeScript#37582>
  - ref: <microsoft/TypeScript#35148>
rivy added a commit to rivy/js.xdg-portable that referenced this issue Jan 26, 2021
…tensions)

- TypeScript fails to correctly generate an ESM module with correct file extensions
  - ref: <microsoft/TypeScript#18442 (comment)>
  - ref: <microsoft/TypeScript#18442>
  - ref: <microsoft/TypeScript#37582>
  - ref: <microsoft/TypeScript#35148>
@tonygiang
Copy link

tonygiang commented May 24, 2021

+1

If I turn on resolveJsonModule and put a MatchmakingConfig.ts and a MatchmakingConfig.json in the same path, a nasty unexpected behavior awaits:

// this results in MatchmakingConfig.json being imported, not MatchmakingConfig.ts!
import MatchmakingConfig from "./path/to/MatchmakingConfig";

Allowing .ts suffix will let us specify .ts files specifically.

@tracker1
Copy link

tracker1 commented Nov 2, 2022

I find it funny... TS will read import foo from "myFile" go through the trouble of checking for (.tsx, .ts, .jsx, .js) then actually load that first match, and proceed to potentially transform that second file, but actually supporting an explicit file extension, or changing the import statement to match the about to be transformed explicit file output (that is different from input), is too far.

@frank-dspeed
Copy link

frank-dspeed commented Nov 3, 2022

@tracker1 the reason is historical and it is not a bad one at all only this implementation is done bad the reasoning and concepts are in general well it is a bit like with the ECMASpec it self it is easy to write a good spec but implementation and definition do then end up in something diffrent.

The ground concept is the classic resolve algo which is you reference myname.js and it will lookup myname.ts as also myname.d.ts

that is the base then the assumption comes in that you can not reference .ts files as this would mean to change existing code eg make it point to .js.

when we now want to reference .ts we broke the core concept of TS which is to be a facade over JS that is optional.

but now as we used all that we come to the conclusion that referencing .ts would be beneficial if we apply external tooling as that saves us from lookups which are error proune.

And now to solve this there are 2 major ways to go and both are none appealing for ts and mirosoft.

The Both ways to go are:

  • Direkt bundler support eg resolve hooks or implementations that fit the needs of external tooling
  • Get tc39 type annotation support some how to happen. even if that would be a babel implementation and then throw that into all of the ecosystem that microsoft controls to over vote the externals that are not aware of the big big issues faced by millions.
    • this is what i am doing at present in fact i invested into over 200 people working on 1:1 API compatible complete scratch rewrites of big parts of the whole ecosystem that is Microsoft controlled at present because they invested also in open source but they did never invest into rewrites they always patch. I invest into a total rewrite as i see the opinion for better core abstractions that are more modular and inject able as also component systems that allow reverse of control in things like the v8 core engine and the content layers of webkit (which is blink got only renamed by google)

But i identified many incremental pathes to update

@vixalien
Copy link

I hope this also allows enforcing the use of extensions as an option so that imports without extensions aren't allowed

@andrewbranch
Copy link
Member

It does not. You might want to track #50153, and if you’re concerned with Deno, see my advice at #51669 (comment).

@karlhorky
Copy link
Contributor

enforcing the use of extensions as an option so that imports without extensions aren't allowed

To approach this problem from another angle, you may want to consider eslint-plugin-node with the node/file-extension-in-import option:

https://github.com/mysticatea/eslint-plugin-node/blob/master/docs/rules/file-extension-in-import.md

This is what we've been using to enforce .js extensions in our TypeScript code (because we're using ESM with Node.js, which requires the extensions). Haven't tried it yet with .ts extensions.

@andrewbranch
Copy link
Member

If you are compiling TS code to JS code that will run in Node, you should be using --module nodenext (which implies --moduleResolution nodenext), which requires extensions in precisely the places that Node does. No lint rule should be necessary if your reason for enforcing extensions is that you want your code to run in Node. That is built into TypeScript, and has been for about a year. #51669 is made for bundlers, and there is not a single bundler I know of (and I tested a bunch) that ever requires extensions on imports, so likewise it is not a requirement of the module resolution mode.

@ctjlewis
Copy link
Contributor

Just saw #51669–is this really finally fixed?!

@karlhorky
Copy link
Contributor

karlhorky commented Dec 15, 2022

No lint rule should be necessary if your reason for enforcing extensions is that you want your code to run in Node. That is built into TypeScript, and has been for about a year.

We added this lint rule because we had breakages that we would only see in the production builds (because use tsm and esbuild for dev mode, which does not have a problem with lacking extensions).

The inconsistencies between bundlers allowing no extensions and .ts extensions and TypeScript allowing for module resolution to other file extensions like .tsx have been a bit of a headache to say the least, lots of hours spent on this in the ecosystem:

Ideal feels like TS should just allow for .ts / .tsx / etc extensions as well and just transpile those import paths to whatever the final file extension will be, as an exception of the rule "TypeScript doesn't modify JavaScript code you write"

It seems almost like maybe that's what --moduleResolution bundler + allowImportingTsExtensions is? But it doesn't seem to be able to emit like this, which would also be desirable.

@karlhorky
Copy link
Contributor

karlhorky commented Dec 15, 2022

But indeed, TypeScript does show the error about missing file extensions also in the IDE, which is great - thanks for the tip! I needed to do some configuration for this, but it's working.

I'll try applying it to those projects, maybe they are just not configured the same way.

Screenshot 2022-12-15 at 10 24 26

I needed to add "type": "module" to my package.json as well as a few things to my tsconfig.json:

{
  "$schema": "https://json.schemastore.org/tsconfig",
  "compilerOptions": {
    "lib": ["dom", "dom.iterable", "esnext"],
    "module": "NodeNext",
    "target": "ESNext",
    "moduleResolution": "NodeNext",
    "resolveJsonModule": true,
    "esModuleInterop": true,
    "isolatedModules": true,
    "allowJs": true,
    "downlevelIteration": true,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "noFallthroughCasesInSwitch": true,
    "skipLibCheck": true,
    "strict": true,
    "incremental": true,
    "noUncheckedIndexedAccess": true
  },
  "include": [
    "**/.eslintrc.cjs",
    "next-env.d.ts",
    "**/*.ts",
    "**/*.tsx",
    "**/*.cjs",
    "**/*.mjs"
  ],
  "exclude": ["node_modules", "build"]
}

@andrewbranch
Copy link
Member

I needed to add "type": "module" to my package.json

Yes, because without either this or .mts/.mjs extensions, you do not have ES modules at all. Node supports both ESM and CJS files, and CJS files are allowed to write module specifiers without extensions. All your .ts files are CJS modules until you add "type": "module". (There will also be a new flag in 5.0 that prevents you from writing ESM syntax in CJS modules, since that is probably a major source of confusion: #51479.)

as well as a few things to my tsconfig.json

This rule is only relevant in node16/nodenext because that’s the only mode that targets versions of Node that have ESM support. The mode called node is out of date and is being renamed to node10: #51901.

@ljharb
Copy link
Contributor

ljharb commented Dec 15, 2022

@andrewbranch it would be really great if TS made it easy to use native ESM without type module, since that package.json flag causes lots of issues with outdated tooling and also causes lots of confusion for new users.

@andrewbranch
Copy link
Member

@ljharb can you expound on that? In modes made for Node, we’re just doing what Node requires (assuming it’s invoked with no special CLI flags altering the behavior) as far as I know. If we were to emit a .js file with ESM syntax, users would get this:

image

image text
// a.js
import path from "path";

❯ node --version
v16.17.1
                                                                                                                                     
❯ node a.js
(node:8214) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
(Use `node --trace-warnings ...` to show where the warning was created)
/Users/andrew/Developer/microsoft/eg/js/a.js:1
import path from "path";
^^^^^^

SyntaxError: Cannot use import statement outside a module
    at Object.compileFunction (node:vm:360:18)
    at wrapSafe (node:internal/modules/cjs/loader:1055:15)
    at Module._compile (node:internal/modules/cjs/loader:1090:27)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1180:10)
    at Module.load (node:internal/modules/cjs/loader:1004:32)
    at Function.Module._load (node:internal/modules/cjs/loader:839:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
    at node:internal/main/run_main_module:17:47

@ljharb
Copy link
Contributor

ljharb commented Dec 15, 2022

@andrewbranch right - i'm asking for there to be a way to emit .mjs files instead (or .cjs, if it's CJS, i suppose).

@andrewbranch
Copy link
Member

There is, just name your files .mts or .cts

@ljharb
Copy link
Contributor

ljharb commented Dec 15, 2022

ah, ok great thanks :-)

@jeremyjacob
Copy link

See the following tsconfig.json:

{
    "compilerOptions": {
         "moduleResolution": "bundler",
         "allowImportingTsExtensions": true
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Fix Available A PR has been opened for this issue Suggestion An idea for TypeScript
Projects
None yet
Development

Successfully merging a pull request may close this issue.