Description
TypeScript Version: 3.4.3
Search Terms:
transitive dependency inferred type cannot be named without a reference
Code
Sorry that this is a Dockerfile, since the problem relates to multiple modules being installed as dependencies to each other it's difficult to express in a single piece of code. There's only 16 lines of code total, near as minimal as I could make it.
https://github.com/ravenscar/ts29221
docker build -t ts29221 https://raw.githubusercontent.com/ravenscar/ts29221/master/Dockerfile
Expected behavior:
tsc
can compile the code
Actual behavior:
tsc
fails with the messge:
src/index.ts(3,14): error TS2742: The inferred type of 'thingyTuple' cannot be named without a reference to '../../b/node_modules/ts29221-a/dist'. This is likely not portable. A type annotation is necessary.
This used to work fine in TS < 3.2 and seems to have broken since then, looking at the breaking changes I can't see anything mentioned which would apply here.
At first we thought we were experiencing issues due to symlinks created by lerna bootstrap and wanted to create an extremely minimal reproduction based on #29221, however we discovered that symlinks aren't the problem and have produced a Dockerfile to show that:
docker build -t ts29221 https://raw.githubusercontent.com/ravenscar/ts29221/master/DockerfileNoSymlinks
We also have a Dockerfile showing this working in 3.1:
docker build -t ts29221 https://raw.githubusercontent.com/ravenscar/ts29221/master/Dockerfile31
What happens here is that module ts29221-a
exports a type definition, module ts29221-b
uses that type definition in the return type of a function, then module ts29221-c
uses that function to assign a value to a const.
This is using the new build system, with refs in the tsconfig, and is bootstrapped by lerna.
What is happening here is that when tsc
is compiling ts29221-c
it imports the types from ts29221-b
which in turn imports them from ts29221-a
. It then sees that they types are at a relative position of ../b/node_modules/ts29221-a/dist
compared to ts29221-c
's package.json.
They are actually also at node_modules/ts29221-b/node_modules/ts29221-a/dist
relative to ts29221-c
's package.json.
This is for example where they may be if they were installed by a package manager that didn't flatten dependencies (e.g. npm2 or pnpm).
The only way around this is for c
to somehow know that b
uses a
and to add a
as a direct dependency of c
. Then to import the used type definition from a
before importing b
, even though c
doesn't directly use a
(so will need a tslint:ignore too).
Or do something even worse such as put this workaround at the top of c/src/index.ts
:
import * as _ from '../node_modules/ts29221-b/node_modules/ts29221-a';
I think it is unreasonable for modules to need to know the dependencies of any of their submodules when it comes to types, although that is what we are currently doing in dozens of places.
I understand that the module resolution shows that it will only go up for node_modules but it really seems like something went wrong here as this pretty much makes TS broken for monorepos that use symlinks, if this is intentional then it's hard to imagine how the new build/watch features (which are awesome BTW) are supposed to work when the ts module dependencies are multiple levels deep.