<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Renato Ruk</title>
    <description>The latest articles on Forem by Renato Ruk (@renatoruk).</description>
    <link>https://forem.com/renatoruk</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F44975%2F465408e6-d1cf-44e0-9070-4fb33a7124d7.png</url>
      <title>Forem: Renato Ruk</title>
      <link>https://forem.com/renatoruk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/renatoruk"/>
    <language>en</language>
    <item>
      <title>Benefits of using Cypress in GitHub actions</title>
      <dc:creator>Renato Ruk</dc:creator>
      <pubDate>Mon, 12 Oct 2020 15:40:42 +0000</pubDate>
      <link>https://forem.com/bornfightcompany/benefits-of-using-cypress-in-github-actions-53fk</link>
      <guid>https://forem.com/bornfightcompany/benefits-of-using-cypress-in-github-actions-53fk</guid>
      <description>&lt;p&gt;If you work with anything related to frontend and you haven’t tried Cypress yet, I highly suggest you give it a try. &lt;br&gt;
But, if you use Cypress and don’t have a CI/CD pipeline set up yet, read forward for the reasons why using GitHub actions for it might be valuable.&lt;/p&gt;

&lt;h1&gt;
  
  
  GitHub actions
&lt;/h1&gt;

&lt;p&gt;The &lt;a href="https://docs.github.com/en/free-pro-team@latest/actions" rel="noopener noreferrer"&gt;docs&lt;/a&gt; say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Automate, customize, and execute your software development workflows right in your repository with GitHub Actions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Cypress GitHub action - installation
&lt;/h3&gt;

&lt;p&gt;Read these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/free-pro-team@latest/actions/quickstart" rel="noopener noreferrer"&gt;GitHub actions basic quickstart&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/cypress-io/github-action" rel="noopener noreferrer"&gt;Cypress github action repository&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Benefits of integration
&lt;/h1&gt;

&lt;h3&gt;
  
  
  Prevention of merging broken code into the target branch
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;by leveraging code status checks of GitHub, there is the potential to list run Cypress checks and block PR merge if all &lt;a href="https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/enabling-required-status-checks" rel="noopener noreferrer"&gt;checks are not passing&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fs7s57i3nhrjrwa45wzvs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fs7s57i3nhrjrwa45wzvs.png" alt="status checks" width="800" height="496"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;&lt;a href="https://docs.cypress.io/guides/dashboard/github-integration.html#Status-checks" rel="noopener noreferrer"&gt;Image source: cypress docs&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Software stability
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;as the status checks prevent merging broken code, there is potentially greater awareness across the development team towards software quality and stability&lt;/li&gt;
&lt;li&gt;another potential outcome is that the team write more (and meaningful) tests because they see how much their code and tests have an impact on the complete codebase&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Tracking code coverage
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;although code coverage can be misused, it can be a valuable metric in determining which code flows are hit or missed&lt;/li&gt;
&lt;li&gt;plugin: &lt;a href="https://github.com/cypress-io/code-coverage" rel="noopener noreferrer"&gt;https://github.com/cypress-io/code-coverage&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Easier onboarding of new developers to project standards
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;if a new team member has not used Cypress before, he/she will potentially see the value of tests sooner if they're visible in the CI and they're not required to be run manually&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Developer’s ownership of CI/CD
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;In some organizations, CI/CD pipelines are the responsibility of the DevOps team&lt;/li&gt;
&lt;li&gt;Delegating some complex pipelines or asking help from the DevOps team is great for collaboration, but it's also really valuable skill knowing setting up the basic CI/CD flows&lt;/li&gt;
&lt;li&gt;I personally think this is really important because the developer can then tweak the CI/CD pipeline to the project's needs and potentially easily experiment with new features of relevant technologies on the projects (e.g. Cypress GitHub actions)&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;This post focuses (on very high level) on the integration of Cypress and GitHub actions, but can easily be applied to other testing frameworks such as Jest and other CI/CD services such as CircleCI or TravisCI. &lt;/p&gt;

&lt;p&gt;I personally found it very convenient to use it with GitHub actions because it's &lt;em&gt;baked in&lt;/em&gt; the GitHub itself, but you can freely use other services that support this kind of automated flows.&lt;/p&gt;

&lt;p&gt;Happy testing!&lt;/p&gt;

</description>
      <category>engineeringmonday</category>
      <category>cypress</category>
      <category>testing</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How to start with JavaScript library development</title>
      <dc:creator>Renato Ruk</dc:creator>
      <pubDate>Mon, 24 Aug 2020 15:59:29 +0000</pubDate>
      <link>https://forem.com/bornfightcompany/how-to-start-with-javascript-library-development-40mb</link>
      <guid>https://forem.com/bornfightcompany/how-to-start-with-javascript-library-development-40mb</guid>
      <description>&lt;p&gt;Software development is about solving problems. Sometimes the problems are tightly coupled with the domain and other times they are generic, not related to a specific business or field.&lt;/p&gt;

&lt;p&gt;To avoid repetition in solving problems, we as developers have the ability to abstract &amp;amp; extract our solutions to self-contained modules, most often called libraries or packages.&lt;/p&gt;

&lt;p&gt;JavaScript is no exception to this, and therefore we currently have a &lt;a href="https://www.npmjs.com/" rel="noopener noreferrer"&gt;plethora of options&lt;/a&gt; available.&lt;/p&gt;

&lt;p&gt;Despite the fact that there are currently more than a million packages available on NPM, there is still potential and value in creating your own.&lt;/p&gt;

&lt;h1&gt;
  
  
  What makes a good library?
&lt;/h1&gt;

&lt;p&gt;There are different kinds of libraries in terms of size and purpose. Whole frameworks could fall into an umbrella term of software libraries, but there are also one-liner functions wrapped in packages that are by definition, also considered libraries. Their context is often different, but some common rules can be applied to all of them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;solves a specific problem &lt;/li&gt;
&lt;li&gt;has good documentation&lt;/li&gt;
&lt;li&gt;easily extendable&lt;/li&gt;
&lt;li&gt;well tested&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Why even bother making a library when you can copy and paste?
&lt;/h1&gt;

&lt;p&gt;Abstracting a problem has its own costs. Sometimes, making an abstraction too early or without a defined plan can lead to accidental complexity or incorrect implementation. Therefore, using good old copy and paste strategy is oftentimes preferred solution, until we know the problem deeper.&lt;/p&gt;

&lt;p&gt;But, if the code is copied too much, it can become difficult to maintain and upgrade. The reason behind this is often because the copied code relies on repeating some patterns in a certain way and if we don't repeat the same structure, bugs can occur.&lt;/p&gt;

&lt;p&gt;Knowing the right timing for abstraction is an art of its own, but a general rule of thumb is that a piece of code is a good candidate of abstraction after being copied for two or three times. &lt;/p&gt;

&lt;h2&gt;
  
  
  JavaScript library anatomy
&lt;/h2&gt;

&lt;p&gt;After we've identified our candidate for abstraction, the next step is to learn about the structure of javascript libraries.&lt;/p&gt;

&lt;p&gt;To be considered a JS package that can be published to NPM or Yarn, it should have the following set up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;valid package.json&lt;/li&gt;
&lt;li&gt;exported JavaScript modules in one or multiple formats

&lt;ul&gt;
&lt;li&gt;Universal Module Definition&lt;/li&gt;
&lt;li&gt;CommonJS&lt;/li&gt;
&lt;li&gt;Asynchronous Module Definition&lt;/li&gt;
&lt;li&gt;ES2015 Modules&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This is a minimum requirement and, depending on the complexity of the software, there could be many more parts to it.&lt;/p&gt;

&lt;h1&gt;
  
  
  Local development
&lt;/h1&gt;

&lt;p&gt;The best way to develop a library is to treat as a realm of its own, if possible. That means that it has its own testing strategy and its own system for running the project in isolation.&lt;/p&gt;

&lt;p&gt;In scenarios when this is not possible or we just want to test our package in a real-world application before publishing it, there are ways to use them in locally. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Npm / Yarn Link&lt;/strong&gt;&lt;br&gt;
Local development can be achieved by linking our packages to a single local source stored on the disk. So, if we're using Yarn, we can use &lt;a href="https://classic.yarnpkg.com/en/docs/cli/link/" rel="noopener noreferrer"&gt;yarn link&lt;/a&gt; command, and for the NPM there is an &lt;a href="https://docs.npmjs.com/cli/link" rel="noopener noreferrer"&gt;equivalent command&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Yalc&lt;/strong&gt; &lt;br&gt;
Yalc is a great tool to achieve more control of local package installation and dependencies. It behaves as a local repository and simulates the behaviour of having a remote repository like npm.&lt;/p&gt;

&lt;h1&gt;
  
  
  Monorepo
&lt;/h1&gt;

&lt;p&gt;Some libraries may be too big to be contained in a single package. Such libraries may be architected as a mono-repository, by storing multiple smaller packages in a single repository. &lt;/p&gt;

&lt;p&gt;This can result in better control of source code and better modularity, especially if many parts of the system depend on different independent modules or consumers of the library don't need to use everything that the library contains.&lt;/p&gt;

&lt;p&gt;An example of monorepo is &lt;a href="https://github.com/babel/babel" rel="noopener noreferrer"&gt;babel&lt;/a&gt; repository that's developed with &lt;a href="https://github.com/lerna/lerna" rel="noopener noreferrer"&gt;lerna&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Best practices
&lt;/h1&gt;

&lt;p&gt;General software development practices can be applied to library development. Most notable elements are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tests

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://jestjs.io/" rel="noopener noreferrer"&gt;https://jestjs.io/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.cypress.io/" rel="noopener noreferrer"&gt;https://www.cypress.io/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;documentation 

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docusaurus.io/" rel="noopener noreferrer"&gt;https://docusaurus.io/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;a href="https://commitlint.js.org/#/" rel="noopener noreferrer"&gt;commitlint&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href="https://www.npmjs.com/package/auto-changelog" rel="noopener noreferrer"&gt;(auto)changelog&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;semantic versioning&lt;/li&gt;

&lt;li&gt;linting&lt;/li&gt;

&lt;/ul&gt;

&lt;h1&gt;
  
  
  Zero-config solutions
&lt;/h1&gt;

&lt;p&gt;Setting up a JavaScript library with all the requirements mentioned can be a bit overwhelming. After the initial development of the library, it calls for maintenance of dependent packages and upgrade of features and procedures.&lt;/p&gt;

&lt;p&gt;To simplify the package development we can use CLI-s that solve most of the common problems of the library set up so we can focus on the code itself.&lt;/p&gt;

&lt;p&gt;Possible options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/formium/tsdx" rel="noopener noreferrer"&gt;TSDX&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Zero-config CLI for TypeScript package development&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/developit/microbundle" rel="noopener noreferrer"&gt;Microbundle&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;Zero-configuration bundler for tiny modules&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;JavaScript libraries are self-contained software modules that export reusable code for usage in different projects and scenarios. We learned about some general guides which can improve the quality of packages and what are the common parts of every package. To simplify the package development, we can use zero-config CLI-s which allow us to focus on the code, but it's important to understand what these tools do for us so we can have better control and knowledge on how to fix something if it's beyond the zero-configuration method.&lt;/p&gt;

&lt;p&gt;Happy packaging!&lt;/p&gt;

</description>
      <category>engineeringmonday</category>
      <category>javascript</category>
      <category>libraries</category>
      <category>architecture</category>
    </item>
    <item>
      <title>10 principles of scalable frontend projects</title>
      <dc:creator>Renato Ruk</dc:creator>
      <pubDate>Mon, 30 Mar 2020 08:38:22 +0000</pubDate>
      <link>https://forem.com/bornfightcompany/10-principles-of-scalable-frontend-projects-3i3d</link>
      <guid>https://forem.com/bornfightcompany/10-principles-of-scalable-frontend-projects-3i3d</guid>
      <description>&lt;h1&gt;
  
  
  A common problem
&lt;/h1&gt;

&lt;p&gt;Web apps have come a long way since their initial appearance. We all now know how powerful JavaScript on the web is, and how many endless possibilities exist in terms of framework and technology choices. Different frameworks have their pros and cons, but some core methodologies can be applied to almost every framework. Tools such as create-react-app, next.js, vue-cli, etc., are really helpful for bootstrapping a project and its structure, but after that, you're free to form an application to your preferences and project's requirements. &lt;/p&gt;

&lt;p&gt;I've collected some principles I found valuable when building web applications with &lt;strong&gt;React&lt;/strong&gt; and &lt;strong&gt;Vue&lt;/strong&gt; that can help you in defining a direction and keeping things tidy and sound. Most of the principles can be applied to all software, but this list is a bit specific to web apps. &lt;/p&gt;

&lt;h1&gt;
  
  
  Principles
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Developing components, not screens&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;try to encapsulate all the logic of a component in isolation, so it can be rendered everywhere with ease, for example, in different screens and sections&lt;/li&gt;
&lt;li&gt;all the CRUD operations go inside the &lt;em&gt;controllers / providers&lt;/em&gt; for the data it needs, and that data is passed to &lt;em&gt;presentational&lt;/em&gt; components

&lt;ul&gt;
&lt;li&gt;one common scenario is redux/vuex - data is persisted to a store and is treated as a single source of truth, and container components extract the relevant data &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separating presentational and business / controller layer&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;not all components need to be connected to stores, backend API or some other service&lt;/li&gt;
&lt;li&gt;by making components "data source agnostic", reusability is greatly increased&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Using a standardized way of fetching the data&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://resthooks.io/" rel="noopener noreferrer"&gt;rest-hooks&lt;/a&gt; is a great example of this principle as it encourages a predictable and readable structure for API calls&lt;/li&gt;
&lt;li&gt;for some projects, using explicit &lt;code&gt;fetch&lt;/code&gt; calls may be sufficient, but if you're dealing with a lot of resources and relationships, having an abstraction over backend API can be helpful&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Having a common user input strategy&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;forms, select tags, validations, error states all fall into this category&lt;/li&gt;
&lt;li&gt;UI component libraries such as &lt;a href="https://ant.design/components/form/" rel="noopener noreferrer"&gt;antd&lt;/a&gt; provide a solution for this out of the box&lt;/li&gt;
&lt;li&gt;if you're creating an app without a UI library, consider putting a bit more emphasis on standardizing these elements for better UX&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Writing automated tests&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;components are, in my experience, best tested with integration tests, specifically with &lt;a href="https://www.cypress.io/" rel="noopener noreferrer"&gt;Cypress&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;business logic should be thoroughly covered with unit tests&lt;/li&gt;
&lt;li&gt;e2e are valuable for smoke testing larger user flows; they can help detect regressions between the client and the API
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Using storybook for creating reusable components&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://storybook.js.org/" rel="noopener noreferrer"&gt;https://storybook.js.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;great way to collaborate with designers and discuss functionality&lt;/li&gt;
&lt;li&gt;serves as a "living style guide" for your app&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Using linters and formatters&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;examples of linters: ESLint, stylelint&lt;/li&gt;
&lt;li&gt;most of the bootstrap tools such as create-react-app pre-install linters for you, but if you're on a legacy codebase, they might not be applied&lt;/li&gt;
&lt;li&gt;they can help you catch bugs, but they can also be used in defining code style for your team — this can help in reducing mental load when developing upon a feature that you've inherited from your colleague&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/SonarSource/eslint-plugin-sonarjs" rel="noopener noreferrer"&gt;sonarJS eslint plugin&lt;/a&gt; can help improve code quality as it checks for logical structure&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://prettier.io/" rel="noopener noreferrer"&gt;prettier&lt;/a&gt; is an awesome code formatter that you set up only once, and never think about it again — very useful when working in a team&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avoiding global styles&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;overrides and resets can be made global&lt;/li&gt;
&lt;li&gt;CSS modules or CSS-in-JS are some of the technologies that can help in achieving scoped, isolated styles&lt;/li&gt;
&lt;li&gt;local styles can often lead to better component reusability&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Using structured version control&lt;/strong&gt; 

&lt;ul&gt;
&lt;li&gt;using a branching model

&lt;ul&gt;
&lt;li&gt;e.g. &lt;em&gt;&lt;a href="https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow" rel="noopener noreferrer"&gt;gitflow&lt;/a&gt;&lt;/em&gt; — "a branching model for Git, created by Vincent Driessen"&lt;/li&gt;
&lt;li&gt;having a structure in your version control is indispensable for working in a team, but it is helpful even when working on your own&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;linting commit messages - &lt;a href="https://commitlint.js.org" rel="noopener noreferrer"&gt;commitlint&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;useful in creating a changelog and finding bugs as the project develops&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/angular/angular/blob/master/CONTRIBUTING.md" rel="noopener noreferrer"&gt;angular's commit message rules&lt;/a&gt; are often applicable to any software project, and commitlint can help you in keeping those messages consistent with the ruleset&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keeping a changelog&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;on the beginning of every project, it's usually easy to keep track of all the changes in your mental context&lt;/li&gt;
&lt;li&gt;as the project grows, the changelog can serve as a central place for discovering notable changes to a codebase, even months and years into development&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  The never-ending list
&lt;/h1&gt;

&lt;p&gt;That's all folks! There could surely be many more items added to the list, depending on the project domain and technology, but I find these can improve many frontend apps radically. Almost all of the principles can be applied progressively and by priority defined by you and your team, so no need to worry of applying them all of once. &lt;/p&gt;

&lt;p&gt;What principles do you find &lt;em&gt;super important&lt;/em&gt; when creating web apps? Share these below in the comments! ⬇️ &lt;/p&gt;

&lt;p&gt;Thanks for reading. 🙏&lt;/p&gt;

</description>
      <category>engineeringmonday</category>
      <category>javascript</category>
      <category>architecture</category>
      <category>frontend</category>
    </item>
    <item>
      <title>TypeScript compile-time interface validation with tagged unions</title>
      <dc:creator>Renato Ruk</dc:creator>
      <pubDate>Mon, 09 Mar 2020 09:09:09 +0000</pubDate>
      <link>https://forem.com/bornfightcompany/typescript-compile-time-interface-validation-with-tagged-unions-c04</link>
      <guid>https://forem.com/bornfightcompany/typescript-compile-time-interface-validation-with-tagged-unions-c04</guid>
      <description>&lt;p&gt;If you work with TypeScript, you probably love the static type checking it provides. It’s a very powerful programming tool that helps us detect bugs before they can even run, by showing us compile errors. &lt;/p&gt;

&lt;p&gt;Using interfaces is by itself very effective technique for writing code that is correct by a specified contract. But, what if we have similar contracts that define some intrinsic behaviour of a class or a function and we want to combine their definitions?&lt;/p&gt;

&lt;p&gt;Let's image we're building a table with React that shows a list of records from the backend API. The component receives &lt;code&gt;columnData&lt;/code&gt; of type &lt;code&gt;TableColumnData[]&lt;/code&gt; as a prop, which is an array of config objects that determine how each column should be built and how it should behave. The content of the column is specified with the &lt;code&gt;dataIndex&lt;/code&gt; field of the interface, which renders the value of matching key from each record passed to the table. As the data is passed from the backend API, it can have thousands of records, therefore we need to add the ability to search only the ones we need. &lt;/p&gt;

&lt;p&gt;We can then add a filter property on the column that will, if not left empty, render a text input in our column header. By submitting the search, the table will do a request on the API with the new value of the specified field.&lt;/p&gt;

&lt;p&gt;The overly simplified version of the &lt;code&gt;TableColumnData&lt;/code&gt; interface could look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;TableColumnData&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;dataIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;TableColumnFilter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the interface for the filter can be specified like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;TableColumnFilter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, table should be used similarly to this (pseudo-react-code):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyTable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;columnData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;dataIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;birthday&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;dataIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;birthday&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ResourceTable&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;columnData&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;columnData&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The filtering made the user experience of our table richer, but what if we want to add new types of filters, such as, for example, date filter? &lt;/p&gt;

&lt;p&gt;We can create another interface for that filter type, rename the TableColumnFilter to TableColumnTextFilter and combine the two filter types together in a union.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;TableColumnDateFilter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;showHours&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, TableColumnFilter can be defined like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;TableColumnFilter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TableColumnTextFilter&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;TableColumnDateFilter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our table still works, but now there is no way of knowing we used the proper interface for the filter type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyTable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;columnData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;dataIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="c1"&gt;// does not make much sense&lt;/span&gt;
                &lt;span class="na"&gt;showHours&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;birthday&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;dataIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;birthday&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;birthday&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ResourceTable&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;columnData&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;columnData&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can then narrow the types further by creating an enum. That enum will tell the TypeScript compiler which filter type is used, and therefore it will hint us what the rest of the interface should look like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;ColumnFilterType&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;Date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;date&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;TableColumnTextFilter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ColumnFilterType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;TableColumnDateFilter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ColumnFilterType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;showHours&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern is called &lt;strong&gt;discriminated union&lt;/strong&gt;, aka &lt;strong&gt;tagged union&lt;/strong&gt; or &lt;strong&gt;algebraic data type&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;In our scenario, the &lt;em&gt;discriminant&lt;/em&gt; is the type field, which will be used to differentiate the types.&lt;/p&gt;

&lt;p&gt;Now, expanding our table example with the type field, we get a compile error when using enums.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyTable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;columnData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;dataIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;showHours&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ColumnFilterType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;birthday&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;dataIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;birthday&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;birthday&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ColumnFilterType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ResourceTable&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;columnData&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;columnData&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The error is &lt;code&gt;Type 'ColumnFilterType' is not assignable to type 'ColumnFilterType.Date'&lt;/code&gt;. This is expected as TypeScript thinks we use &lt;code&gt;ColumnFilterType&lt;/code&gt; as a value for the type field.&lt;/p&gt;

&lt;p&gt;We can prevent this by using &lt;a href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#const-assertions" rel="noopener noreferrer"&gt;const assertion&lt;/a&gt; and prevent further type widening.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyTable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;columnData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;dataIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ColumnFilterType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Text&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;birthday&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;dataIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;birthday&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;birthday&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ColumnFilterType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;showHours&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ResourceTable&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;columnData&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;columnData&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, using the interface incorrectly will result in a compile error, which may help you prevent runtime errors if that internal behaviour is determined by a correct interface. To me, this ability to have pre-compile validation of implementation is what makes typed languages really stand out. They are especially helpful in collaboration and refactoring.&lt;/p&gt;

&lt;p&gt;Have you had a chance to used tagged unions before? Do you have a TypeScript feature that you could not live without? Share those in the comments below! ✌🏻&lt;/p&gt;

</description>
      <category>engineeringmonday</category>
      <category>typescript</category>
      <category>react</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Configuring Cypress for multiple testing environments</title>
      <dc:creator>Renato Ruk</dc:creator>
      <pubDate>Mon, 17 Feb 2020 08:18:35 +0000</pubDate>
      <link>https://forem.com/bornfightcompany/configuring-cypress-for-multiple-testing-environments-406l</link>
      <guid>https://forem.com/bornfightcompany/configuring-cypress-for-multiple-testing-environments-406l</guid>
      <description>&lt;p&gt;&lt;a href="https://www.cypress.io/" rel="noopener noreferrer"&gt;Cypress&lt;/a&gt; is a great tool for front-end testing. It provides features such as mocking HTTP responses, stubbing objects and methods and simulating user interaction. &lt;/p&gt;

&lt;p&gt;It can be used as a fully-fledged front-end testing framework for unit, integration and e2e tests. This post assumes you are already somewhat familiar with Cypress and its methodologies, and it serves as a guide for advanced configuration which can help you structure and organize your tests. So, let's get straight to the point.&lt;/p&gt;

&lt;p&gt;When first &lt;a href="https://docs.cypress.io/guides/getting-started/installing-cypress.html#Opening-Cypress" rel="noopener noreferrer"&gt;installing and running Cypress with npm or yarn&lt;/a&gt;, it will generate some default tests and configuration for you. &lt;/p&gt;

&lt;p&gt;At the time of writing, these files and directories are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// configuration&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;cypress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt; 

&lt;span class="c1"&gt;// directories with respective files / tests&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;cypress&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;fixtures&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt; 
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;cypress&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;integration&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;examples&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;cypress&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;plugins&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;cypress&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;support&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The default command for running Cypress in interactive mode is &lt;code&gt;./node_modules/.bin/cypress open&lt;/code&gt; and it will by default use &lt;code&gt;cypress.json&lt;/code&gt; as a configuration file.&lt;/p&gt;

&lt;p&gt;An example of a configuration file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"baseUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:3000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"integrationFolder"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cypress/integration"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cypress will also by default run all tests from the &lt;code&gt;integration&lt;/code&gt; directory, supporting nested directories. We could add all our unit, integration and e2e tests to the &lt;code&gt;integration&lt;/code&gt; directory, but we want to configure some things differently, depending on our environment. For example, we could avoid some API calls from the app by completely blocking the host from the configuration file.&lt;/p&gt;

&lt;p&gt;Let's say we want to mock the response for the &lt;code&gt;foo&lt;/code&gt; resource from our API. In our Cypress test, we'll do something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should fetch and render foo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;server&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="c1"&gt;// load fixture and mock response&lt;/span&gt;
      &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="c1"&gt;// you can also setup base url in cypress config&lt;/span&gt;
          &lt;span class="c1"&gt;// it can be used as Cypress.env('BASE_API_URL')&lt;/span&gt;
          &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.my-app.com/foo/**&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
     &lt;span class="p"&gt;});&lt;/span&gt;
     &lt;span class="c1"&gt;// ...rest of the test&lt;/span&gt;
&lt;span class="p"&gt;}));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By specifying the same host of our backend API in blacklistHosts, Cypress will allow creating mock responses for the blocked domain, but it will intercept and disallow any other response to that same domain. This could be a handy setup for integration testing, where you want to test a feature in a very specific scenario, mocking all the requests and data the application needs. This is a powerful configuration, but for e2e tests, we don't want to block the real API because we need to test front-end with the real back-end services. Therefore, we need to split our configs.&lt;/p&gt;

&lt;p&gt;We'll first create a &lt;code&gt;cypress-integration.json&lt;/code&gt; file and place it inside auto-generated &lt;code&gt;cypress&lt;/code&gt; directory. Then, we specify another path for the location of the tests. For our use-case, that is &lt;code&gt;cypress/tests/integration&lt;/code&gt;. Notice the change from &lt;code&gt;integration&lt;/code&gt; to &lt;code&gt;tests&lt;/code&gt; naming. The naming is not needed to be replicated; feel free to use directory names which suit you and your team.&lt;/p&gt;

&lt;p&gt;Example &lt;code&gt;cypress-integration.json&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"baseUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:3000"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"integrationFolder"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cypress/tests/integration"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"blacklistHosts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"api.my-app.com"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, to run only those tests suites for integration tests, we need to add commands in our &lt;code&gt;package.json&lt;/code&gt;. &lt;code&gt;"cypress:open:integration"&lt;/code&gt; is for "interactive" mode, and &lt;code&gt;cypress:run:integration&lt;/code&gt; is for running in "CI" mode.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"cypress:open:integration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cypress open --config-file cypress/cypress-integration.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"cypress:run:integration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cypress run --config-file cypress/cypress-integration.json"&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, you can repeat this process for other environments, such as e2e, storybook or unit tests, and provide different configurations for them. 🚀&lt;/p&gt;

&lt;p&gt;Thank you for reading! 🙏&lt;/p&gt;

</description>
      <category>engineeringmonday</category>
      <category>javascript</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
