{"@attributes":{"version":"2.0"},"channel":{"title":"DEV Community: Danielo Artola","description":"The latest articles on DEV Community by Danielo Artola (@danielart).","link":"https:\/\/dev.to\/danielart","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%2F162992%2F8157a3c5-6db2-4564-895e-64bc9c370005.jpg","title":"DEV Community: Danielo Artola","link":"https:\/\/dev.to\/danielart"},"language":"en","item":[{"title":"5 Atlassian MCP Hacks to supercharge your workflow","pubDate":"Thu, 16 Oct 2025 12:35:56 +0000","link":"https:\/\/dev.to\/manomano-tech-team\/5-atlassian-mcp-hacks-to-supercharge-your-workflow-3hfj","guid":"https:\/\/dev.to\/manomano-tech-team\/5-atlassian-mcp-hacks-to-supercharge-your-workflow-3hfj","description":"<p>I hate to be switching over and over between the IDE, the task in Jira to review details, Confluence, or other docs to ensure I have <strong>the<\/strong> details, finding sometimes outdated docs, so I was curious how I could save time on that through AI. Every time we jump from writing code to updating a ticket or checking a requirement, we lose focus and momentum.<\/p>\n\n<p>After some weeks testing, configuring, <strong>failing<\/strong>, and enjoying the process, I bring you here 5 situations that I found useful and saved me time. Your time is also important, so I added <strong>TL;DR<\/strong> in all the sections!<\/p>\n\n<p><strong>The case:<\/strong> During a recent migration of a microservice from Node.js to Kotlin, I put this to the test. Basically, the AI should help not only to do a plan for migration, <strong>but also<\/strong> we needed ideas for a <strong>safe<\/strong> rollout, a list of all the endpoints, business rules, external dependencies, <strong>and<\/strong> adapt the plan for the team size, etc. I created a single workspace in my IDE containing the legacy Node.js project, the new Kotlin boilerplate, and another small service we were absorbing to <strong>provide<\/strong> all the context I thought relevant for once I start prompting in the IDE. By giving the agent access to this full context, along with a link to our \"Backend Guidelines\" page in Confluence, I could kickstart the documentation process. A migration is a big topic, with several steps:<\/p>\n\n<ol>\n<li><p>Create a plan to migrate it, and add it to somewhere easy to edit, share, review with the team, update, comment -&gt; a Confluence page<\/p><\/li>\n<li><p>Divide the work according to the team size to achieve in a <strong>determined<\/strong> time -&gt; a Feature in Jira with all the tasks related<\/p><\/li>\n<li><p>Follow our internal guidelines and avoid generic results -&gt; use the current documentation in Confluence to consider them in the code<\/p><\/li>\n<li><p>Ensure the information in the tickets: Edit tasks descriptions, info, and adding comments with the info in the code<\/p><\/li>\n<li><p>Code fast small features or tech debt -&gt; Implementing a Jira ticket<\/p><\/li>\n<\/ol>\n\n<p><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%2Fuploads%2Farticles%2Fb1gupo4rxbvonqf4ek7t.png\" class=\"article-body-image-wrapper\"><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%2Fuploads%2Farticles%2Fb1gupo4rxbvonqf4ek7t.png\" alt=\"Steps in SDLC reinforced by AI for the microservice migration\" width=\"642\" height=\"589\"><\/a><\/p>\n\n<h3>\n  \n  \n  1. Documentation on Autopilot: From Code to Confluence\n<\/h3>\n\n<p><strong>TL;DR:<\/strong> Use the AI to analyze projects (or specific pull requests) to generate or update technical documentation in Confluence, <strong>provide it with<\/strong> the best context, ask <strong>it to improve<\/strong> the prompt; this will help to have your docs sync with your code with minimal effort.<\/p>\n\n<p>Generating and <em>updating<\/em> documentation is a task many of us postpone or <strong>sometimes defer to<\/strong> the bottom of the backlog. With an integrated AI agent, you can delegate a significant portion of this work.<\/p>\n\n<p>Why in a document? Because it's easier to read and review than in the agent chat, and I can edit what I want to precise or correct easily.<\/p>\n\n<p>Why a conversation? Because I involved the agent in the <strong>decisions<\/strong>.<\/p>\n\n<p>Why did you mention a conversation? Because I shared my idea with <strong>Gemini 2.5 Flash in the web<\/strong> to help me generate the best prompt for the Cursor Agent (which primarily runs on <strong>Gemini 2.5 Pro<\/strong> for coding tasks) with my ideas, and I asked <strong>it to ask me questions<\/strong> to ensure the best result.<\/p>\n\n<p>After refining the prompt and editing some things, I ended with a big prompt with around 4k characters. It\u2019s big, but needed to have a good start. Having the output in Confluence helped me to edit easily some sections, add or remove parts, add comments in some parts, and copy-paste some of the text to ask the AI if that was an assumption or something in the code (and guess what, there were some wrong assumptions, AI is not perfect, folks, yet).<\/p>\n\n<p><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%2Fuploads%2Farticles%2Fq3p1q8ekcyb95xko4c80.png\" class=\"article-body-image-wrapper\"><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%2Fuploads%2Farticles%2Fq3p1q8ekcyb95xko4c80.png\" alt=\"Steps to build a plan for migration with AI agent\" width=\"588\" height=\"522\"><\/a><\/p>\n\n<p>Here is the final, structured prompt used to generate the migration plan:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>You are a Senior Solutions Architect tasked with creating a comprehensive \nand actionable migration plan. \n\nProject: Migrate the existing [Legacy Service Name] microservice \n(Node.js\/TypeScript, GraphQL, REST) to the new [Target Service Name] microservice \n(Kotlin, Ktor, GraphQL). \n\nPrimary Goal: Complete the migration and have the new service fully operational \nin Production within a three-month timeline. \n\nKey Context &amp; Constraints: \nTeam: The project will be executed by a team of 4 backend developers. \nTechnology: We must maintain GraphQL compatibility. \nThe goal is to reuse the existing GraphQL schema and as much of the domain logic \nand DTOs as possible to ensure a smooth transition for frontend clients. \nDevelopment Velocity: \nThe plan should incorporate the use of AI-powered code generation tools \nto accelerate the translation of business logic from TypeScript to Kotlin \nand to generate boilerplate code. \nStandards: The entire plan, architecture, and proposed implementation must \nstrictly adhere to the best practices documented in the [Your Company Name] \nBackend Guidelines found here: [Link to Backend Guidelines] \n\nRequired Plan Structure: Please generate the plan with the following \ndistinct sections: \n1. Executive Summary \n2. Gap Analysis &amp; Feature Parity Strategy \n3. Development &amp; AI-Assisted Strategy \n4. Phased Rollout Strategy (Zero-Risk Production Deployment) \n5. Monitoring, Logging, and Alerting Strategy ([Observability Platform]) \n6. Timeline: Sprints &amp; Milestones (3 Months) \n7. Risks &amp; Dependencies. \n\nWrite the output in **Confluence**, in the space for \n[Wiki Space ID] &gt; [Parent Page], called \"[Project Name] Node &lt;&gt; KT migration\".\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>\ud83d\udd16 Add this to your prompts to improve results:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>Ask me any questions you find relevant have better information.\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>Then add them to a Google Doc or similar, it will be easier to answer. Then just copy all the question and answers<\/p>\n\n<p>\u203c\ufe0f I prefer to be specific with the destination link. Vague instructions can sometimes lead the AI to edit the wrong page. Double-check the target, I edited wrong documentation because there were two pages with the same title in different folders!<\/p>\n\n<h3>\n  \n  \n  2. Intelligent Task Creation, Right From the Codebase\n<\/h3>\n\n<p><strong>TL;DR:<\/strong> Define high-level constraints (time, team size) to have the AI decompose epics into manageable tasks. When you spot bad code, select it and instantly create a detailed tech debt ticket in Jira.<\/p>\n\n<p>Whether you're breaking down a massive epic or flagging unexpected tech debt, you can create perfectly detailed Jira tickets without ever leaving your code.<\/p>\n\n<p>For the migration project, I tasked the AI with planning. Here is the prompt used to decompose the epic into manageable, one-week tasks:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>Act as a senior project manager responsible for planning a microservice migration.\n\n**Context:**\nBased on the migration plan from Node.js to Kotlin for the [microservice A] to [microservice B] that we just finalized in our previous conversation, your task is to generate a detailed list of Jira tasks required to execute this plan.\n\n**Jira Target Information:**\n- Jira Feature\/Epic Link to associate tasks with: [Link to Feature\/Epic]\n\n**Rules and Constraints:**\n1.  **Adherence to Plan:** All tasks must directly correspond to the phases and steps we previously agreed upon.\n2.  **Team &amp; Timeframe:** Remember, our team consists of 4 developers and the target timeframe is 12 weeks.\n3.  **Task Granularity:** No single task should exceed one week of effort for a single developer. If a step from our plan is too large, break it down into smaller, logical sub-tasks.\n4.  **Dependencies:** Identify and order the tasks logically.\n\n**Final Step (Review):**\nBefore you execute the creation in Jira, please present the full list of proposed tickets here in the chat for my review and final approval. This is a dry run.\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>After a bit of back-and-forth, I gave the final command to execute the creation in Jira.<\/p>\n\n<p>A more powerful feature is on-the-fly tech debt flagging. We've all been there: you're working on a feature and stumble upon a piece of convoluted \"spaghetti code.\" Instead of letting it slide, I now select the code block and prompt the agent:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>Create a tech debt ticket in Jira to refactor this. \nExplain that it has high complexity and doesn't follow our guidelines. \nReference the file @path\/to\/the\/problematic\/file.js \nin the description and propose a solution explaining the why and how.\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>Using the <code>@<\/code> symbol (formerly <code>#<\/code> in some tools) to reference specific files is always better if you want to tag several files, try always to provide the best context for best results. It gives the AI precise, unambiguous context, leading to faster and more accurate results. You're not making it search; you're telling it exactly where to look. This focused context approach is a key feature of modern AI IDEs, allowing you to scope the AI's attention precisely.<\/p>\n\n<h3>\n  \n  \n  3. Weaving Confluence Knowledge into Your Code\n<\/h3>\n\n<p><strong>TL;DR:<\/strong> Provide the AI with links to relevant Confluence pages (e.g., coding guidelines, business logic) to use as context when generating or refactoring code.<\/p>\n\n<p>How many times have you had to dig through Confluence to find that one specific business rule or coding standard? I don\u2019t remember 100% of things, and I hate to be searching some specific info in Confluence, <strong>switching over and over<\/strong> screens. Now, you can bring that knowledge directly to the agent that writes your code.<\/p>\n\n<p>When I need the AI to generate code that must adhere to complex business logic documented in Confluence (or follow several internal guidelines), I start a conversation by providing the link. I first verify it has understood the content with a prompt like<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>Summarize the key business rules from this document regarding user authentication.\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>Once I confirm the agent has \"ingested\" the knowledge correctly, I can proceed with my code generation request, confident that it will respect the documented constraints.<\/p>\n\n<p>\ud83d\udd16 If you don\u2019t trust <strong>the<\/strong> AI answers 100% like me, I recommend you to ask something like<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>Explain to me how you took the info provided and how you adapted it \nand considered it in the code\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>This is currently the best way to provide external knowledge through Atlassian MCP. While Atlassian Intelligence is moving towards a future where the AI can automatically index and understand your entire Confluence space, the \"link and verify\" method is the best practice at the moment of writing this.<\/p>\n\n<h3>\n  \n  \n  4. The IDE as Your Jira Cockpit\n<\/h3>\n\n<p><strong>TL;DR:<\/strong> Use natural language commands in your IDE to add comments to tickets, change their status, or clarify descriptions, drastically reducing the need to switch to the Jira web UI.<\/p>\n\n<p>Small, repetitive Jira actions add up, pulling you out of your coding mindset. You can execute most of these tasks with a single command from your editor.<\/p>\n\n<p>The actions that save me the most time are:<\/p>\n\n<ul>\n<li><p><strong>Adding Contextual Comments:<\/strong> Highlighting a discrepancy in the code and telling the agent, \"Add a comment to ticket XYZ explaining that the current implementation doesn't fit our business rules and requires extra development not considered in the task.\"<\/p><\/li>\n<li><p><strong>Changing Status:<\/strong> Simple commands like <code>Move this ticket to 'In Progress' and give me a summary of the app<\/code> or <code>Move this ticket to 'Code Review' and push the code<\/code> (I don\u2019t waste tokens just to move a ticket, I do it with a relevant action).<\/p><\/li>\n<li><p><strong>Clarifying Ticket Descriptions:<\/strong> Sometimes a ticket's description is too vague or you forgot to mention something, or you have to correct a wrong assumption (yes, all of that sometimes happens). After discovering the necessary technical details, I'll ask the agent to update it. This is a great practice for enriching the ticket with implementation details.<\/p><\/li>\n<\/ul>\n\n<p>\u203c\ufe0f However, be mindful of when editing a description is appropriate.<\/p>\n\n<ul>\n<li>  <strong>Good Practice (Clarification):<\/strong> Adding technical notes, like \"The new field must be encrypted in the database using AES-256.\" This is the precise command used for ticket refinement:\n<\/li>\n<\/ul>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>Edit the description removing assumptions and adding only things in the code\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<ul>\n<li>  <strong>Bad Practice (Scope Creep):<\/strong> Changing the core requirement, like turning \"Add a 'First Name' field\" into \"Add 'First Name' and 'Date of Birth' fields and a new validation flow.\" The latter should be a new ticket.<\/li>\n<\/ul>\n\n<p>Jira ticket created with generic description, generated in a bundle with several tasks:<br>\n<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%2Fuploads%2Farticles%2Fhtz8jbj0cj7zrv4t1ruc.png\" class=\"article-body-image-wrapper\"><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%2Fuploads%2Farticles%2Fhtz8jbj0cj7zrv4t1ruc.png\" alt=\"Jira ticket created with generic description, generated in a bundle with several tasks\" width=\"800\" height=\"648\"><\/a><\/p>\n\n<p>A different task, but this with detailed description after asking to improve it with code, links and relevant info:<br>\n<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%2Fuploads%2Farticles%2Fmfy4my4kazl146at6xar.png\" class=\"article-body-image-wrapper\"><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%2Fuploads%2Farticles%2Fmfy4my4kazl146at6xar.png\" alt=\"A different task, but this with detailed description after asking to improve it with code, links and relevant info\" width=\"800\" height=\"755\"><\/a><\/p>\n<h3>\n  \n  \n  5. From Jira Ticket to Code, and Beyond with Automation\n<\/h3>\n\n<p><strong>TL;DR:<\/strong> For simple and well-scoped Jira tickets, you can ask the AI to generate the code just by providing the ticket link. This opens the door to automating entire parts of your development lifecycle.<\/p>\n\n<p>For well-defined, simple tasks, you can go from a Jira ticket link to functional code in minutes. This is where we can start thinking about true workflow automation.<\/p>\n\n<p>This works best for tasks like:<\/p>\n\n<ul>\n<li>  Adding a new optional field to an API endpoint.<\/li>\n<li>  Changing UI label text for clarity or rewording.\n<\/li>\n<li>  Adding missing translations.<\/li>\n<li>  Generating unit tests for a function or improving test coverage.<\/li>\n<\/ul>\n\n<p>\ud83d\udd16 The prompt is as simple as<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>Implement the changes described in this Jira ticket: [link].\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>The most important <strong>thing<\/strong> is to ensure a good description in the ticket.<\/p>\n\n<p>This leads to the next logical step: automation. For example, here is a powerful multi-action prompt that links code analysis, documentation, and task creation:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>Document the endpoints of the [Legacy Service Name] Node.js project for migration. \n\nFollow these steps to generate a **Confluence** page and a **Jira** task. \n\n1. **Confluence** Page Documentation: \nGenerate a new page at the URL: [link here] \nFor each endpoint in the [Legacy Service Name] project, create a new section with a descriptive title. Within each section, provide the following details: Route, Method, Parameters, Response Example, OpenAPI Link. \nAt the end of the page, create a sortable table with the following columns: Status, Method, Endpoint, and Link to openapi. Use the sortable table macro or a similar feature if available in **Confluence** to enable sorting. \n\n2. **Jira** Task Creation: \nCreate a new **Jira** task with the following specifications: \nParent Epic: [link here] \nTitle: Endpoints to migrate from [Legacy Service Name] \nDescription: Include a TLDR section with a brief summary of the task. After the TLDR, include the same table you generated for the **Confluence** page.\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>This is the next frontier: a truly automated, intelligent development environment.<\/p>\n\n<h3>\n  \n  \n  6. Setting Up Your AI Cockpit: MCP Atlassian Integration\n<\/h3>\n\n<p><strong>TL;DR:<\/strong> Install the specific Atlassian Marketplace Connect Program (MCP) agent\/plugin in your preferred editor (Cursor, IntelliJ, VS Code) and authenticate with your Atlassian workspace.<\/p>\n\n<p>As always, there are requirements, some steps to follow depending on the IDE you use, etc. Here you'll see the instructions for all the IDEs. Let's see the example for Cursor<\/p>\n\n<h4>\n  \n  \n  A. Cursor\n<\/h4>\n\n<p>Since Cursor is built with AI capabilities from the start, the integration is often seamless. The Cursor agent uses <strong>Gemini 2.5 Pro<\/strong> as its underlying model for the deep reasoning and code generation tasks described. (you also can add it easily from <a href=\"https:\/\/cursor.com\/docs\/context\/mcp\/directory\" rel=\"noopener noreferrer\">the cursor mcp list<\/a>).<\/p>\n\n<ol>\n<li><p><strong>Open Settings:<\/strong> Navigate to <code>Settings<\/code> (usually <code>Cmd+,<\/code> or <code>Ctrl+,<\/code>).<\/p><\/li>\n<li><p><strong>Find Integrations:<\/strong> Look for \"MCP\".<\/p><\/li>\n<li><p><strong>Connect Atlassian:<\/strong> Select the Atlassian integration option (Jira\/Confluence) and follow the prompts to sign in with your Atlassian credentials. This connects the internal <code>atlassian-mcp-server<\/code> to your agent, granting it read access to your permitted documentation and ticketing.<\/p><\/li>\n<\/ol>\n\n<p>Yes, if you have troubles installing the mcp in your ide, you can ask to your favourite model in your IDE copy pasting the errors or explaining the problems! Or, of course, you can comment in this post and we'll try to help<\/p>\n\n<h3>\n  \n  \n  Conclusion\n<\/h3>\n\n<p>Integrating Atlassian's AI tools into your IDE isn't about replacing the developer. It's about augmenting our abilities by removing friction, having the developer focus <strong>on<\/strong> development, and avoid the <strong>satellite<\/strong> tasks that are repetitive and from what you learn little to nothing. By handling the overhead of documentation, task management, and context gathering, it allows us to dedicate our most valuable resource\u2014our focused brainpower\u2014to what we do best: building bold and <strong>ingenious<\/strong> software.<\/p>\n\n<p>What are your favorite AI-driven workflows? Share them in the comments below!<\/p>\n\n<p>AI created, <strong>Human-enhanced<\/strong>.<\/p>\n\n<p>Daniel Artola.<\/p>\n\n","category":["mcp","productivity","ai","cursor"]},{"title":"How to work with redux on module federation","pubDate":"Thu, 17 Feb 2022 15:14:17 +0000","link":"https:\/\/dev.to\/manomano-tech-team\/how-to-work-with-redux-on-module-federation-13gm","guid":"https:\/\/dev.to\/manomano-tech-team\/how-to-work-with-redux-on-module-federation-13gm","description":"<p>Module Federation is a plugin from Webpack 5 that helps to orchestrate micro-frontend architecture, thereby making it easier for organizational teams to decouple and share applications.<\/p>\n\n<p>If you have a big monolith on frontend with react, you probably have redux to share info between components. What happens if we have to move some of those components to a module?<\/p>\n\n<p><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%2Fuploads%2Farticles%2F1sod1o3008dazl1igbqy.png\" class=\"article-body-image-wrapper\"><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%2Fuploads%2Farticles%2F1sod1o3008dazl1igbqy.png\" alt=\"Redux on module federation\" width=\"800\" height=\"256\"><\/a><\/p>\n\n<h2>\n  \n  \n  Introduction\n<\/h2>\n\n<p>This post discusses how to share info between modules via redux, so we\u2019ll focus on how modules and base will work with the store, reducers, and actions. Also, the following sections will explain how modules can work with redux as isolated applications or a federated module with minimal code changes.<\/p>\n\n<p><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%2Fuploads%2Farticles%2F3a7dm28hm0et3yz2tzz7.png\" class=\"article-body-image-wrapper\"><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%2Fuploads%2Farticles%2F3a7dm28hm0et3yz2tzz7.png\" alt=\"Basic architecture schema\" width=\"697\" height=\"588\"><\/a><\/p>\n\n<h2>\n  \n  \n  Index\n<\/h2>\n\n<ol>\n<li>Basic app architecture<\/li>\n<li>Store for base and modules<\/li>\n<li>Trigger an action from module<\/li>\n<li>Inject reducer from module to base<\/li>\n<li>\nConclusions <\/li>\n<\/ol>\n\n<h2>\n  \n  \n  Basic app architecture\n<\/h2>\n\n<p>In general, this approach consist of:<\/p>\n\n<ol>\n<li>A NextJS project as a host\/base project.<\/li>\n<li>Federated modules for some features, such as Header, Homepage, Cart, and ProductPage.<\/li>\n<\/ol>\n\n<p>Keep in mind that for this example we\u2019ll be working with an NextJS base (React, Redux, etc) and a module with the same technologies (which exports only one component).<\/p>\n\n<p>Our module will consist of a web app with react and redux, without NextJS, but the main point here will be only how to interact with redux.<\/p>\n\n<h2>\n  \n  \n  Store for base and modules\n<\/h2>\n\n<p>The idea here is that all modules should work as an isolated app and as a federated module. Furthermore, we know redux actions can only be triggered under a redux provider.<\/p>\n\n<p>Therefore, the strategy is to place a provider on sub apps, but export only the components under this provider to inject the module on our base.<\/p>\n\n<p><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%2Fuploads%2Farticles%2Fw7lkvqzt8agz2lilxnp8.png\" class=\"article-body-image-wrapper\"><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%2Fuploads%2Farticles%2Fw7lkvqzt8agz2lilxnp8.png\" alt=\"export only component under provider\" width=\"621\" height=\"248\"><\/a><\/p>\n\n<p>The idea is to always have a redux provider, but avoid having two providers when a module is injected.<\/p>\n\n<p>With this we\u2019ll be able to trigger actions from modules, so no matter if is working as isolated or module.<\/p>\n\n<p><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%2Fuploads%2Farticles%2Flix2p1qz194wkoc4axy2.png\" class=\"article-body-image-wrapper\"><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%2Fuploads%2Farticles%2Flix2p1qz194wkoc4axy2.png\" alt=\"Fig. 3: shared store when component is injected\" width=\"697\" height=\"588\"><\/a><\/p>\n\n<h2>\n  \n  \n  Trigger an action from module\n<\/h2>\n\n<p>To trigger an action on Module, we can do it exactly as we do it on any react-redux app. There are only few things to keep in mind:<\/p>\n\n<ol>\n<li>When we export a component to work as a module, we are exporting the component and not the reducers, so if we want to have same reducers on base and module, we need to inject it. \nAs que saw in the last image, the exported component will have different provider depending how we work with it.<\/li>\n<li>If we need a reducer and we don\u2019t want to inject it because we have it from the beginning on the app, we have to search a way to export the reducer from module an include it on base to add it to the store when necessary, avoiding to have the same duplicated reducer on base and module.<\/li>\n<li>We don\u2019t need to change anything to trigger an action, but the available reducers will depend on where the component is mounted. If it's working isolated, we\u2019ll need to mock or recreate the reducers that we need from base. If is working as module on base, we\u2019ll need to add the reducers that we have on sub-app.<\/li>\n<li>For testing you'll have to import or mock as in the point 3<\/li>\n<\/ol>\n\n<h2>\n  \n  \n  Inject reducer from module to base\n<\/h2>\n\n<p>If you're working on big react app with redux, you probably faced the reducer injection:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight plaintext\"><code>const injectAsyncReducer = (store, name, asyncReducer) =&gt; {\n  store.asyncReducers[name] = asyncReducer;\n  store.replaceReducer(createReducer(store.asyncReducers));\n}\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>You probably wrote a function similar to this, and that code will work perfectly for both of the cases that we're managing here: working isolated and as a module. Why? because in the two cases we'll have a redux provider, the only difference will be that each case will inject the reducer in a different store, but you'll have nothing to deal with. <\/p>\n\n<p>NOTE: Yes, you can inject redux-sagas as well, if you're curious about I drop this <a href=\"https:\/\/scite.ai\/blog\/2021-11-10_Redux-store-splitting-bundle-optimization\" rel=\"noopener noreferrer\">line here<\/a><\/p>\n\n<h2>\n  \n  \n  Conclusions\n<\/h2>\n\n<p>If you were worried about split your monolith in federated modules due to redux, there's nothing to be afraid. The most complicated part probably will be the Webpack configuration and not the redux. <\/p>\n\n<p>Thank to <a href=\"https:\/\/www.instagram.com\/english.with.yama\/\" rel=\"noopener noreferrer\">english.with.yama@gmail.com<\/a> for proofreading the article.<\/p>\n\n"},{"title":"How to add custom types to a javascript library","pubDate":"Mon, 06 Dec 2021 16:12:52 +0000","link":"https:\/\/dev.to\/manomano-tech-team\/how-to-add-custom-types-to-a-javascript-library-2dag","guid":"https:\/\/dev.to\/manomano-tech-team\/how-to-add-custom-types-to-a-javascript-library-2dag","description":"<p>Few weeks ago, I started contributing to an open source library called <a href=\"https:\/\/github.com\/teafuljs\/teaful\" rel=\"noopener noreferrer\">Teaful<\/a>, a Tiny, EAsy, and powerFUL for React state management, with ambitious roadmap. Now <code>Teaful<\/code> reached more than <strong>500 GitHub \u2b50\ufe0f Stars<\/strong>, the library and his community are growing fast.<\/p>\n\n<p>That means issues and pull requests are growing as well, and soon we realized that we need to improve dev-experience and provide <a href=\"https:\/\/dev.to\/aralroca\/teaful-devtools-released-37lp\">tools<\/a> for that reason.<\/p>\n\n<p>Bear this in mind, implement custom types to allow all the benefits from <code>TypeScript<\/code> at <code>Teaful<\/code> is a big step on that way.<\/p>\n\n<p>(Yes, I know, migrate a library to pure ts probably is a better solution, and it's on our <a href=\"https:\/\/github.com\/teafuljs\/teaful#roadmap-\" rel=\"noopener noreferrer\">roadmap<\/a> before 1.0.0)<\/p>\n\n<p><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%2Fuploads%2Farticles%2Frxj91tgnh9as4jn3ualx.gif\" class=\"article-body-image-wrapper\"><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%2Fuploads%2Farticles%2Frxj91tgnh9as4jn3ualx.gif\" alt=\"Autocomplete code gif\" width=\"813\" height=\"508\"><\/a><\/p>\n\n<h2>\n  \n  \n  Index\n<\/h2>\n\n<ol>\n<li>Avoiding auto generated types<\/li>\n<li>Where to place custom types<\/li>\n<li>Create custom types<\/li>\n<li>Conclusions<\/li>\n<\/ol>\n\n<h2>\n  \n  \n  Avoiding auto generated types\n<\/h2>\n\n<p>In our case, an auto-generated custom type full of <code>any<\/code> was useless. So, we started implementing custom types.<\/p>\n\n<p>We're using <code>microbundle<\/code>, they provide a flag to avoid auto-generate types, <code>--no-generateTypes<\/code>. Microbundle, according to docs, generally respect your TypeScript config at <code>tsconfig.json<\/code> (you can read more about <a href=\"https:\/\/github.com\/developit\/microbundle#using-with-typescript\" rel=\"noopener noreferrer\">here<\/a>), but at this moment we don't need a specific configuration for <code>TypeScript<\/code><\/p>\n\n<p>Then we can inform on <code>package.json<\/code> where are our custom types with <code>\"types\": \"folder\/index.d.ts\"<\/code>.<\/p>\n\n<h2>\n  \n  \n  Where to place custom types\n<\/h2>\n\n<p>Create a file with extension <code>.d.ts<\/code> , generally you'll put this file on <code>dist<\/code> folder. Now here you can add your custom types.<\/p>\n\n<h2>\n  \n  \n  Create custom types\n<\/h2>\n\n<p>Here  I'm going to explain how we created custom types specifics for <code>Teaful<\/code> and why some decisions were taken, if you're reading this to know how to add custom types to your js library and already know about <code>TypeScript<\/code>, feel free to skip this section.<\/p>\n\n<h3>\n  \n  \n  InitialStore\n<\/h3>\n\n<p>The <code>store<\/code> is where <code>Teaful<\/code> saves data, is a key-value object (you can have more than one store). Easy to type:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code><span class=\"nx\">type<\/span> <span class=\"nx\">initialStoreType<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">Record<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">string<\/span><span class=\"p\">,<\/span> <span class=\"nx\">any<\/span><span class=\"o\">&gt;<\/span><span class=\"p\">;<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>So far so good, nothing strange here. We want to store anything, and all keys will be string.<\/p>\n\n<p>Then things become more complicated. In this article only things about creating types will be explained, so if you want to know more about how to implement <code>Teaful<\/code> I strongly recommend visit the <a href=\"https:\/\/github.com\/teafuljs\/teaful#teaful\" rel=\"noopener noreferrer\">README at github<\/a>. <\/p>\n\n<h3>\n  \n  \n  Hook Return\n<\/h3>\n\n<p>To create a new value on store is pretty similar to <code>useState<\/code> from <code>React<\/code>. Let's see an example:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code><span class=\"kd\">const<\/span> <span class=\"p\">[<\/span><span class=\"nx\">username<\/span><span class=\"p\">,<\/span> <span class=\"nx\">setUsername<\/span><span class=\"p\">]<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">useStore<\/span><span class=\"p\">.<\/span><span class=\"nf\">username<\/span><span class=\"p\">();<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>Easy right? Ok, so what have we here? <code>useStore<\/code> returns an array of two elements (Yes! Like useState!), the element in the store and the function to update it. <\/p>\n\n<p>The type we need:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code><span class=\"nx\">type<\/span> <span class=\"nx\">HookReturn<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">T<\/span><span class=\"o\">&gt;<\/span> <span class=\"o\">=<\/span> <span class=\"p\">[<\/span><span class=\"nx\">T<\/span><span class=\"p\">,<\/span> <span class=\"p\">(<\/span><span class=\"nx\">value<\/span><span class=\"p\">:<\/span> <span class=\"nx\">T<\/span> <span class=\"o\">|<\/span> <span class=\"p\">((<\/span><span class=\"nx\">value<\/span><span class=\"p\">:<\/span> <span class=\"nx\">T<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"nx\">T<\/span> <span class=\"o\">|<\/span> <span class=\"kc\">undefined<\/span> <span class=\"o\">|<\/span> <span class=\"kc\">null<\/span><span class=\"p\">)<\/span> <span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"k\">void<\/span><span class=\"p\">];<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>If you're not familiar with TS, this could look a little cryptic. We're creating a new type called <code>HookReturn<\/code> which gets a generic type we called '<code>T<\/code>' (from Type, but you can use any name). <\/p>\n\n<p>This type is a tuple(a data structure that is an ordered list of elements with a fixed length, because we aren't going to add more elements for the return of our <code>useStore<\/code>), where first element is <code>T<\/code>, because we want to return a value with specific type that we don't know at the moment of creating the type, but we want to ensure, for example, that the setter function (the second element on this tuple) will get the same type we are using for the first element as param.<\/p>\n\n<p>Then, let's pay attention on the second element of our tuple.<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code><span class=\"p\">(<\/span><span class=\"nx\">value<\/span><span class=\"p\">:<\/span> <span class=\"nx\">T<\/span> <span class=\"o\">|<\/span> <span class=\"p\">((<\/span><span class=\"nx\">value<\/span><span class=\"p\">:<\/span> <span class=\"nx\">T<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"nx\">T<\/span> <span class=\"o\">|<\/span> <span class=\"kc\">undefined<\/span> <span class=\"o\">|<\/span> <span class=\"kc\">null<\/span><span class=\"p\">)<\/span> <span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"k\">void<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>Here, our type is a function that returns nothing ( <code>() =&gt; void<\/code>), but accepts one param (<code>value: T | ((value: T) =&gt; T | undefined | null)<\/code>), and this param could be a value of type <code>T<\/code>, or a function that get a value of type <code>T<\/code> and returns <code>null<\/code>, <code>undefined<\/code> or a value of type <code>T<\/code> (<code>(value: T) =&gt; T | undefined | null<\/code>). <\/p>\n\n<p>What this means? what are we allowing here with this type? Ok, let's imagine a counter:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code><span class=\"kd\">const<\/span> <span class=\"p\">[<\/span><span class=\"nx\">counter<\/span><span class=\"p\">,<\/span> <span class=\"nx\">setCounter<\/span><span class=\"p\">]<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">useStore<\/span><span class=\"p\">.<\/span><span class=\"nf\">counter<\/span><span class=\"p\">();<\/span>\n\n<span class=\"c1\">\/\/allowed by T<\/span>\n<span class=\"nf\">setCounter<\/span><span class=\"p\">(<\/span><span class=\"nx\">counter<\/span><span class=\"o\">+<\/span><span class=\"mi\">1<\/span><span class=\"p\">);<\/span>\n<span class=\"c1\">\/\/allowed by  ((value: T) =&gt; T | undefined | null)<\/span>\n<span class=\"nf\">setCounter<\/span><span class=\"p\">((<\/span><span class=\"nx\">counter<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"nx\">counter<\/span><span class=\"o\">*<\/span><span class=\"mi\">2<\/span><span class=\"p\">))<\/span>\n<span class=\"nf\">setCounter<\/span><span class=\"p\">((<\/span><span class=\"nx\">counter<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"kc\">undefined<\/span><span class=\"p\">)<\/span>\n<span class=\"nf\">setCounter<\/span><span class=\"p\">((<\/span><span class=\"nx\">counter<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"kc\">null<\/span><span class=\"p\">)<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>Yes, Teaful accepts a function as param on the setter function.<\/p>\n\n<h3>\n  \n  \n  Hook type\n<\/h3>\n\n<p>When you create\/call a new property with <a href=\"https:\/\/github.com\/teafuljs\/teaful#usestore-hook\" rel=\"noopener noreferrer\">useStore<\/a>, you call <code>useStore.[newProperty]()<\/code>. This accepts two optional params, first for <code>initialValue<\/code>, and the second one is for <code>updateValue<\/code> (a function to update the store property indicated with the <code>proxy<\/code>). The hook looks easy to create here:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code><span class=\"nx\">type<\/span> <span class=\"nx\">Hook<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">S<\/span><span class=\"o\">&gt;<\/span> <span class=\"o\">=<\/span> <span class=\"p\">(<\/span>\n    <span class=\"nx\">initial<\/span><span class=\"p\">?:<\/span> <span class=\"nx\">S<\/span><span class=\"p\">,<\/span>\n    <span class=\"nx\">onAfterUpdate<\/span><span class=\"p\">?:<\/span> <span class=\"nx\">afterCallbackType<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">S<\/span><span class=\"o\">&gt;<\/span>\n<span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"nx\">HookReturn<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">S<\/span><span class=\"o\">&gt;<\/span><span class=\"p\">;<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>Both optional, but the second one is a specific function. Type <code>onAfterUpdate<\/code>, is a function with two params: <code>store<\/code> before and after the changes, both will be same type, extending our <code>initialStore<\/code> type.<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code><span class=\"nx\">type<\/span> <span class=\"nx\">afterCallbackType<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">S<\/span> <span class=\"kd\">extends<\/span> <span class=\"nx\">initialStoreType<\/span><span class=\"o\">&gt;<\/span> <span class=\"o\">=<\/span> <span class=\"p\">(<\/span>\n    <span class=\"nx\">param<\/span><span class=\"p\">:<\/span> <span class=\"p\">{<\/span> <span class=\"nl\">store<\/span><span class=\"p\">:<\/span> <span class=\"nx\">S<\/span><span class=\"p\">;<\/span> <span class=\"nl\">prevStore<\/span><span class=\"p\">:<\/span> <span class=\"nx\">S<\/span><span class=\"p\">;<\/span> <span class=\"p\">}<\/span>\n<span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"k\">void<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>Finally, our type <code>Hook<\/code> will return a tuple <code>[property,setter]<\/code>, so indeed, we're going to return our custom type <code>HookReturn<\/code> with our generic type. If we create a number, have sense to take care about number type in all places, for the initial value, the returned tuple... etc.<\/p>\n\n<h3>\n  \n  \n  Hoc type\n<\/h3>\n\n<p>Teaful allows to use it as Hoc (as connect on Redux, code explain it by itself):<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code><span class=\"kd\">const<\/span> <span class=\"p\">{<\/span> <span class=\"nx\">withStore<\/span> <span class=\"p\">}<\/span> <span class=\"o\">=<\/span> <span class=\"nf\">createStore<\/span><span class=\"p\">({<\/span> <span class=\"na\">count<\/span><span class=\"p\">:<\/span> <span class=\"mi\">0<\/span> <span class=\"p\">});<\/span>\n<span class=\"kd\">class<\/span> <span class=\"nc\">Counter<\/span> <span class=\"kd\">extends<\/span> <span class=\"nc\">Component<\/span> <span class=\"p\">{<\/span>\n  <span class=\"nf\">render<\/span><span class=\"p\">()<\/span> <span class=\"p\">{<\/span>\n  <span class=\"kd\">const<\/span> <span class=\"p\">[<\/span><span class=\"nx\">store<\/span><span class=\"p\">,<\/span> <span class=\"nx\">setStore<\/span><span class=\"p\">]<\/span> <span class=\"o\">=<\/span> <span class=\"k\">this<\/span><span class=\"p\">.<\/span><span class=\"nx\">props<\/span><span class=\"p\">.<\/span><span class=\"nx\">store<\/span><span class=\"p\">;<\/span>\n    <span class=\"k\">return <\/span><span class=\"p\">(<\/span>\n      <span class=\"c1\">\/\/ [...]<\/span>\n    <span class=\"p\">);<\/span>\n  <span class=\"p\">}<\/span>\n<span class=\"p\">}<\/span>\n\n<span class=\"c1\">\/\/ Similar to useStore()<\/span>\n<span class=\"kd\">const<\/span> <span class=\"nx\">CounterWithStore<\/span> <span class=\"o\">=<\/span> <span class=\"nf\">withStore<\/span><span class=\"p\">(<\/span><span class=\"nx\">Counter<\/span><span class=\"p\">);<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>The HOC <a href=\"https:\/\/github.com\/teafuljs\/teaful#withstore-hoc\" rel=\"noopener noreferrer\"><code>withStore<\/code><\/a> wraps a <code>Component<\/code> and returns the component with a prop called store. A second parameter for initial value is allowed, and a third one for <code>onAfterUpdate<\/code> callback.<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code><span class=\"nx\">type<\/span> <span class=\"nx\">HocFunc<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">S<\/span><span class=\"p\">,<\/span> <span class=\"nx\">R<\/span> <span class=\"kd\">extends<\/span> <span class=\"nx\">React<\/span><span class=\"p\">.<\/span><span class=\"nx\">ComponentClass<\/span> <span class=\"o\">=<\/span> <span class=\"nx\">React<\/span><span class=\"p\">.<\/span><span class=\"nx\">ComponentClass<\/span><span class=\"o\">&gt;<\/span> <span class=\"o\">=<\/span> <span class=\"p\">(<\/span>\n       <span class=\"nx\">component<\/span><span class=\"p\">:<\/span> <span class=\"nx\">R<\/span><span class=\"p\">,<\/span>\n       <span class=\"nx\">initial<\/span><span class=\"p\">?:<\/span> <span class=\"nx\">S<\/span><span class=\"p\">,<\/span>\n       <span class=\"nx\">onAfterUpdate<\/span><span class=\"p\">?:<\/span> <span class=\"nx\">afterCallbackType<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">S<\/span><span class=\"o\">&gt;<\/span>\n<span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"nx\">R<\/span><span class=\"p\">;<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>We need two generic types, one for initial value and <code>onAfterUpdate<\/code> (both will use same generic, but <code>onAfterUpdate<\/code> will have a specific type, explained later) and the other one for <code>React<\/code> component to wrap that would be the same for the return, because we want the same component but with a new prop called store.<\/p>\n\n<p>Look at the <code>R<\/code> type, is extending <code>React.ComponentClass<\/code> (type provided by <code>React<\/code>). This means that we are taking profit from that type and including it in our generic type called <code>R<\/code>. <\/p>\n\n<p>Why extending component class only and not functional component? <\/p>\n\n<p>Well, we didn't found a single situation when we wanted to wrap any component that doesn't extend Class with a HOC to get the store.<\/p>\n\n<p>Ok, third type: <code>onAfterUpdate<\/code>.  Here we need a function with two params store before and after the changes, both will be same type, extending our <code>initialStore<\/code> type. Same as first hook, we reuse same type for all callbacks params<\/p>\n\n<p>Now we only have to export the a type to use<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code>  <span class=\"k\">export<\/span> <span class=\"nx\">type<\/span> <span class=\"nx\">Hoc<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">S<\/span><span class=\"o\">&gt;<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span> <span class=\"na\">store<\/span><span class=\"p\">:<\/span> <span class=\"nx\">HookReturn<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">S<\/span><span class=\"o\">&gt;<\/span> <span class=\"p\">};<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<h3>\n  \n  \n  HookDry type\n<\/h3>\n\n<p><code>Teaful<\/code> provides a helper called <a href=\"https:\/\/github.com\/teafuljs\/teaful#getstore-helper\" rel=\"noopener noreferrer\"><code>getStore<\/code><\/a>, like useStore but:<\/p>\n\n<ul>\n<li>It does not make a subscription. So it is no longer a hook and you can use it as a helper wherever you want.<\/li>\n<li>It's not possible to register events that are executed after a change.<\/li>\n<\/ul>\n\n<p>This means we don't want same as <code>useStore<\/code>type, we return the same but we want to ensure we don't accept a second param as callback. Let's create another one:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code>  <span class=\"nx\">type<\/span> <span class=\"nx\">HookDry<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">S<\/span><span class=\"o\">&gt;<\/span> <span class=\"o\">=<\/span> <span class=\"p\">(<\/span><span class=\"nx\">initial<\/span><span class=\"p\">?:<\/span> <span class=\"nx\">S<\/span><span class=\"p\">)<\/span> <span class=\"o\">=&gt;<\/span> <span class=\"nx\">HookReturn<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">S<\/span><span class=\"o\">&gt;<\/span><span class=\"p\">;<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>The return is clear, same as Hook.<\/p>\n\n<h3>\n  \n  \n  Let's type useStore, getStore and withStore\n<\/h3>\n\n<p>Ok, now we have almost all the work done. A custom type is needed for each tool, <code>useStore<\/code>, <code>getStore<\/code>and <code>withStore<\/code>:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code>  <span class=\"nx\">type<\/span> <span class=\"nx\">getStoreType<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">S<\/span> <span class=\"kd\">extends<\/span> <span class=\"nx\">initialStoreType<\/span><span class=\"o\">&gt;<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span>\n    <span class=\"p\">[<\/span><span class=\"nx\">key<\/span> <span class=\"k\">in<\/span> <span class=\"nx\">keyof<\/span> <span class=\"nx\">S<\/span><span class=\"p\">]:<\/span> <span class=\"nx\">S<\/span><span class=\"p\">[<\/span><span class=\"nx\">key<\/span><span class=\"p\">]<\/span> <span class=\"kd\">extends<\/span> <span class=\"nx\">initialStoreType<\/span>\n      <span class=\"p\">?<\/span> <span class=\"nx\">useStoreType<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">S<\/span><span class=\"p\">[<\/span><span class=\"nx\">key<\/span><span class=\"p\">]<\/span><span class=\"o\">&gt;<\/span> <span class=\"o\">&amp;<\/span> <span class=\"nx\">HookDry<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">S<\/span><span class=\"p\">[<\/span><span class=\"nx\">key<\/span><span class=\"p\">]<\/span><span class=\"o\">&gt;<\/span> <span class=\"p\">:<\/span> <span class=\"nx\">HookDry<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">S<\/span><span class=\"p\">[<\/span><span class=\"nx\">key<\/span><span class=\"p\">]<\/span><span class=\"o\">&gt;<\/span><span class=\"p\">;<\/span>\n  <span class=\"p\">};<\/span>\n\n  <span class=\"nx\">type<\/span> <span class=\"nx\">useStoreType<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">S<\/span> <span class=\"kd\">extends<\/span> <span class=\"nx\">initialStoreType<\/span><span class=\"o\">&gt;<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span>\n    <span class=\"p\">[<\/span><span class=\"nx\">key<\/span> <span class=\"k\">in<\/span> <span class=\"nx\">keyof<\/span> <span class=\"nx\">S<\/span><span class=\"p\">]:<\/span> <span class=\"nx\">S<\/span><span class=\"p\">[<\/span><span class=\"nx\">key<\/span><span class=\"p\">]<\/span> <span class=\"kd\">extends<\/span> <span class=\"nx\">initialStoreType<\/span>\n      <span class=\"p\">?<\/span> <span class=\"nx\">useStoreType<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">S<\/span><span class=\"p\">[<\/span><span class=\"nx\">key<\/span><span class=\"p\">]<\/span><span class=\"o\">&gt;<\/span> <span class=\"o\">&amp;<\/span> <span class=\"nx\">Hook<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">S<\/span><span class=\"p\">[<\/span><span class=\"nx\">key<\/span><span class=\"p\">]<\/span><span class=\"o\">&gt;<\/span> <span class=\"p\">:<\/span> <span class=\"nx\">Hook<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">S<\/span><span class=\"p\">[<\/span><span class=\"nx\">key<\/span><span class=\"p\">]<\/span><span class=\"o\">&gt;<\/span><span class=\"p\">;<\/span>\n  <span class=\"p\">};<\/span>\n\n  <span class=\"nx\">type<\/span> <span class=\"nx\">withStoreType<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">S<\/span> <span class=\"kd\">extends<\/span> <span class=\"nx\">initialStoreType<\/span><span class=\"o\">&gt;<\/span> <span class=\"o\">=<\/span> <span class=\"p\">{<\/span>\n    <span class=\"p\">[<\/span><span class=\"nx\">key<\/span> <span class=\"k\">in<\/span> <span class=\"nx\">keyof<\/span> <span class=\"nx\">S<\/span><span class=\"p\">]:<\/span> <span class=\"nx\">S<\/span><span class=\"p\">[<\/span><span class=\"nx\">key<\/span><span class=\"p\">]<\/span> <span class=\"kd\">extends<\/span> <span class=\"nx\">initialStoreType<\/span>\n      <span class=\"p\">?<\/span> <span class=\"nx\">withStoreType<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">S<\/span><span class=\"p\">[<\/span><span class=\"nx\">key<\/span><span class=\"p\">]<\/span><span class=\"o\">&gt;<\/span> <span class=\"o\">&amp;<\/span> <span class=\"nx\">HocFunc<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">S<\/span><span class=\"o\">&gt;<\/span>\n      <span class=\"p\">:<\/span> <span class=\"nx\">HocFunc<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">S<\/span><span class=\"o\">&gt;<\/span><span class=\"p\">;<\/span>\n  <span class=\"p\">};<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<p>The <a href=\"https:\/\/www.typescriptlang.org\/docs\/handbook\/2\/keyof-types.html\" rel=\"noopener noreferrer\"><code>keyOf<\/code><\/a> type operator ensures that our <code>property<\/code> will exist on <code>store<\/code>.<\/p>\n\n<p>The ternary here looks weird if you're not familiar with <code>Typescript<\/code>, is used for <a href=\"https:\/\/www.typescriptlang.org\/docs\/handbook\/2\/conditional-types.html\" rel=\"noopener noreferrer\">conditional-types<\/a>. The logic shared in three types is, get a generic type (<code>S<\/code>, that extends our <code>initialStoreType<\/code>), then get a <code>key<\/code>that must be on <code>S<\/code> (the property should exists on our store). <\/p>\n\n<p>Finally, this <code>withStoreType&lt;S[key]&gt; &amp; HocFunc&lt;S&gt;<\/code> is a <a href=\"https:\/\/www.typescriptlang.org\/docs\/handbook\/unions-and-intersections.html#intersection-types\" rel=\"noopener noreferrer\">Intersection type<\/a>. According to TypeScript documentation \"An intersection type combines multiple types into one\". So if <code>S[key]<\/code> extends <code>initialStore<\/code>, we set the intersection type, if not, the hook\/hoc type only.<\/p>\n\n<h3>\n  \n  \n  createStore\n<\/h3>\n\n<p>Last, the function to export from <code>Teaful<\/code>, the masterpiece:<br>\n<\/p>\n\n<div class=\"highlight js-code-highlight\">\n<pre class=\"highlight javascript\"><code><span class=\"kd\">function<\/span> <span class=\"nf\">createStore<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">S<\/span> <span class=\"kd\">extends<\/span> <span class=\"nx\">initialStoreType<\/span><span class=\"o\">&gt;<\/span><span class=\"p\">(<\/span>\n    <span class=\"nx\">initial<\/span><span class=\"p\">?:<\/span> <span class=\"nx\">S<\/span><span class=\"p\">,<\/span>\n    <span class=\"nx\">afterCallback<\/span><span class=\"p\">?:<\/span> <span class=\"nx\">afterCallbackType<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">S<\/span><span class=\"o\">&gt;<\/span>\n  <span class=\"p\">):<\/span> <span class=\"p\">{<\/span>\n    <span class=\"nl\">getStore<\/span><span class=\"p\">:<\/span> <span class=\"nx\">HookDry<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">S<\/span><span class=\"o\">&gt;<\/span> <span class=\"o\">&amp;<\/span> <span class=\"nx\">getStoreType<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">S<\/span><span class=\"o\">&gt;<\/span><span class=\"p\">;<\/span>\n    <span class=\"nl\">useStore<\/span><span class=\"p\">:<\/span> <span class=\"nx\">Hook<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">S<\/span><span class=\"o\">&gt;<\/span> <span class=\"o\">&amp;<\/span> <span class=\"nx\">useStoreType<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">S<\/span><span class=\"o\">&gt;<\/span><span class=\"p\">;<\/span>\n    <span class=\"nl\">withStore<\/span><span class=\"p\">:<\/span> <span class=\"nx\">HocFunc<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">S<\/span><span class=\"o\">&gt;<\/span> <span class=\"o\">&amp;<\/span> <span class=\"nx\">withStoreType<\/span><span class=\"o\">&lt;<\/span><span class=\"nx\">S<\/span><span class=\"o\">&gt;<\/span><span class=\"p\">;<\/span>\n  <span class=\"p\">};<\/span>\n<\/code><\/pre>\n\n<\/div>\n\n\n\n<h2>\n  \n  \n  Conclusions\n<\/h2>\n\n<p>That's definitely not everything, but there are few steps that you'll face:<\/p>\n\n<ol>\n<li>Check how to stop auto-generated types, check if types are generated by the bundler like our case, by <code>tsconfig.json<\/code> or whatever.<\/li>\n<li>Create a custom types on a <code>d.ts<\/code> file.<\/li>\n<li>Indicate to <code>package.json<\/code> the place of that file with property <code>\"types\"<\/code>.<\/li>\n<\/ol>\n\n<p>Adding custom types to a javascript library could be difficult at the beginning, but will improve the dev-experience from your users. <\/p>\n\n<p>And most important, this could be a great opportunity to learn and improve your skills, to start networking with the community or a good way to help other devs.<\/p>\n\n<p>I hope it was helpful to you, have a super nice day!<\/p>\n\n<p>Thank to <a href=\"https:\/\/www.instagram.com\/english.with.yama\/\" rel=\"noopener noreferrer\">english.with.yama@gmail.com<\/a> for proofreading the article.<\/p>\n\n","category":["javascript","typescript","teaful","react"]}]}}