You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Bug]: 10.0.0 types inferred as any break compilation #2341
Adapter compiler options in src/lib/typescriptSettings.ts:7 set neither strict nor noImplicitAny.
Live runtime confirmed effective values: strict=true, noImplicitAny=true.
rxjs inference was already failing under v9—the editor displayed observer: any. v9 tolerated it because strict mode defaulted off. v10 rejects same existing any.
Live reproduction produced TS7006. Adding strict: false made TS7006 disappear. Original test script restored and running.
Compatibility fix: explicitly add strict: false to tsCompilerOptions. Narrower fix: noImplicitAny: false, but other newly enabled strict checks would remain. No repository files changed.
changed the title [-][Bug]: 10.0.0 types inferred as `any` break compilation with rxjs[/-][+][Bug]: 10.0.0 types inferred as `any` break compilation[/+]on Aug 6, 2026
In the long term, I'd rather fix #928 and think about how to implement global scripts, regular scripts, and additional node_modules with default TypeScript compilation. Please share your thoughts.
Wouldn't it make sense to make this user configurable. strict can be enabled by default, but should be easy to disable. I have a lot of scripts to fix, which I use not only for me but also for friends and family. It would be a lot of work to go through all of that.
Wouldn't it make sense to make this user configurable. strict can be enabled by default, but should be easy to disable. I have a lot of scripts to fix, which I use not only for me but also for friends and family. It would be a lot of work to go through all of that.
Wouldn't it also make sense to rethink Typescript compilation to properly support type inference and modern packages?
Both halves of that question are now addressed — and it turned out not to need a rethink of the
compilation. Details, in case they are useful.
Type inference
This was not a TypeScript 6 problem. The declarations of a library never reached the compiler intact,
so everything imported from one was any; strict only made that visible as an error. Three things
were wrong in resolveTypings:
the declaration files were placed under the name the library has on disk, while the package.json next to them went under the name the scripts import. The js-controller installs
script libraries under an adapter-scoped alias, so those were two different directories and
TypeScript could never connect them.
the entry point was wrapped in declare module "<pkg>" { … }. Inside an ambient module declaration
the relative paths a barrel file re-exports from are not resolved, so every exported name collapsed
to any. It is only applied now where it actually helps — declarations that are not a module.
the regex that follows the imports inside a .d.ts had no m flag, so exactly one import per file
was followed, and only if it happened to be on the first line. For rxjs 6 that collected 6 of its
~800 declaration files. Side effect imports (import "./x";) were not followed at all — which is
what @iobroker/types consists of, so the ioBroker.* types were missing as well.
Modern packages
You are right that this was broken, and it is what #928 is about on the type side. resolveTypings
only read the legacy types/typings fields. rxjs 7 declares "types": "index.d.ts" although no
such file exists — the real declarations are named by the exports map — so resolution gave up and
the adapter fell back to declare module "rxjs";, i.e. the whole library as any.
Four changes were needed, all in that one function:
the entry point is taken from exports["."].types (including the nested require/import
conditions) when the legacy field is missing or points at a file that is not there. A candidate
only counts if it really exists.
the declarations are laid out around the entry point rather than around the package.json, so moduleResolution: node10 finds it even when they live in a subdirectory. Where declarations
reach above the entry point, the original layout is kept — moving them would break the relative
imports between them.
the package.json handed to TypeScript describes that layout instead of the one on disk. This one
was not obvious: with the original manifest the compile failed with Cannot find module 'rxjs',
without it the same files worked. TypeScript refuses to resolve a package whose exports map it
cannot follow under node10 — and those paths do not exist in the virtual file system anyway.
the package.json is located on disk instead of through require.resolve("<pkg>/package.json").
That goes through Node's module resolution, and a package whose exports map does not list ./package.json makes it throw. rxjs 7 happens to export it; many packages do not.
Result
Verified with strict: true, so nothing is hidden by a silent any:
declaration files
inference
const bad: number = new Observable<string>()
o.next(42) on Observable<string>
rxjs 6
6 → 65
works
rejected
rejected
rxjs 7
none (was declare module) → 190
works
rejected
rejected
The two negative probes matter as much as the positive one: they are what tells a properly typed
library apart from one that is silently any, and they are checked to fail on the type error rather
than on "cannot find module".
There is a regression test for all of it, with a fixture shaped like rxjs 7 — legacy stub, real
declarations in a subdirectory, exports map — plus the alias installation path.
What this does not cover
moduleResolution stays at node10, because virtual-tsc forces it and it cannot be configured
from the adapter. That is fine for resolving a package's main entry, as above. Subpath entry points
(rxjs/operators) go through a separate resolveTypings call and land in the case where the layout
is deliberately left alone — I have not verified those. And this is the type side only; whether newer
rxjs also runs is a separate question I did not touch.
Verified with strict: true, so nothing is hidden by a silent any:
declaration files
inference
const bad: number = new Observable<string>()
o.next(42) on Observable<string>
rxjs 6
6 → 65
works
rejected
rejected
rxjs 7
none (was declare module) → 190
works
rejected
rejected
The two negative probes matter as much as the positive one: they are what tells a properly typed
library apart from one that is silently any, and they are checked to fail on the type error rather
than on "cannot find module".
There is a regression test for all of it, with a fixture shaped like rxjs 7 — legacy stub, real
declarations in a subdirectory, exports map — plus the alias installation path.
What this does not cover
moduleResolution stays at node10, because virtual-tsc forces it and it cannot be configured
from the adapter. That is fine for resolving a package's main entry, as above. Subpath entry points
(rxjs/operators) go through a separate resolveTypings call and land in the case where the layout
is deliberately left alone — I have not verified those. And this is the type side only; whether newer
rxjs also runs is a separate question I did not touch.
So the honest answer to your question: rethinking the compilation would still be the cleaner long-term
route if we ever need real node16/bundler semantics, but neither type inference nor modern
packages require it.
One additional scenario does not appear to be covered: when both global and non-global scripts import the same library (using rxjs as an example), the generated code fails with a duplicate declaration:
One additional scenario does not appear to be covered: when both global and non-global scripts import the same library (using rxjs as an example), the generated code fails with a duplicate declaration:
The example you've given, or even multiple global scripts importing rxjs. Every script is considered isolated from the editor's perspective, so you need to import in every script. You would need to do the same when developing in a normal Javascript project outside of iobroker. In addition, I also compile the scripts using Typescript "project files" to emulate iobroker's global script behavior in e.g. GitHub actions: https://github.com/agross/iobroker-scripts/blob/master/home/Automation/Alarm.tsconfig.json
It contains your two commits (Isolate compiled TypeScript module bindings and Isolate TypeScript bindings between global scripts), with you as the author. I left out the first two commits of your branch, because they only changed the old build/src output and master already has the typings part. Your fixtures were also not needed: the new tests in test/testTypeScript.js use a simple ambient module declaration instead.
The PR description lists a few behaviour changes that come with the wrapping (strict mode, no hoisting across scripts, exported const/let of global scripts now visible). If you can, please test the branch fix/ts-import-collision-2341 with your scripts.
Activity
[-][Bug]: 10.0.0 breaks rxjs compatibility[/-][+][Bug]: 10.0.0 types inferred as `any` break compilation with rxjs[/+]agross commentedon Aug 6, 2026
I ran Codex on this:
Cause confirmed: TypeScript 6 default change, not rxjs regression.
5.9.3→6.0.3inpackage.json:57, via PR Typescript6 #2335.strictby default. This implicitly enablesnoImplicitAny. Official TypeScript 6 notes.src/lib/typescriptSettings.ts:7set neitherstrictnornoImplicitAny.strict=true,noImplicitAny=true.observer: any. v9 tolerated it because strict mode defaulted off. v10 rejects same existingany.Live reproduction produced TS7006. Adding
strict: falsemade TS7006 disappear. Original test script restored and running.Compatibility fix: explicitly add
strict: falsetotsCompilerOptions. Narrower fix:noImplicitAny: false, but other newly enabled strict checks would remain. No repository files changed.I prefer a fix to get type inference working.
[-][Bug]: 10.0.0 types inferred as `any` break compilation with rxjs[/-][+][Bug]: 10.0.0 types inferred as `any` break compilation[/+]agross commentedon Aug 6, 2026
I have the necessary changes required to fix my scripts in my fork:
master...agross:ioBroker.javascript:issue-2341
In the long term, I'd rather fix #928 and think about how to implement global scripts, regular scripts, and additional node_modules with default TypeScript compilation. Please share your thoughts.
technyon commentedon Aug 8, 2026
Wouldn't it make sense to make this user configurable. strict can be enabled by default, but should be easy to disable. I have a lot of scripts to fix, which I use not only for me but also for friends and family. It would be a lot of work to go through all of that.
GermanBluefox commentedon Aug 15, 2026
Already done
Correcting #2341
agross commentedon Aug 15, 2026
Wouldn't it also make sense to rethink Typescript compilation to properly support type inference and modern packages?
https://github.com/ioBroker/ioBroker.javascript/issues/2341
GermanBluefox commentedon Aug 15, 2026
Both halves of that question are now addressed — and it turned out not to need a rethink of the
compilation. Details, in case they are useful.
Type inference
This was not a TypeScript 6 problem. The declarations of a library never reached the compiler intact,
so everything imported from one was
any;strictonly made that visible as an error. Three thingswere wrong in
resolveTypings:package.jsonnext to them went under the name the scripts import. The js-controller installsscript libraries under an adapter-scoped alias, so those were two different directories and
TypeScript could never connect them.
declare module "<pkg>" { … }. Inside an ambient module declarationthe relative paths a barrel file re-exports from are not resolved, so every exported name collapsed
to
any. It is only applied now where it actually helps — declarations that are not a module..d.tshad nomflag, so exactly one import per filewas followed, and only if it happened to be on the first line. For rxjs 6 that collected 6 of its
~800 declaration files. Side effect imports (
import "./x";) were not followed at all — which iswhat
@iobroker/typesconsists of, so theioBroker.*types were missing as well.Modern packages
You are right that this was broken, and it is what #928 is about on the type side.
resolveTypingsonly read the legacy
types/typingsfields. rxjs 7 declares"types": "index.d.ts"although nosuch file exists — the real declarations are named by the
exportsmap — so resolution gave up andthe adapter fell back to
declare module "rxjs";, i.e. the whole library asany.Four changes were needed, all in that one function:
exports["."].types(including the nestedrequire/importconditions) when the legacy field is missing or points at a file that is not there. A candidate
only counts if it really exists.
package.json, somoduleResolution: node10finds it even when they live in a subdirectory. Where declarationsreach above the entry point, the original layout is kept — moving them would break the relative
imports between them.
package.jsonhanded to TypeScript describes that layout instead of the one on disk. This onewas not obvious: with the original manifest the compile failed with
Cannot find module 'rxjs',without it the same files worked. TypeScript refuses to resolve a package whose
exportsmap itcannot follow under node10 — and those paths do not exist in the virtual file system anyway.
package.jsonis located on disk instead of throughrequire.resolve("<pkg>/package.json").That goes through Node's module resolution, and a package whose
exportsmap does not list./package.jsonmakes it throw. rxjs 7 happens to export it; many packages do not.Result
Verified with
strict: true, so nothing is hidden by a silentany:const bad: number = new Observable<string>()o.next(42)onObservable<string>declare module) → 190The two negative probes matter as much as the positive one: they are what tells a properly typed
library apart from one that is silently
any, and they are checked to fail on the type error ratherthan on "cannot find module".
There is a regression test for all of it, with a fixture shaped like rxjs 7 — legacy stub, real
declarations in a subdirectory,
exportsmap — plus the alias installation path.What this does not cover
moduleResolutionstays atnode10, becausevirtual-tscforces it and it cannot be configuredfrom the adapter. That is fine for resolving a package's main entry, as above. Subpath entry points
(
rxjs/operators) go through a separateresolveTypingscall and land in the case where the layoutis deliberately left alone — I have not verified those. And this is the type side only; whether newer
rxjs also runs is a separate question I did not touch.
Verified with
strict: true, so nothing is hidden by a silentany:const bad: number = new Observable<string>()o.next(42)onObservable<string>declare module) → 190The two negative probes matter as much as the positive one: they are what tells a properly typed
library apart from one that is silently
any, and they are checked to fail on the type error ratherthan on "cannot find module".
There is a regression test for all of it, with a fixture shaped like rxjs 7 — legacy stub, real
declarations in a subdirectory,
exportsmap — plus the alias installation path.What this does not cover
moduleResolutionstays atnode10, becausevirtual-tscforces it and it cannot be configuredfrom the adapter. That is fine for resolving a package's main entry, as above. Subpath entry points
(
rxjs/operators) go through a separateresolveTypingscall and land in the case where the layoutis deliberately left alone — I have not verified those. And this is the type side only; whether newer
rxjs also runs is a separate question I did not touch.
So the honest answer to your question: rethinking the compilation would still be the cleaner long-term
route if we ever need real
node16/bundlersemantics, but neither type inference nor modernpackages require it.
https://github.com/ioBroker/ioBroker.javascript/issues/2341
https://github.com/ioBroker/ioBroker.javascript/issues/2341
Added a wizard to the rule editor (#2344)
Start Wizard automatically (#2345)
Fixing CI (#2346)
[OFFICIAL RELEASE] 10.1.0
agross commentedon Aug 17, 2026
One additional scenario does not appear to be covered: when both global and non-global scripts import the same library (using
rxjsas an example), the generated code fails with a duplicate declaration:My fork includes a fix for this case in these two commits:
Could you please include this scenario when evaluating the proposed changes?
Blockly ts (#2354)
GermanBluefox commentedon Aug 30, 2026
Am I understand you right you make something like:
?
If yes, why you import rxjs 2 times?
agross commentedon Aug 30, 2026
The example you've given, or even multiple global scripts importing rxjs. Every script is considered isolated from the editor's perspective, so you need to import in every script. You would need to do the same when developing in a normal Javascript project outside of iobroker. In addition, I also compile the scripts using Typescript "project files" to emulate iobroker's global script behavior in e.g. GitHub actions: https://github.com/agross/iobroker-scripts/blob/master/home/Automation/Alarm.tsconfig.json
agross commentedon Oct 8, 2026
@GermanBluefox Can you please look at this? It's preventing me from upgrading the adapter.
Using the same imports in global scripts and regular scripts should work as before.
agross commentedon Oct 11, 2026
@GermanBluefox My fork includes the fix, rebased on master.
GermanBluefox commentedon Oct 16, 2026
Can you make a PR for that?
GermanBluefox commentedon Oct 16, 2026
@agross Thanks for the fix! The PR is here: #2408
It contains your two commits (
Isolate compiled TypeScript module bindingsandIsolate TypeScript bindings between global scripts), with you as the author. I left out the first two commits of your branch, because they only changed the oldbuild/srcoutput andmasteralready has the typings part. Your fixtures were also not needed: the new tests intest/testTypeScript.jsuse a simple ambient module declaration instead.The PR description lists a few behaviour changes that come with the wrapping (strict mode, no hoisting across scripts, exported
const/letof global scripts now visible). If you can, please test the branchfix/ts-import-collision-2341with your scripts.Kept the line numbers of global TypeScript scripts in error messages, #…
agross commentedon Oct 17, 2026
@GermanBluefox Thanks, I installed the adapter from the branch and recompiled all scripts. No errors. LGTM!
GermanBluefox commentedon Oct 17, 2026
Thanks for testing! I will merge #2408 and it will be in the next release.
Fixed: TypeScript scripts importing the same module as a global script,