Adding New Typings
Finding/Discovering new typings
The first step to add new typings, is finding the object/interface/module you want to add typings for within the Obsidian app.
One of the easiest way to do this, is to open the Obsidian DevTools Console (Ctrl + Shift + I
), and start searching for the interface you wish to
type from the app.
object.
If you want to, for example, add typings for the InternalPlugins
object, you can type app.internalPlugins
into the console,
which will produce the following output:
Note how the internalPlugins object contains multiple prototypes: the InternalPlugins
interface itself, but also
the Events
class and then the Object
literal (does not need to be typed). This can be determined this by looking at the other definitions in the (un)official API,
and checking whether the two objects define the same properties.
Thus, to define InternalPlugin
, you can start by adding each of the methods and properties to a InternalPlugins
interface:
To keep it simple, we first add unknown
as type to every variable and method — unless it is obvious what the type should be
(i.e. app
will always be an instance of App
).
Next up, is the most tedious part of adding typings: finding the correct types for each of the methods and variables.
generateTypes
helper
We built a helper to simplify discoverability process. The generated types contain all the properties and functions that could be reached from the provided objects.
Most of types, especially for function parameters would be marked as unknown
, so you would still have to reverse engineer the logic to replace unknown
with something meaningful, but it’s a good starting point.
In order to use the helper:
- Install the Fix Require Modules plugin. This is needed to be able to run
require('obsidian')
from the Obsidian DevTools Console. - Copy the code from generateTypes.js.
- Open Obsidian DevTools Console (
Ctrl + Shift + I
). - Paste the copied code into your Obsidian DevTools Console.
- Invoke the function like
generateTypes(app.internalPlugins)
.
The helper tries to detect all known obsidian
types, so in the output you will see types like App123
, meaning it’s the most likely can be replaced with just App
(from obsidian
types), but the helper keeps inferring those type to ensure the typings are complete.
By default, the generator depth is 1
, but you can change it generateTypes(app.internalPlugins, depth)
, if you use depth = 0
it means unlimited
.
Typing variables
The easiest way to start, is by tackling the variables first. For example, in the console output, you can see that config
is mapping a string to true
. In this case, it would be safe to assume that the type of config
is Record<string, boolean>
.
However, you can take this one step further still by considering the fact that each string is essentially a different plugin ID.
In this case, we might just define a new type that contains all the plugin IDs, and use this as the key for the config
object:
Similarly, you can define the plugins
variable as Record<InternalPluginName, unknown>
.
Further, it is safe to assume that each element in Plugins
would be some instance of a InternalPlugin
class, so we
will also need to add a new interface for this:
Make sure to also add brief descriptions (using TSDoc) to each of the variables. Mark variables that you are not entirely sure about with @internal
.
Feel free to copy descriptions from previous typings, or from the official API.
Typing methods
Typing methods is a bit more difficult, as aside you will need to know what the method does, and what the expected input and output is.
You could start by typing the methods that do not take any arguments, and have a simple return type. For example, the requestSaveConfig
method.
From the name, we can already make the assumption that this method will probably not return anything, as it is just telling
the app that a config should be saved, the method will thus likely be of type void
.
However, to make sure whether this assumption is correct, you should check the source code (see next section).
If possible, you can also run the method in the console, and see what happens (in this case, nothing happens, so it is likely just void
).
As before, if you are not certain about the return type or the workings of the function in general,
add a @internal
tag to the method description, and/or mark the return type as unknown
.
Next up, are methods that take arguments. For example, the getEnabledPluginById
method takes a single argument.
It is very likely that this argument will just be of type InternalPluginName
, but again, you could easily verify this by
running the method in the console (i.e. app.internalPlugins.getEnabledPluginById('audio-recorder')
and app.internalPlugins.getEnabledPluginById('wrong-id')
).