(types coming from @type/markdown-it) https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/markdown-it/lib/index.d.ts#L93
I'd like to create arguments types for the possible arguments to the markdown-it
use
method which is typed as this:
type PluginSimple = (md: MarkdownIt) => void;
type PluginWithOptions<T = any> = (md: MarkdownIt, options?: T) => void;
type PluginWithParams = (md: MarkdownIt, ...params: any[]) => void;
I have a react hook that I would like to hand in markdown-it plugins like this:
type Plugin = MarkdownIt.PluginSimple |
[MarkdownIt.PluginWithOptions, {}?] |
[MarkdownIt.PLuginWithParams, ...any[]]
type Plugins = Plugin[]
function useMarkdown(source: any, plugins?: Plugins) {
React.useEffect(() => {
plugins?.forEach(plugin => md.use(...plugin))
}, [md, plugins])
}
First of, I did not know how to add the template argument to the second plugin definition.
This does not work:
[<T = any>MarkdownIt.PluginWithOptions<T>, T?]
But mostly I would like the TS compiler to recognise that that the use of md.use(...plugin)
is safe.
It complains, that the argument needs to support
Expected at least 1 argument, but git 0 or more
An argument for 'plugin' was not provided.
Type
Plugin must have a '[Symbol.iterator]()' method that returns an interator
Changing my line to handle the array cases manually:
plugins?.forEach(plugin => Array.isArray(plugin) ? md.use(...plugin) : md.use(plugin))
Removes the iterator error message but leaves the other too for the usage of ...plugin
You are passing all three cases to
md.use
with spread syntax, so they all have to be tuples. In the case ofPluginSimple
, the arguments are a tuple of length1
.Now for the error about requiring 1 or more arguments, we can make sure that typescript knows that there is always at least one argument by destructuring the first entry in the tuple separately from the rest. Based on our union type, we know that
main
is always defined and is one of the plugin types, while rest isany[]
and will be[]
for the simple plugin.This isn't perfect because we aren't enforcing a match between the
PluginWithOptions
and its specific option type, but hopefully it's good enough for your use case. For more advanced typing, I would think about defining your own plugin object which holds a plugin and its arguments rather than using tuples.