Reputation: 1874
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.
Upvotes: 2
Views: 354
Reputation: 53370
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
Upvotes: 2