What methods can be implemented to ensure ComponentOverride's universality?

These type definitions for markdown-to-jsx don't seem to be generic enough, causing issues like the one mentioned below. For more details, refer to Why is type SFC<AnchorProps> not assignable to type SFC<{}>?

/Users/sunknudsen/Sites/sunknudsen/sunknudsen-website/src/Test.tsx
TypeScript error in /Users/sunknudsen/Sites/sunknudsen/sunknudsen-website/src/Test.tsx(40,13):
No overload matches this call.
  Overload 1 of 2, '(props: Readonly<MarkdownProps>): Markdown', gave the following error.
    Type 'FunctionComponent<AnchorProps>' is not assignable to type 'string | SFC<{}> | ComponentClass<{}, any>'.
      Type 'FunctionComponent<AnchorProps>' is not assignable to type 'SFC<{}>'.
        Types of parameters 'props' and 'props' are incompatible.
          Type '{ children?: ReactNode; }' is not assignable to type 'PropsWithChildren<AnchorProps>'.
            Type '{ children?: ReactNode; }' is missing the following properties from type 'AnchorProps': baseUrl, relativeUrl, href
  Overload 2 of 2, '(props: MarkdownProps, context?: any): Markdown', gave the following error.
    Type 'FunctionComponent<AnchorProps>' is not assignable to type 'string | SFC<{}> | ComponentClass<{}, any>'.
      Type 'FunctionComponent<AnchorProps>' is not assignable to type 'SFC<{}>'.  TS2769

    38 |         overrides: {
    39 |           a: {
  > 40 |             component: Anchor,
       |             ^
    41 |             props: {
    42 |               baseUrl: "/privacy-guides",
    43 |               relativeUrl: "",

Is there a way to make the ComponentOverride more generic? Understanding these type definitions can be challenging. I see this as an opportunity for learning for myself and others. I've spent the whole day trying to unravel it.

// Type definitions for markdown-to-jsx 6.9
// Project: https://probablyup.github.io/markdown-to-jsx
// Definitions by: Elizabeth Craig <https://github.com/ecraig12345>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.8

import * as React from 'react';

export default class Markdown extends React.Component<MarkdownProps> { }

export interface MarkdownProps extends React.HTMLAttributes<HTMLElement> {
    options?: MarkdownOptions;
    // React.ReactNode contains both null and undefined
    // tslint:disable-next-line:no-null-undefined-union
    children?: React.ReactNode;
}

export type ComponentOverride = string | React.ComponentClass | React.SFC | {
    component: string | React.ComponentClass | React.SFC;
    props?: any;
};

export interface MarkdownOptions {
    /** Force all input strings to use block layout. */
    forceBlock?: boolean;

    /** Force all input strings to use inline layout. */
    forceInline?: boolean;

    /** Override representation of any HTML tag or custom component. */
    overrides?: {
        // As of 6.9.3, these tags are the only ones automatically generated by markdown-to-jsx.
        a?: ComponentOverride;
        br?: ComponentOverride;
        button?: ComponentOverride;
        code?: ComponentOverride;
        del?: ComponentOverride;
        div?: ComponentOverride;
        em?: ComponentOverride;
        footer?: ComponentOverride;
        input?: ComponentOverride;
        h1?: ComponentOverride;
        h2?: ComponentOverride;
        h3?: ComponentOverride;
        h4?: ComponentOverride;
        h5?: ComponentOverride;
        h6?: ComponentOverride;
        hr?: ComponentOverride;
        img?: ComponentOverride;
        ol?: ComponentOverride;
        p?: ComponentOverride;
        pre?: ComponentOverride;
        span?: ComponentOverride;
        strong?: ComponentOverride;
        sub?: ComponentOverride;
        sup?: ComponentOverride;
        table?: ComponentOverride;
        tbody?: ComponentOverride;
        td?: ComponentOverride;
        th?: ComponentOverride;
        thead?: ComponentOverride;
        tr?: ComponentOverride;
        ul?: ComponentOverride;
        /** In addition to HTML tags, you can specify a custom component name which can be used within markdown text. */
        [key: string]: ComponentOverride | undefined;
    };

    /** Custom React.createElement behavior. */
    createElement?: <P extends {}>(
        type: React.SFC<P> | React.ComponentClass<P> | string,
        // This typing is copied from React
        // tslint:disable-next-line:no-null-undefined-union
        props?: React.Attributes & P | null,
        // tslint:disable-next-line:no-null-undefined-union
        ...children: React.ReactNode[]) => React.ReactElement<P>;

    /** Custom function to generate an HTML id from headings. */
    slugify?: (text: string) => string;
}

export function compiler(markdown: string, options?: MarkdownOptions): JSX.Element;

Answer №1

Congratulations on delving into the world of TypeScript. Below is a versatile version of a ComponentOverride just for you.

export type ComponentOverride<TProps = any, TState = any> = 
  string | 
  React.ComponentClass<TProps, TState> | 
  React.SFC<TProps> | 
  {
    component: string | React.ComponentClass<TProps, TState> | React.SFC<TProps>;
    props?: TProps;
  };

You can implement it like this:

const Markdown = function() {
  const anchorOverride: ComponentOverride<AnchorProps> = {
    component: Anchor,
    props: {
      baseUrl: "/privacy-guides",
      relativeUrl: "",
      href: ""
    }
  };

  return (
    <MarkdownToJSX
      options={{
        overrides: {
          a: anchorOverride
        }
      }}
    ></MarkdownToJSX>
  );
};

Further reading: https://www.typescriptlang.org/docs/handbook/generics.html

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

Quick way to specify type for Observable in Typescript

Exploring Shortcut Declarations When working with TypeScript, I often take a shortcut when declaring object shapes. Instead of creating an interface first and then specifying that the object conforms to that type, I simply do: object: { fizz: boolean, buz ...

Tips for incorporating SectionList sections in React Native using an array

I am working with an array of objects named movies (const movies = movie[]). Each movie object includes properties like name, description, date and duration. movie: { name: string; description: string; date: Date; duration: number } My ...

What is the process for clearing cache in inversifyJS?

In my simple application, I am using express server along with TypeScript. Initially, I can successfully reach my endpoint, but after updating the model, the old data persists. I have attempted the suggested approach mentioned here: How to disable webpage ...

employing ts as a reference for the pathway

Every time I reference path using "ts" I include the following code snippet: import * as fs from 'fs'; import * as path from 'path'; However, when I try to run node index.ts, an error occurs: import * as path from 'path'; ...

Mastering Angular 7: A guide to efficiently binding values to radio buttons

Struggling to incorporate radio buttons into my project, I encountered an issue where the first radio button couldn't be checked programmatically. Despite attempting the suggested Solution, it didn't resolve the problem within my code. Below is ...

Fixing the "Cannot find name" error by targeting ES6 in the tsconfig.json file

I recently started learning AngularJS through a tutorial. The code repository for the tutorial can be accessed at this link. However, upon running npm start using the exact code provided in the tutorial, I encountered the following error: Various TS2304 e ...

Converting objects to arrays in Typescript: A step-by-step guide

Can anyone assist me in converting a string to a Typescript array? Any help would be greatly appreciated. Take a look at the following code snippet: private validateEmptyOption(): any { console.log("CHECKED") let isValid = true; this.currentF ...

ERROR: Unhandled promise rejection: Route cannot be found. URL Segment: 'details'

My current setup involves a router configuration in my Angular application. Below is the code snippet showcasing my router settings: import { Route, RouterModule } from '@angular/router'; import { ProjectDetailsComponent } from '../componen ...

Achieving a similar functionality to Spring Security ACL in a Node.js AWS Lambda serverless environment

I am tackling a javascript challenge that has me stumped. Specifically, I am trying to figure out how to implement fine-grained authorization using an AWS serverless approach. In Spring security ACL, users can be banned from specific tasks at the instanc ...

Error message occurs during compilation of basic Vue file in Webpack

When I execute webpack watch in the VS2017 task runner, it displays the following error: ERROR in ./wwwroot/js/src/App.vue Module build failed: SyntaxError: Unexpected token { at exports.runInThisContext (vm.js:53:16) at Module._compile (module.js:373:25) ...

Dealing with TypeScript and the Mongoose loadClass problem

Working with Mongoose's class schemas has been very beneficial for me. Incorporating TypeScript into my Node project has enhanced the development process. I made sure to refer to Mongoose the Typescript way...? in order to ensure that my Model align ...

What is the process for including a unique attribute for child elements within a React component using TypeScript?

I have a component that creates a Table of Contents (TOC) and List for the child elements. These children can be any JSX.Element. Here is an example... <SectionScrollList> <View key="first"/> <View key="second"/> ...

In TypeScript, what specific type or class does a dynamically imported module belong to?

Can someone assist me in determining the type of an imported module in TypeScript? Here is my query: I have a module called module.ts export class RSL1 {}; Next, I import it into my index.ts using the following code: const script = await import('mod ...

Errors may occur when utilizing TypeScript with the Context Provider API in React

I am in the process of developing a theme changer feature and I want to save the color chosen by the user in the context. However, when I try to pass data to the value prop of the Context.Provider, I encounter an error message TS2739: Type 'Readonly&l ...

Customizing page layout for pages wrapped with higher-order components in TypeScript

Looking to add a layout to my page similar to the one in this link: layouts#per-page-layouts The difference is that my page is wrapped with a HOC, so I tried applying getLayout to the higher order component itself like this: PageWithAuth.getLayout Howev ...

Is it possible to configure a unique Bearer Access Token in the "angular-oauth2-oidc" library?

For my Facebook login, I have set up a custom endpoint where the client sends the Facebook access token. In my Ionic App, I use the '@ionic-native/facebook/ngx' package to retrieve this token. Within a Laravel Json API controller, I utilize Soci ...

Creating a conditional statement within an array.map loop in Next.js

User Interface after Processing After retrieving this dataset const array = [1,2,3,4,5,6,7,8] I need to determine if the index of the array is a multiple of 5. If the loop is on index 0, 5, 10 and so on, it should display this HTML <div class="s ...

Tips for preventing duplicate imports in Sass with the @use rule in Webpack

My sass modules have the ability to import each other as shown in the examples below: // LinearLayout.scss @mixin LinearLayout { ... } linear-layout { @include LinearLayout; } // ScrollView.scss @use "LinearLayout" as *; @mixin ScrollView { ...

Effectively managing user access by authorizing levels and securing routes

Below is the code snippet for a protected route where the authentication status is managed by Redux. If there is no token saved in local storage, the isAuthenticated state is set to false. This code snippet is for protecting routes: import PropTypes from & ...

Ensure that the date is valid using Joi without transforming it into UTC format

Is there a method to validate a date using @joi/date without converting it to UTC? I need to ensure the date is valid before storing it in the database. Here's what I've attempted: const Joi = require('joi').extend(require('@joi/ ...