-
Notifications
You must be signed in to change notification settings - Fork 1
Description
After some research, here is the third version of implementing extendable typings for packages.
1. Modules
TS docs: https://www.typescriptlang.org/docs/handbook/modules.html
Modules are considered a modern way of structuring code.
Example folder structure:
├── 📁 some-package-name
| ├── 📁 api
| | ├── 📄 Api.processor.js
| | ├── 📄 Api.query.js
| | ├── 📄 Api.request.js
| | └── 📄 Api.type.js
| ├── 📁 component
| | ├── 📁 First
| | | ├── 📄 index.js
| | | ├── 📄 First.component.js
| | | └── 📄 First.container.js
| | └── 📁 Second
| | | ├── 📄 index.js
| | | ├── 📄 Second.component.js
| | | └── 📄 Second.container.js
| ├── 📁 context
| | ├── 📄 File.context.js
| | └── 📄 File.provider.js
| ├── 📁 plugin
| └── 📄 package.jsonTo add typings for any of the following files we can use the following method:
// lets write typings for the file ./component/First/First.component.js
// import necessary dependencies
import React from 'react';
import { FileType } from '../../context/File.context';
// declare that we are located in this module
declare module 'some-package-name/src/component/First/First.component' {
// write typings
interface FirstComponentProps {
file: FileType
}
interface FirstComponentState {}
class FirstComponent extends React.PureComponent<FirstComponentProps, FirstComponentState> {
getContextValue(): FileType
}
}
// if necessary, export default outside module declaration
export default FirstComponent;- We imported type dependency for React as in any other dependency in js file.
- We declared types for
FirstComponentinside a module. - We don't need to write
exportinside module declaration, they are exported by default. - We exported FirstComponent as default export outside of the declared module.
TS does not allow toexport defaulttypes inside module declaration, because it can possibly interfere with other packages. FirstComponentStateinterface is empty, rather it doesn't have an initial state. Even tho it's empty, we still need to declare that it exists and we can extend it later by other modules or plugins.
That is it! We made a module! Now we can start playing with our modular code, extend it and bring life to the modular types!
2. Overrides & Extends
Now, we have a package with its module and typed component module ./component/First/First.component.
If we go to some-package-name-extended package which has a plugin that adds to the component First.component additional state, we can define it in typings as well.
Now we need to actually extend types.
declare module 'some-package-name/src/component/First/First.component' {
// and same type name
interface FirstComponentState {
extendedState?: Record<string, string>
}
}That is it, we have just extended our component state!
Now if we import the original component, that is extended by our plugin, and go to its state we will see that it is extended.
3. Notes
3.1 Empty interfaces
To be able to easily override components types we need to define all types for React.Component generics, even if they are empty.
declare module 'some-package-name/src/component/First/First.component' {
interface FirstComponentProps {
product: ProductType
}
interface FirstComponentState {}
class FirstComponent extends React.PureComponent<ProductProviderProps, ProductProviderState> {
getSomeDataMethod(): SomeDataTypeReturnedByMethod
render(): JSX.Element
}
}
// other module
declare module 'some-package-name/src/component/First/First.component' {
interface FirstComponentState {
myAddedState?: Record<string, string>
}
}3.2 Useful links
https://stackoverflow.com/questions/39853825/how-to-extend-an-interface-declared-in-an-external-library-d-ts/44828876
https://stackoverflow.com/questions/30357634/how-do-i-use-namespaces-with-typescript-external-modules
https://raw.githubusercontent.com/DefinitelyTyped/DefinitelyTyped/master/types/material-ui/index.d.ts
https://www.typescriptlang.org/docs/handbook/namespaces.html
https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html