I just start working on a React project with TypeScript and ask myself what should I do with regular class files? Should I use .ts
or .tsx
files an
The reason why .jsx extension was introduced is that JSX is an extension of JS syntax and thus .jsx files don't contain valid JavaScript.
TypeScript follows the same convention by introducing .ts and .tsx extensions. A practical difference is that .tsx don't allow <Type>
type assertions because the syntax is in conflict with JSX tags. as Type
assertions was introduced as a replacement for <Type>
and considered a preferred choice for consistency reasons in both .ts and .tsx. In case the code from .ts is used in .tsx file, <Type>
will need to be fixed.
The use of .tsx extension implies that a module is related to React and uses JSX syntax. In case it doesn't, the extension may give false impression about module contents and the role in the project, this is the argument against using .tsx extension by default.
On the other hand, if a file is related to React and has good chances to contain JSX at some point, it can be named as .tsx from the beginning to avoid renaming later.
For instance, utility functions that are used together with React components may involve JSX at any point and thus can be safely use .tsx names, while Redux code structure isn't supposed to use React components directly, can be used and tested apart from React and can use .ts names.
You can use tsx
instead of ts
with very little difference. tsx
obviously allows the usage of jsx
tags inside TypeScript, but this introduces some parsing ambiguities that make tsx slightly different. In my experience these differences are not very big:
Type assertions with <>
don't work as that is the marker for a jsx tag.
TypeScript has two syntaxes for type assertions. They both do the exact same thing but one is usable in tsx the other is not:
let a: any;
let s = a as string // ok in tsx and ts
let s2 = <string>a // only valid in ts
I would use as
instead of <>
in ts
files as well for consistency. as
was actually introduced in TypeScript because <>
was not usable in tsx
.
Generic arrow functions with no constraint are not parsed correctly
The arrow function below is ok in ts
but an error in tsx
as <T>
is interpreted as the start of a tag in tsx
:
const fn = <T>(a: T) => a
You can get around this either by adding a constraint or not using an arrow function:
const fn = <T extends any>(a: T) => a
const fn = <T,>(a: T) => a // this also works but looks weird IMO
const fn = function<T>(a: T) { return a;}
Note
While you can use tsx
instead of ts
, I would recommend against it. Convention is a powerful thing, people associate tsx
with jsx
and will probably be surprised you don't have any jsx
tags, best keep developer surprise to a minimum.
While the ambiguities above (although probably not a complete list) are not big they probably played a big part in the decision to use a dedicated file extension for the new syntax in order to keep ts
files backward compatible.
It's kind of a convention to use x
in the end when your JavaScript is in JSX Harmony
mode. That is, when this is valid:
doSomething(<div>My div</div>);
However, your file extension doesn't really matter, as long as your pre-processors are aware of your decision (browserify or webpack). I, for one, use .js
for all my JavaScript, even when they are React. The same applies for TypeScript, ts/tsx
.
EDIT
Now, I would strongly recommend using JSX for Javascript with React syntax and TSX for TypeScript with React because most editors/IDEs will use the extension to enable or not the React syntax. It is also consider it to be more expressive.
I believe with the .tsx files you could use the all JSX (JavaScript XML) code. Whereas in the .ts file you can only use just typescript.
.ts
files have an <AngleBracket>
type assertion syntax which conflicts with the JSX grammar. In order to avoid breaking a ton of people, we use .tsx
for JSX, and added the foo as Bar
syntax which is allowed in both .ts
and .tsx
files.
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
And the other is the as-syntax:
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
We can use .ts with as-syntax
but <string>someValue
is cool!