tsx-control-statements
Basically jsx-control-statements, but for the typescript compiler toolchain. Works for both javascript and typescript.
Typescript version range | tsx-control-statements version |
---|---|
2.4.x - 3.3.x |
v3.3.x |
>= 3.4.x |
v4.0.x |
Drop-in replacement for jsx control statements
- No need to rewrite anything
- Compile control statements in typescript
.tsx
files- Control statements transpile to type-correct typescript before type checking
- Compile control statements in javascript
.js
and.jsx
files"allowJs"
should be set totrue
in your typescript configuration
- Run the test suite:
yarn && yarn --cwd transformer build && yarn test
. It includes:- Compatibility tests with
jsx-control-statements
(i.e. both produce the same output html) - Tests for correct transpilation
- Tests for typechecking
- Compatibility tests with
Zero dependencies apart from typescript
- Pick any typescript version equal to or above
2.4.x
- Can be used with Vue, React or just plain jsx/tsx
Known limitations:
- [js, ts] I haven't found any way of integrating this into
create-react-app
scaffold project without ejecting the scripts and modifying them - [js, ts] Various CLIs (
tsc
,ts-register
,ts-node
) feature no flag (that I know of) that allows for addition of custom transformers [ts] TheisolatedModules
flag currently causes build errors for typescript files, since the typings currently live in a namespaceisolatedModules
is supported since the moduletsx-control-statements/components
contains stub definitions which can be importedimport { For, If } from 'tsx-control-statements/components'
- [ts] Cannot work with various "smart" plugins that instead of invoking the typescript compiler rather strip the types and handle the code as javascript. This includes tools like:
@babel/preset-typescript
@babel/plugin-transform-typescript
What are the control statements transpiled to?
If - Ternary operators
import { If } from 'tsx-control-statements/components';
const SongRelatedThingy = ({ songList }: { songList: string[] }) => (
<p>
<If condition={songList.includes('Gery-Nikol - Im the Queen')}>
good taste in music
</If>
</p>
)
// will transpile to
const SongRelatedThingy = ({ songList }) => (
<p>
{
songList.includes('Gery-Nikol - Im the Queen')
? 'good taste in music'
: null
}
</p>
)
With - Immediately invoked function expression
import { With } from 'tsx-control-statements/components';
const Sum = () => <p>
<With a={3} b={5} c={6}>
{a + b + c}
</With>
</p>
// becomes
const Sum = () => <p>
{((a, b, c) => a + b + c))(3, 5, 6)}
</p>
Array.from
calls
For - More flexible than [].map
, since it can be provided with an iterator or an array-like as it's first parameter. For non-legacy code, prefer the more type-safe alternative.
import { For } from 'tsx-control-statements/components';
// more type-safe for, the typechecker knows the types of the "name" and "i" bindings
const Names = ({ names }: { names: string[] }) => <ol>
<For of={names} body={(name, i) => (
<li key={name}>
{i}<string>{name}</strong>
</li>
)} />
</ol>
// jsx-control-statements compatible
const Names = ({ names }: { names: string[] }) => <ol>
<For each="name" of={names} index="i">
<li key={name}>
{i}<strong>{name}</strong>
</li>
</For>
</ol>
// both of the above will transpile to:
const Names = ({ names }) => <ol>
{
Array.from(names, (name, i) => (
<li key={name}>
{i}<strong>{name}</strong>
</li>
)
}
</ol>
Choose/When/Otherwise - nested ternary operators, emulates switch/case.
import {
Choose,
When,
Otherwise
} from 'tsx-control-statements/components';
const RandomStuff = ({ str }: { str: string }) => <article>
<Choose>
<When condition={str === 'ivan'}>
ivancho
</When>
<When condition={str === 'sarmi'}>
<h1>yum!</h1>
</When>
{/*
* Otherwise tag is optional,
* if not provided, null will be rendered
*/}
<Otherwise>
im the queen da da da da
</Otherwise>
</Choose>
</article>
// transpiles to
const RandomStuff = ({ str }) => <article>
{
str === 'ivan'
? 'ivancho'
: (str === 'sarmi'
? React.createElement('h1', null, 'yum!')
: 'im the queen da da da da')
}
</article>
Cookbook
Bundlers and scaffolding tools
- fuse-box
- The unit test cases for this project are bundled with
fuse-box
(link).
- The unit test cases for this project are bundled with
- webpack
- create-react-app
Testing
- ava, mocha or anything other that can use ts-node - ts-node supports programatically adding custom transformers so it can be used to run test suites.
- jest - I couldn't find a way to pass a custom transformer to ts-jest. A solution to this is to compile the test files prior to running them with jest.
Importing the transformer in your build configs:
// commonjs
const transformer = require('tsx-control-statements').default;
// ts
import transformer from 'tsx-control-statements';
Importing type definitions:
import {
For,
If,
With,
Choose,
When,
Otherwise
} from 'tsx-control-statements/components';
Reasons to not use any control statements for jsx:
Hard to statically type- Has been somewhat adressed, with the exception of
With
- Has been somewhat adressed, with the exception of
- Not part of the standard
- Not ordinary jsx elements
- Requires extra dependencies to use
- Many typescript tools do not support custom transformers in a convenient way