microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
100.77k stars 12.46k forks source link

allow voluntary .ts suffix for import paths #37582

Closed timreichen closed 1 year ago

timreichen commented 4 years ago

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:

ctjlewis commented 1 year ago

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

karlhorky commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

@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 commented 1 year ago

@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 commented 1 year ago

@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 commented 1 year ago

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

ljharb commented 1 year ago

ah, ok great thanks :-)

jeremyjacob commented 1 year ago

See the following tsconfig.json:

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