fix!: declare that getPlugin can return undefined - #554
Merged
Merged
Conversation
`BpmnVisualization.getPlugin` casts the result of a `Map.get` to `T` and declares a non-nullable return type. The map
returns `undefined` for an unknown identifier, so the declared type is a lie: a typo in the id, or a plugin that was
never registered, produces `undefined` at runtime while the compiler guarantees an instance. The failure then surfaces
later as a `TypeError` on the first method call, far from its cause.
Declare `T | undefined` so the compiler forces the call site to deal with the missing plugin. The runtime behavior is
unchanged.
The demo, the README example and the tests assert non-null at the retrieval sites, since they all register the plugin
they retrieve a few lines above. Also document the returned value on the method and in the ADR, and cover the lookup of
an unregistered identifier on an instance that does have plugins.
`check-ts-support` now registers and retrieves a plugin before its two existing API calls, and imports
`BpmnVisualization` from the addons rather than from the core package, which is what the README asks consumers to do.
It therefore validates the `GlobalOptions` module augmentation, the new return type and both documented retrieval forms
against the lowest supported TypeScript version. None of this was checked there before.
BREAKING CHANGE:
- `BpmnVisualization.getPlugin` now returns `T | undefined` instead of `T`. Code compiled with `strictNullChecks` that
chains directly on the result, such as `getPlugin<MyPlugin>('my-plugin').aMethod()`, no longer compiles. Add a
non-null assertion when the plugin is known to be registered, or handle `undefined`.
11 tasks
Follow-up of the review of this branch. `CLAUDE.md` still described `getPlugin` without its nullable return, while the README and the ADR were updated. It is the instruction source for automated changes in this repository, so a stale statement there propagates to the next generated call site. The comment in `check-ts-support` claimed that the `GlobalOptions` module augmentation is only checked there. The demo compiles with `tsc` and passes `plugins` as well, so it checks the augmentation too. What is specific to `check-ts-support` is the TypeScript version it checks it against.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Problem
BpmnVisualization.getPlugincasts the result of aMap.gettoTand declares a non-nullable return type:The map returns
undefinedfor an unknown identifier, so the declared type is a lie. A typo in the id, or a plugin that was never registered, producesundefinedat runtime while the compiler guarantees an instance. The failure surfaces later, as aTypeErroron the first method call, far from its cause.Fix
Declare
T | undefined, so the compiler forces the call site to deal with the missing plugin. The runtime behavior is unchanged: the same value was already returned, it was only mistyped.The change makes the defect visible where it was hidden. Building the demo, which compiles in
strict, produced 14TS18048errors acrossoverlays.ts,path-resolver.tsandplugins-by-name.ts: every one of those call sites relied on a guarantee the compiler never had. They now assert non-null, since each registers the plugin it retrieves a few lines above. The tests and the README example do the same.Also in this PR
getPlugingains a JSDoc documenting the returned value, and the ADR states that the lookup returnsundefinedfor an unregistered id.check-ts-supportregisters and retrieves a plugin before its two existing API calls, and importsBpmnVisualizationfrom the addons rather than from the core package, which is what the README asks consumers to do. It now validates theGlobalOptionsmodule augmentation, the new return type, and both documented retrieval forms against the lowest supported TypeScript version, 4.5.2, withstrictandskipLibCheck: false. Removing the optional chaining there fails withTS2532, so the check does bite. This item was initially planned for the tooling PR of docs: analyze the plugin system against browser extension mechanisms #552 and lands here instead, because it is the natural place to prove the new signature holds on the oldest supported compiler.Breaking change
getPluginreturnsT | undefinedinstead ofT. Consumer code compiled withstrictNullChecksthat chains directly on the result, such asgetPlugin<MyPlugin>('my-plugin').aMethod(), no longer compiles. Add a non-null assertion when the plugin is known to be registered, or handleundefined. Nothing changes at runtime, and code that already handled the value defensively is unaffected.Found while writing the plugin system analysis in #552, section 2.3.