734fd997c9
Bumps [eslint](https://github.com/eslint/eslint) from 8.13.0 to 8.14.0. - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/compare/v8.13.0...v8.14.0) --- updated-dependencies: - dependency-name: eslint dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> |
||
---|---|---|
.github | ||
.vscode | ||
src | ||
.eslintrc.js | ||
.gitignore | ||
.nvmrc | ||
.prettierrc | ||
jest.config.js | ||
package.json | ||
README.md | ||
tsconfig.json | ||
yarn.lock |
Inato fp-ts
training
This repo is a work in progress toward having a comprehensive training material
to onboard people on using fp-ts
efficiently.
The exercises consist of unimplemented functions and their associated failing tests.
But first, it is essential to understand why we are using fp-ts
. I suggest you read this article and then start the exercises.
After cloning the repository, setup the project by running
$ yarn
To run the tests, simply run
$ yarn test
You can also run them in watch mode:
$ yarn test:watch
Finally, if you wish to only run the tests for a given exercise exoN
, you can
run the following:
$ yarn test[:watch] exoN
The exercises are organized into exoN
folders and most of what is required to
complete each is detailed in the comments.
code style guide
For readability purpose, we replace ReaderTaskEither
by rte
- Use
flow
instead ofpipe
when possibleWhy? Using flow reduces the amount of variables to declare in a method, hence the visibility and readability of the code
// Bad
const formatUserPhoneNumber = (user: User) =>
pipe(user, User.phoneNumber, User.formatPhoneNumber);
// Good
const formatUserPhoneNumber = flow(User.phoneNumber, User.formatPhoneNumber);
- Avoid using
boolean
methodmatch
when unecessaryWhy? boolean.match can lower the global understanding of a method and enforce nested pipes. Using classic
if/else
is often the best option
// Bad
const triggerEmailCampaign = ({
user,
...emailSettings
}: {
user: User} & EmailSettings) =>
pipe(
user.nationality === 'FR',
boolean.match(
() => triggerGlobalEmailCampaign({ to: user.email, emailSettings }),
() => triggerFrenchEmailCampaign({ to: user.email, emailSettings }),
),
);
// Good
const triggerEmailCampaign = ({
user,
...emailSettings
}: { user: User } & EmailSettings) => {
if (user.nationality === 'FR') {
return triggerFrenchEmailCampaign({ to: user.email, emailSettings });
}
return triggerGlobalEmailCampaign({ to: user.email, emailSettings });
- Avoid nested pipes
Why? They lower global understanding of the code. We allow ourselves 2 levels of piping maximum per function and tend to do atomic functions instead
// Bad
export const convertDollarAmountInCountryCurrency = ({
countryName,
amountInDollar,
}: {
countryName: CountryName;
amountInDollar: number;
}) =>
pipe(
getCountryCode(countryName),
either.map(
countryCode =>
pipe(
getCountryCurrency(countryCode),
option.map(
flow(
convertFromDollarAmount(amountInDollar),
convertedAmount =>
console.log(
`converted amount for country ${countryCode} is ${convertedAmount}`,
),
),
),
),
),
),
);
// Good
export const convertDollarAmountInCountryCurrency = (amountInDollar: number) =>
flow(
getCountryCode,
either.map(convertDollarAmountToCountryCodeCurrency(amountInDollar)),
);
const convertDollarAmountToCountryCodeCurrency =
(amountInDollar: number) => (countryCode: CountryCode) =>
pipe(
getCountryCurrency(countryCode),
option.map(convertFromDollarAmount(amountInDollar)),
option.map(convertedAmount =>
console.log(
`converted amount for country ${countryCode} is ${convertedAmount}`,
),
),
);