2

There is some type in react library I try to overload with an augmentation:

In my project, I created a file: ./@types/react/index.d.ts, with content:

import type React from 'react';

declare module 'react' {
    function memo<A, B>(Component: (props: A) => B): (props: A) => React.ReactElement | null;
}

I did to solve some issue I had with TypeScript, which is irrelevant for this question.

I have also provided this @types folder in my ./tsconfig.json file:

"typeRoots": ["./node_modules/@types", "./@types"],

But issue still shows (i.e. my overload does not affect the initial issue).

If instead, in the file I had the issue, I just paste the code block:

declare module 'react' {
    function memo<A, B>(Component: (props: A) => B): (props: A) => React.ReactElement | null;
}

...then issue is resolved.

But I rather not use this way, inline-like. I don't want this declaration to show in the file.

Could anyone tell why the @types folder solution does not work?


Reproduce: Create a dummy project with boilerplate of CRA:

npx create-react-app whatever --template typescript

Modify ./tsconfig.json file with content:

{
  "compilerOptions": {
    "target": "es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",
    "typeRoots": [
      "./node_modules/@types",
      "./@types"
    ]  
  },
  "include": [
    "src"
  ]
}

Create ./@types/react/index.d.ts file with content:

declare module "react" { // augment React types
    function memo<A, B>(Component: (props: A) => B): (props: A) => React.ReactElement | null
    // return type is same as ReturnType<ExoticComponent<any>>
  }

Create ./src/Filter.tsx file:

import React from 'react';

import type { IOption } from './option';

import FilterView from './Filter.view';

interface IProps<T> {
    readonly title: string;
    readonly options: IOption<T>[];
    readonly selectedOption: T;
    readonly onSelectOption: (value: T) => void;
}

const Filter = <T,>(props: React.PropsWithChildren<IProps<T>>) => (
    <FilterView
        title={props.title}
        options={props.options}
        selectedOption={props.selectedOption}
        onSelectOption={props.onSelectOption}
    />
);

Filter.displayName = 'Filter';
Filter.defaultProps = {};

export default React.memo(Filter);

Create Filter.view.tsx file:

import React from 'react';

import type { IOption } from './option';

import classes from './Filter.module.scss';

interface IProps<T> {
    readonly title: string;
    readonly options: IOption<T>[];
    readonly selectedOption: T;
    readonly onSelectOption: (value: T) => void;
}

const FilterView = <T,>(props: React.PropsWithChildren<IProps<T>>) => {
    return (
        <div className={classes['container']}>
            <h5 className={classes['container__title']}>{props.title}</h5>

            {props.options.map((option, index) => (
                <button
                    key={index}
                    className={classes['blabla']}
                    type="button"
                    onClick={() => props.onSelectOption(option.value)}
                >
                    {option.label}
                </button>
            ))}
        </div>
    );
};

FilterView.displayName = 'FilterView';
FilterView.defaultProps = {};

export default React.memo(FilterView);

Create ./option.ts file:

export interface IOption<T> {
    readonly value: T;
    readonly label: string;
}

Then you'll see an error in Filter.tsx file.

1 Answer 1

2
+50

If your module augmentation is in src/@types/react/index.d.ts, you should fill your typeRoots tsconfig option with "./src/@types" (instead of "./@types"):

"typeRoots": [
    "./node_modules/@types",
    "./src/@types"
]

All paths are relative to the tsconfig.json.

Note: there is a typo in the import:

import React from 'react'; // React first letter uppercase to match how it is used in the file: React.ReactElement

Demo: https://codesandbox.io/s/hopeful-silence-qzu12v?file=/tsconfig.json


Once you move your global type files outside your ./src directory, they are no longer automatically included in your TS compilation, since the tsconfig mentions only that folder:

  "include": [
    "src"
  ]

That is why you then have to include the new folder in typeRoots option list.

However, TS finds the normal react types in "./node_modules/@types/react" first, and stops there. Our augmentation file is left behind...

So just make sure to specify your own type root first:

"typeRoots": [
    "./@types", // Own root with augmentations first
    "./node_modules/@types" // base root last
]

That way, TS picks up our augmentation file first in "./@types/react". Within that file, it finds the import "react" again, and looks for the corresponding "base" react type definition file.

Demo: https://codesandbox.io/s/wizardly-shaw-dw84bj?file=/tsconfig.json:519-566

Sign up to request clarification or add additional context in comments.

3 Comments

I edited the question please read again
Please provide a minimal reproducible example otherwise we will never see the end
Done. Please check it now

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.