How can I transform this statement into a higher-order function that offers a resource instead of using an object for initialization and destruction?

Starting with this code snippet: convert utilizes svgInjector to start and terminate a resource.

export async function convert(
  serializedSvg: string,
  svgSourceId: string,
  containerId: string
): Promise<string> {
  const svgInjector = new SvgInjector(serializedSvg, containerId).inject();
  if (!svgInjector.injectedElement) {
    throw new Error("Svg not injected");
  }

  const doc = new TargetDocument({});
  const xml = convertRecursively(
    svgInjector.injectedElement,
    doc,
    {
      svgSourceId,
    }
  );

  svgInjector.remove();

  return doc.saveXML();
}

Is there a way to refactor this to have a higher order function handle the initiation, provision, and destruction of the resource svgInjector.injectedElement for the convert function?

UPDATE:

Below is a simple reproducible demo:

var svg = '<svg xmlns="http://www.w3.org/2000/svg"><text x="20" y="20">I am made available in DOM</text></svg>'

function convert(
  serializedSvg,
  containerId
) {
  // make resource accessible (cross-cutting concern)
  var container = document.getElementById(containerId);
  var resource = new DOMParser().parseFromString(serializedSvg, "image/svg+xml").documentElement;
  container.appendChild(resource);

  // core conversion functionality manipulates the resource
  console.log(resource.getBBox())
  
  // clean up resource (cross-cutting concern)
  resource.remove()
}

convert(svg, "container")
<!DOCTYPE html>
<html>
<head>
  <title>Minimal</title>
</head>
<body>
<div id="container">
</div>
</body>
</html>

UPDATE 2

Presenting a TypeScript version based on the JavaScript from the previous update

var svg = '<svg xmlns="http://www.w3.org/2000/svg"><text x="20" y="20">I am made available in DOM</text></svg>'

function convert(
  serializedSvg: string,
  containerId: string
) {
  // make resource accessible (cross-cutting concern)
  var container = document.getElementById(containerId);
  if (!(container instanceof HTMLDivElement)) {
    throw new Error("Expected a div element")
  }
  var resource = new DOMParser().parseFromString(serializedSvg, "image/svg+xml").documentElement;
  if (!(resource instanceof SVGSVGElement)) {
    throw new Error("Expected an SVG element")
  }
  container.appendChild(resource);

  // core conversion functionality manipulates the resource
  console.log(resource.getBBox())

  // clean up resource (cross-cutting concern)
  resource.remove()
}

convert(svg, "container")

Answer №1

Considering your requirements, one approach could be to refactor the control flow in a way that the convert() function utilizes or receives a "resource manager" responsible for handling the resource's creation, initialization, and deletion tasks. The idea is to define a generic type ResourceManager as follows:

type ResourceManager<T, I> = <R>(initProps: I, cb: (resource: T) => R) => R;

This ResourceManager<T, I> acts as a blueprint allowing you to manage different types of resources using specific implementations tailored to each resource type. For instance, you can abstract out a separate

ResourceManager<SVGSVGElement, { serializedSvg: string, containerId: string }>
from the existing convert() function:

const svgManager: ResourceManager<SVGSVGElement, { serializedSvg: string, containerId: string }> =
  (initProps, cb) => {

    // Acquire necessary resources
    var container = document.getElementById(initProps.containerId);
    if (!(container instanceof HTMLDivElement)) {
      throw new Error("Expected a div element");
    }
    var resource = new DOMParser().parseFromString(initProps.serializedSvg, "image/svg+xml").documentElement;
    if (!(resource instanceof SVGSVGElement)) {
      throw new Error("Expected an SVG element")
    }
    container.appendChild(resource);

    // Delegate core functions to callback
    const ret = cb(resource);

    // Clean up after use
    resource.remove()

    // Return output from callback
    return ret;
  }

In this scheme, the actual logic or "core functionality" is deferred to the provided callback function, simplifying the convert() function as shown below:

function convert(
  serializedSvg: string,
  containerId: string
) {
  svgManager({ serializedSvg, containerId }, (resource => console.log(resource.getBBox())));
}

The snippet

resource => console.log(resource.getBBox())
depicts the operation performed on the acquired resource without concerning itself with resource management details.


Hopefully, this conceptual streamlining gives you some insights or suggestions. Best of luck with your endeavor!

Explore code in TypeScript Playground

Answer №2

After much effort, this is the best solution I have come up with so far. I am hopeful that someone cleverer will share a superior alternative.

As I analyze the solution provided below, I have pinpointed two weaknesses:

  • The enhancer's typing is not generic, limiting its reusability
  • The enhancer necessitates binding, obstructing enhancer composition
    type Props = {
      svg: SVGSVGElement;
      svgSourceId: string;
      containerId: string;
    };

    async function convertBase(props: Props): Promise<string> {
      const doc = new TargetDocument({});
      const xml = convertRecursively(props.svg, doc, {
        svgSourceId: props.svgSourceId,
      });

      return doc.saveXML();
    }

    type EnhancerProps = {
      serializedSvg: string;
      svgSourceId: string;
      containerId: string;
    };

    type EnhancerPropsLight = {
      svgSourceId: string;
      containerId: string;
    };

    function enhancer(fn: Function, props: EnhancerProps) {
      const rest = omit(["serializedSvg"])(props) as EnhancerPropsLight;
      const svgInjector = new SvgInjector(
        props.serializedSvg,
        props.containerId
      ).inject();
      if (!svgInjector.injectedElement) {
        throw new Error("Svg not injected");
      }

      const res = convertToTgmlBase({ ...rest, svg: svgInjector.injectedElement });

      svgInjector.remove();

      return res;
    }

    const convert = enhancer.bind(null, convertBase);
    export { convert };

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

After the jquery.show function is executed, an unexpected WebDriverException occurred, indicating an unknown error that prevents focusing

Here is my line of JavaScript code: $('#name').show(); And this is my webdriver code line: wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("name"))).sendKeys("Some Name"); When running the test, I encountered an exception: Web ...

What are the steps to send $http requests from AngularJS to a local server in an app?

Situation at Hand My goal is to perform an $http post request from Angular using the function below, defined in my controller: $scope.sendUserData = function(){ var userData = JSON.stringify({ 'firstName': $scope.firstName, ...

Having trouble with the dropdown multiselect feature in AngularJS?

I'm striving to develop a straightforward multi-select dropdown utilizing angular JS, bootstrap, and JS. This dropdown should display days (mon, tue...sun), with options for select all and unselect all. My goal is to create a controller that will de- ...

Tips for retrieving the tenth document in Firestore Query using JavaScript

Specifically, a selection of the arranged files. Here's my thought, although I know it can be improved: firestore().collection("queue").orderBy("order_id", "asc").limit(3,5) If anyone has a better solution, I would appre ...

Javascript Calculator with Dual Input Fields

I have been given a task to create a calculator by tomorrow using Javascript. Unfortunately, I am currently taking a distance course and Javascript has just been introduced in this assignment. While I am familiar with HTML and CSS, Javascript is something ...

Steps for choosing an option in MUI select list using jest

I am looking for a way to automate tests for a Material UI select component using Jest. Is there a way to select an option from the list of options in the Material UI component and confirm that its value has been successfully populated in Jest? I have se ...

Error: Unable to locate module - Invalid generator instance detected. The Asset Modules Plugin has been started with a generator object that does not adhere to the API standards

Encountering an issue in nextjs when utilizing the 'asset/inline' Asset Module Type within a custom webpack configuration while running yarn dev. I attempted to utilize the 'asset/inline' asset module type to output the URI of the impor ...

Nestjs crashes with "terminated" on an Amazon EC2 instance

https://i.stack.imgur.com/3GSft.jpgMy application abruptly terminates with the error message "killed" without providing any additional information or stack trace. Interestingly, it functions properly when run locally. Any assistance would be greatly appr ...

Is there a way for me to adjust the image dimensions so that it doesn't surpass the width of its parent container?

When working with images, it can be tricky to set the original width while also ensuring it fits within a parent container. For example, if the parent container has a width of 1000px, you may want the image to have a max-width of 100%, but not exceed 1000p ...

Using Generic Types in TypeScript Files for React Components

I have encountered an issue that I haven't been able to find a solution for online. When I define a function in a ts file like this: const lastGeneric = <T>(arr: Array<T>): T => { return arr[arr.length - 1]; } But when I try to do ...

issues arise post-transpilation with creating errors

In order to practice, I decided to create a basic TypeScript project. If it could be helpful, here is my ts.config: { "compilerOptions": { "target": "es2016", "module": "commonjs", "outDir": "./dist", "esModuleInterop": true, "forceC ...

Uploading Images to Imgur with Angular 4

As a newcomer to TypeScript, I am faced with the challenge of uploading an image to the Imgur API using Angular. Currently, my approach involves retrieving the file from a file picker using the following code: let eventObj: MSInputMethodContext = <MSIn ...

Utilizing JQuery and AJAX to upload unprocessed data to a WebAPI

I need help figuring out how to send raw data to a webAPI using JQuery and Ajax. I've been struggling to make the data get transmitted successfully. The API endpoint functions correctly in Postman: https://i.sstatic.net/AX8zL.png Here is my simple J ...

Customized Box with MaterialUI Styling

Currently, I am utilizing Material UI 5 for my project. To avoid repeatedly defining the same sx prop every time I create a box, I aim to build a custom box component that ensures all boxes have a consistent appearance. Upon going through the documentation ...

Tips for updating an element in an array by using another element from a separate array

What is the objective? We have two arrays: orders and NewOrders We need to check for any orders with the same order_id in both arrays. If there is a match, we then compare the order status. If the order from the NewOrders array has a different status, w ...

Angular-Translate fails to function within a tag's attribute

For my project, I utilize angular-translate. One of the key definitions looks like this: { "paging":{ "first":"First", "last":"Last", "next":"Next2", "pre":"Previous" } } I implement it in the following way: <uib-pagination first-tex ...

Guide on how to retrieve a list of objects from a controller and showcase them utilizing JQuery in a Spring MVC application with ajax functionality

Here is the controller code snippet: @Controller public class MainController { @Autowired SqlSession sqlSession; @Autowired Message messageVO; @RequestMapping(value="getMessages", method=RequestMethod.GET) public @ResponseBody List<Messag ...

The object for checking Ajax connections is not providing any values

I've been working on creating a unique AJAX connection check object, but unfortunately I'm struggling to get it to function properly. :/ Here's the object structure: function Connection(data){ this.isConnected = false, this.check = funct ...

Can you explain the variance between the (Record<string, unknown>) and object type?

Can you explain the distinction between type Record<string, unkown> and type object? Create a generic DeepReadonly<T> which ensures that every parameter of an object - and its nested objects recursively - is readonly. Here's the type I c ...

What sets apart extending and intersecting interfaces in TypeScript?

If we have the following type defined: interface Shape { color: string; } Now, let's explore two different methods to add extra properties to this type: Using Extension interface Square extends Shape { sideLength: number; } Using Intersection ...