microsoft / TypeScript

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

Handling input file extensions other than `.ts`, `.js`, `.tsx`, and `.jsx` #10939

Closed mhegazy closed 8 years ago

mhegazy commented 8 years ago

There has been multiple requests/issues for a more flexible use of file extensions. There are a few requests, but to summarize, they fall into one of the following two buckets:

Scenarios:

Today the file extensions are used in multiple places:

Add a new compiler option extensions that would be a map from a file extension to a ScriptKind entry. If provided, the extension map would override the mapping of the extension.

Different parts of the compiler will need to ask for ScriptKind and not an extension (many already do, e.g. parsing). For tsconfig.json file discovery, the set of keys in extension map would be combined with the set of known extensions.

allowJs in this model would be an alias for "extensions": { ".js" : "JS" }. defining both properties would be an error.

Declaration files are the only exemption of this rule. A .d.ts file meaning can not be changed, and a declaration file can only have .d.ts extension.

{
    "compilerOptions": {
        "extensions" : {
             ".ts": "TS",
             ".es": "JS",
             ".js": "JSX"
        }
    }
}
niieani commented 8 years ago

That sounds like a good proposal, however there's one problem I see with scoping of these changes. If we set to treat all .js files as TS (for example), and the project mixes un-typed JS (or if you want to gradually type parts of it), it's going to generate errors.

Perhaps instead of assigning "extensions", we could assign glob patterns?

Example:

{
    "compilerOptions": {
        "extensions" : {
             "**/*.ts": "TS",
             "**/*.es": "JS",
             "src/**/*.js": "TS" // treat all JS files inside src as TS
        }
    }
}
RyanCavanaugh commented 8 years ago

That's a really good idea -- it would solve the "Treat .js as JSX" problem for React Native at the same time

ghost commented 8 years ago

With this, tsx extension could be dropped since it conflicts with the very popular 2d map editor Tiled tileset extension.

http://www.mapeditor.org/ http://doc.mapeditor.org/reference/tmx-map-format/#tileset

RyanCavanaugh commented 8 years ago

Declining in favor of #11158 -- the complexity of determining the grammar, emit extension, etc, especially in a loose file context, doesn't seem to outweigh the benefits relative to simply renaming the input files.

niieani commented 8 years ago

@RyanCavanaugh really? This solves so much more use cases than just allowing .jsx. I'm really disappointed. 😞 See all the relevant related issues, such as #9839, #9551, #7926, #7699 and #9670, listed by @mhegazy.

Even saying it won't be implemented until TypeScript 3.0 would be better than closing it. :(

jbutz commented 8 years ago

@niieani, I agree with you. I am very disappointed by this. I've been having issues with the first scenario @mhegazy listed since the release of VSCode and I have been looking for this feature ever since.

mhegazy commented 8 years ago

The rational here is what is the use of extensions if they do not mean what they say. a file with type annotations is not .js, and so is a file with JSX syntax. saying that they are is just an outright lie.

For users who feel that .js is more community-friendly than .ts, we felt that this is doing community members a disservice. if the file is meat to contain typescript syntax, and meant to be processed by the typescript compiler, then why not advertise it. it will takes your community members 5 more seconds to know that the file is not really pure JS, so save them the trouble and call it a .ts.

For users who want to use other extensions than .ts. We are open for changing the output extension, since that might be used by other tools down stream. an example for this is the react native case in #11158. If there are similar cases we would be happy to support them as well.

As for tooling, tsserver does support loading files in different modes already. we can add additional server configuration to mark certain extensions, e.g. es or es6 as JS. but do not think support .js as TS or .ts as JS would be needed in these scenarios.

This really comes down to Occam's razor. the simplest solution for tools and for users is for extensions to really mean what they are meant to mean. changing this only leads to complexity in the tooling and confusion to users.

jonaskello commented 8 years ago

Not sure #11158 would help in the jspm work-flow scenario? If I load and transpile files in the browser, they are loaded with their original extension (.ts, .tsx). If I want to use JSX in one file out of 100 in a package I will have to name all 100 files .tsx since JSPM has a single default extension per package. I would rather name all files .ts since 99 files does not contain any JSX. Another way to do it would be specify the extension in the imports and not having a default extension but AFAIK tsc does not allow that either.

mhegazy commented 8 years ago

Output file extension should be allowed in imports. See https://github.com/Microsoft/TypeScript/issues/4595

jonaskello commented 8 years ago

I was actually referring to input extensions, such as import foo from './bar.ts'. From what I gather that will give a compile error in 2.0.3, and this is a good thing. However, that means that we in SystemJS must use the defaultExtension option. So we have to choose one extension to use. So if we in a package have one typescript file with JSX and 100 without JSX, they all have to have the extension TSX. I think this is rather unfortunate. I would rather have all files with extension TS weather they contain JSX or not. For some more background see for example this issue and this issue.

jonaskello commented 8 years ago

I guess the reason for the TSX extension is that the parser could not see the difference between the old style type casting and JSX? If that is the case I would suggest that if the compiler option jsxis set to react then the old style type casting is disallowed in all files (both TS and TSX) and regular TS extension files can contain JSX. This would get rid of the need for the hard-coded TSX extension. To not break compatibility maybe jsx could be set to something new like react-all-filesto enable the mode where <foo> will always be interpreted as JSX rather than typecasting, regardless of file extension.

christianbundy commented 7 years ago

I have to admit I'm a bit confused by this issue:

  1. At least five issues were closed in favor of this proposal, which is strange because there isn't a 1:1 or many:1 relationship between issues and proposals.
  2. A sixth related issue (#11158) was created, and when it became obvious that the sixth issue didn't depend on this proposal, this proposal was closed.
  3. The current situation is that the five original issues are unresolved, and the umbrella issue is now closed because a tangentially related problem has been resolved. I don't mean to be rude, but closing five issues because a sixth issue was resolved seems incredibly counterintuitive.

Put simply, there are many people and projects who want to use TypeScript as a linter/compiler, but don't want to re-architect our projects around tsc. For example, in my project we use JS + JSDoc everywhere, but tsc won't output anything meaningful unless we change all of our file extensions to .ts. If the official TypeScript position is that we must use a non-standard file extension for our JS, that's fine, but it seems like it goes directly against the goals of #4789.

I hope that you'll consider re-opening this issue as an umbrella, or re-opening the five issues that were closed in favor of this proposal. Thanks for all of your work on this project, have a great week.

chessman commented 7 years ago

As for tooling, tsserver does support loading files in different modes already. we can add additional server configuration to mark certain extensions, e.g. es or es6

Do you really plan to support it? I'm emacs user so now I'm choosing between tern with .es6 files handling + bad jsx support and tide with good jsx + no .es6 files support.

christianbundy commented 7 years ago

Just wanted to be clear on one bit:

The rational here is what is the use of extensions if they do not mean what they say. a file with type annotations is not .js, and so is a file with JSX syntax. saying that they are is just an outright lie.

This is the disagreement. If TypeScript is a superset of JavaScript (which I'm quickly learning may not be true) then .js should be parseable by the TypeScript compiler with absolutely 0 problems. In my mind, the file would become .ts when you start using non-standard features (e.g. typing) that aren't parseable as JavaScript. On the other hand, if I pass a JavaScript file with JSDoc types to tsc, it should throw errors if there are issues.

TL;DR: If TypeScript is a superset of JavaScript, then tsc should be able to take JavaScript as input without any special treatment. Is that the case?

mhegazy commented 7 years ago

TL;DR: If TypeScript is a superset of JavaScript, then tsc should be able to take JavaScript as input without any special treatment. Is that the case?

This works today with --allowjs.

What is not allowed, is putting type annotations i.e. : number in your .js file. either do not put type annotations, or change the file extension to .ts.

christianbundy commented 7 years ago

Thanks for the quick response, but --allowJs is the special treatment I was talking about, it ignores JavaScript rather than allowing it to be parsed as TypeScript. For example, here's some JavaScript with type annotations:

/*
 * @param {number} a
 * @param {number} b
 */
function add(a, b) {
  return a + b;
}

add(40, 2);

The TypeScript documentation for this is labeled "JSDoc support in JavaScript", which makes sense as it is JavaScript, but tsc won't look at it unless I use the .ts extension. It seems like there are four cascading issues:

  1. Non-fatal warnings (implicit any, dependencies without declarations, etc.) are called "errors", which make tsc return a non-0 exit code on perfectly valid JavaScript. This invalidates the "superset" thing that keeps coming up.
  2. Most (all?) vanilla JavaScript throws warnings, which means that an --ignoreWarnings flag had to be created. Unfortunately it was called --allowJs.
  3. Because of this, tsc refuses to look at .js files unless the above --ignoreWarnings flag is set, meaning that type-annotated JavaScript is ignored and add('foo', [1, 2, 3]) goes by unnoticed.
  4. Obviously this creates problems, which were brought up in issues, which are all now closed.

It seems like this is all one big messy issue, and I have a ton of respect for the maintainers for trying to solve it. I'm sure there's some bike-shedding to be done, but it seems like a solution would look something like this (in order of simplicity):

  1. The issues that were proposed above (#9839, #9551, #7926, #7699, and #9670) should be re-opened or closed on their own merit, as they don't seem to have anything to do with #11158 (which they were closed "in favor of").
  2. Non-fatal warnings (i.e. problems that don't stop an emission from being created) should be called warnings,.
  3. The --allowJs flag is tricky, but I might suggest renaming it to --ignoreJs as there's already #11051 for ignoring specific warnings.
  4. This would probably be a process full of bike-shedding, but it's only logical that if TypeScript is a superset of JavaScript that the warnings being reported on valid JavaScript shouldn't be called "errors" (and shouldn't return a non-0 exit code).
  5. tsc should be allowed to compile JavaScript files (which, code-wise, is dead simple), which should work fine as JavaScript is a subset of TypeScript.

Do those make sense, or am I off the mark here?

mhegazy commented 7 years ago

May be I should start by asking what are you trying to do? do you have an existing project that you want to migrate to TS? try using TS on a file in an existing project?...

christianbundy commented 7 years ago

My comment on the closed issues above is unrelated to my project, but my personal use-case is a JavaScript application with JavaScript tooling (building, linting, testing, analysis, documentation, etc.) that would benefit from the tsc.

We were under the impression that we could add JSDoc type annotation comments to get the best of both worlds (the interoperability of JavaScript + the sanity of TypeScript), but it seems like we have two options:

  1. Use --allowJs, which basically disables the functionality we care about.
  2. Rename all of our JavaScript files to use the .ts extension, reconfigure our JavaScript tooling to expect the new filenames, and be extra careful not to use non-JS functionality that would break our non-TypeScript tooling.

It seems like there's only a tiny fraction of TypeScript syntax that isn't JavaScript, so our goal is to reap some of those benefits without non-JS syntax that will break our build. I thought that JSDoc-style annotations would be a perfect fit for us, but I'm sure you're much more familiar with the project and may be able to point me in the right direction. Thanks!

mhegazy commented 7 years ago

We were under the impression that we could add JSDoc type annotation comments to get the best of both worlds (the interoperability of JavaScript + the sanity of TypeScript), but it seems like we have two options

I have a PR out for a feature with a similar goal, would love to get your feedback on it. If you are interested to give it a shoot, here is how i would start:

aikeru commented 7 years ago

This continues to cause problems downstream for VS Code, which supports .es6 file extension for syntax coloring JavaScript files, but prevents things like intellisense when importing between .es6 files or importing from an .es6 file within any other file. As much as I'd like to use VS Code, forcing teams to change their file extension (which is widely supported by the community and other tools) just to support a specific editor/tool is a non-starter. https://github.com/Microsoft/vscode/issues/8816

ahamid commented 7 years ago

Being able to configure the extension of files treated as full TS files would also help me on a project with a hybrid setup which has gone through a couple of transitions in the language/bundling stack and seems like a pretty small concession to make adoption smoother.

omeid commented 7 years ago

As it has been argued many times, there is a lot of value in allowing tsc to treat files with alternative extensions (e.g .js, .es) as TypeScript.

The added options of allowJs, checkJs, jsx seems to be ad-hoc hacks to address very specific use-cases that would be solved adding for alternative extensions.

lastmjs commented 7 years ago

I would really like to allow TypeScript type annotations in .js files. I think it's one of the best and simplest solutions for my use-case, and would solve a lot of problems that I'm running into with es modules because of the non-standard .ts file extensions.

kevde commented 7 years ago

I think this feature should be also fixed as well because I have a case wherein I used a non-relative path when importing a JSON file. Then the allowed extensions for using non-relative paths are only .ts, .tsx, .js.

lostpebble commented 7 years ago

Please, this is so useful. I have a massive React project I'd like to migrate to Typescript now. But the Typescript compiler complains that it can't compile my .js files which are standard ES6 with JSX components in them. I'd just like an easy way I can tell to the compiler to treat all .js files as .tsx files and start the migration process step by step.

Right now the only way seems to be to convert all my files' extensions over to .tsx... but I imagine it's going to take time to migrate / test things and I'd like to keep my current builds compatible while it's happening.

omeid commented 7 years ago

A year later and everytime I consider moving a project to typescript, this issue stops me. :(

lostpebble commented 7 years ago

Right now I'm actually just thinking of using babel (my current setup) to read my files as they are and remove any Typescript syntax as I'm changing it over - seems they've just landed a parser for it: https://github.com/babel/babylon/pull/523.

This means no compile-time checks though, so I'm guessing I'll have to rely on the IDE for those for now... (definitely not ideal, but half way there at least)

Ah, JavaScript configuration... the bane of my existence.

variousauthors commented 7 years ago

I would actually like to tslint my .test.tsx files.

aikeru commented 7 years ago

Now we'll have the new .mjs extension for modular JS node files: https://blogs.windows.com/msedgedev/2017/08/10/es-modules-node-today/

Will the TypeScript engine be extended to recognize this extension too? Might this be a good time to reevaluate the part of this request that allows user-defined extensions for JS modules? I'd love to be able to advertise this as a feature to enable greater adoption of VSCode, which depends on the TS engine for intellisense!

aluanhaddad commented 7 years ago

I imagine the only thing which will be done, if anything, will be to add support for that specific extension.

That said, I think .mjs is terrible and will fracture the community.

:-1: NodeJS

micimize commented 6 years ago

This makes react-native platform specific code difficult to organize. Custom module resolution has become a bit of a staple in the javascript world. Like path inclusion, file naming enforcement should not be the role of a build tool.

For anyone encountering a similar multi-target issue, I ended up leveraging conditional imports. Even though I was using webpack with awesome-typescript-loader already, I couldn't get various resolution configurations or the NormalModuleReplacementPlugin to work.

apieceofbart commented 6 years ago

I have a node bin file and I would like this file to able to consume typscript files. Hence I'm using ts-node. However this is a problem because tsc does not allow files without extensions (and node bin files have no extension usually). The solution of transpiling those typescript files to be consumed and just using node is not an easy task.

I went through this issue but it's not clear to me why this is not possible: tsc myFile.abc ?

lastmjs commented 6 years ago

The modules spec allows other extensions, I don't understand why TypeScript sees them as errors, this is causing large issues.

On Mon, May 14, 2018, 2:42 AM Bartek notifications@github.com wrote:

I have a node bin file and I would like this file to able to consume typscript files. Hence I'm using ts-node. However this is a problem because tsc does not allow files without extensions (and node bin files have no extension usually). The solution of transpiling those typescript files to be consumed and just using node is not an easy task.

I went through this issue but it's not clear to me why this is not possible: tsc myFile.abc ?

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/Microsoft/TypeScript/issues/10939#issuecomment-388758042, or mute the thread https://github.com/notifications/unsubscribe-auth/AGrSj9NQ6XFzeXo1JOxz9KNH75jUTNuUks5tyVGIgaJpZM4J-GVA .

tunnckoCore commented 6 years ago

Yea, absolutely. It just not make sense. Also we are talking at least for that to be able to output different extensions not only .js ones. I don't see what's the problem, since that after compilation TS won't care for those files what and how you are using the out files.

If it is so big problem, we, both sides can make compromises - still accept only ts/tsx files as input, but to be able to output different extensions - just to remove that extra step that we are able to do right now of renaming those .js output files to whatever extension we want, e.g. .mjs.

ctsstc commented 6 years ago

2018 and still we're nowhere with this :( 😞

tunnckoCore commented 6 years ago

Yea.. And even with 3.0 ...

❯ yarn build
yarn run v1.7.0
$ tsc --emitDeclarationOnly && babel src -d dist
src/index.js:2:5 - error TS8009: 'private' can only be used in a .ts file.

2     private x = 10
      ~~~~~~~

src/index.js:4:21 - error TS8010: 'types' can only be used in a .ts file.

4     setX = (newVal: number) => { this.x = newVal; }
                      ~~~~~~

error Command failed with exit code 1.
massimonewsuk commented 6 years ago

I made a comment earlier on this thread (which I deleted) saying how I was using the TypeScript compiler to check Flow code (cause the TypeScript tooling is so much better), and how I wished we could allow the checking of JS code with type annotations.

I have to say my use case is completely useless because Flow syntax is different in some cases anyway.

Also, I'm thinking if TypeScript allowed people to specify any file extension then you'd get all these insane people in the JavaScript community who would input .js files and be like "yeah I only write standards compliant JS, I only use TS for type checking", but their code is littered with non-standard type annotations (cough Flow users cough*).

I think it's fantastic that TypeScript type annotations are only allowed inside .ts, .tsx and and .d.ts files and I think it should always be kept that way. It makes it much better for the community. Everyone knows immediately whether they are looking at a TypeScript file, or a standard JavaScript file. GitHub statistics are correct too (has anyone ever spotted metrics on how many people are using Flow? No... because they use the same .js extension).

Also people won't try doing node myFile.js and have it blow up due to syntax errors because they had non-standard type annotations (ahem, Flow). Imagine some poor kid trying to learn javascript sees a *.js file with all these type annotations inside and is like what? Why doesn't this run?

I could go on and on about the list of reasons why it would be a terrible idea for TypeScript team to support custom extensions...

mikepatrick commented 6 years ago

I don't have strong feelings on this issue, but I do have two cents to register.

I think that eliminating file extension restrictions would open up options for people building their own tooling around tsc. That, in and of itself, may or may not be a good enough reason to consider it seriously. At any rate, consider this webpack example:

This person wanted to create a webpack loader that would generate Typescript interfaces from files with (potentially) arbitrary extensions. These interfaces could then be passed along to the Typescript loader of choice.

This was (arguably) more difficult/confusing than it could have been, due to a combination of the file extension restrictions and a poor error message.

While ts-loader has a convenient work-around for this restriction (as documented in the link above), it's not great being tied to one particular loader (or having to write your own).

Again, just two cents. I've been using TS full time for about a year, and I love it. Keep up the good work!

duzenko commented 5 years ago

Sorry if I'm duplicating someone else but not being able to move forward with simple things like this is rather frustrating

lellimecnar commented 5 years ago

With @babel/preset-typescript now being a thing, this needs to be allowed...

We are using Babel for a rather large application, and we would like to slowly add types and use the new Babel preset. We are not interested in fully "switching" to the TypeScript ecosystem, and we are currently using non-TypeScript features via Babel plugins. It makes absolutely zero sense to rename all of our JS files to .ts just to silence the mountain of "'types' can only be used in a .ts file." messages in VS Code... and we can't move forward without a way of silencing those errors, since every single type annotation will be marked as an error, and the other devs will end up just ignoring everything...

...assuming that there is no valid use-case for something that developers keep asking for is never a good thing...

gugadev commented 5 years ago

Really, there is no logic reason to do not implement this proposal. This is a must for god's sake.

omeid commented 5 years ago

Despite a few short comings, I have decided to stay with flowtype for the prime reason that it allows incremental use unlike TypeScript which has been hardheaded on this issue. I suggest you consider it for your project too, @lellimecnar.

JounQin commented 5 years ago

Any hack way??? Something like appendTsPrefixTo?

bgraceful commented 5 years ago

noun:I verb:am verb:guessing adjective:that noun:there auxiliary verb:is adjective:no noun:way preposition:to verb:run noun:typescript preposition:on indefinite article:a noun:String?

strokirk commented 5 years ago

@RyanCavanaugh can this issue please be re-opened, since there has been a lot of really thoughtful comments since the close date regarding the utility of this (simple) functionality?

Or would it be better to open a new issue?

jfrank14 commented 3 years ago

My scenario is to be able to port a large Adobe Animate application from javascript to typescript. Animate uses a naming convention that I probably can't change. Logic for components is stored in files called MyComponent.js and MyComponent.jsfl, where the js file is generated code containing resources and the jsfl file is the code that you write to control the component.

I DO actually want the source file to be Typescript, but I can't use a ts extension because each component has TWO source files and also two different output file extensions.

Rigidity commented 3 years ago

"Too complex" my ass

josh-sachs-epic commented 3 years ago

I came across this proposal while trying to setup nextjs (react) to import css modules without requiring the '.css' extension.

This currently works at runtime, but tsc reports it as an error ts(2307)

tsconfig.json

 baseUrl": "./src",
 "paths": {
    "@api/*": ["pages/api/*"],
    "@pages/*": ["pages/*"],
    "@styles/*": ["styles/*.css"],
 } 

When I import the module at src/styles/home.module.css as such: import styles from @styles/home.module

it works at compile/runtime, but tsc validation in vscode keeps it flagged as an error.

Doing something like: import styles from @styles/home.module.css validates fine (as long as I remove .css from the path delaration)

Seems to me like using paths to extend typescript's base list of source file extensions should be a no brainer... and is 95% working today.

RobertAKARobin commented 1 year ago

I have a situation where I want to import modules from a cache, in which all files have had their extensions removed. But I can't import them because of this limitation.