Typescript: tsconfig paths break when consuming the compiled declaration files from a different package

Created on 5 Oct 2017  路  16Comments  路  Source: microsoft/TypeScript

TypeScript Version: tried 2.2.1 and 2.5.2

Code

src/file1.ts

export type myType = {anything : number};

src/file2.ts

import { myType } from '@src/file1';

export type myOtherType = {anythingElse : number} & myType;

tsconfig.json

{
  "compilerOptions": {
    "outDir": "./dist",
    "target": "es5",
    "module": "commonjs",
    "moduleResolution": "node",
    "declaration": true,
    "noImplicitAny": true,
    "removeComments": true,
    "sourceMap": true,
    "strictNullChecks": true,
    "noImplicitThis": true,
    "noImplicitReturns": true,
    "noUnusedParameters": true,
    "noUnusedLocals": true,
    "baseUrl": ".",
    "paths": {
      "@src/*": ["./src/*"]
    },
    "lib": ["es2017", "dom"]
  },
  "compileOnSave": false,
  "include": [
    "src/**/*",
    "app/**/*"
  ],
  "exclude": [
    "node_modules",
    "dist"
  ]
}

Expected behavior:
i should be able to import myOtherType in a different package (after say publishing this to npm, with the typings field set to dist/file2.d.ts) without errors

Actual behavior:
typescript says Cannot find module "@src/file1" when compiling any package that consumes these types.

Note: the runtime behavior works properly in my case because i'm handling the module resolution through webpack. It's only the types that are failing and I'm curious how the paths property could be expected to work outside of the package in which they are defined. Is that property simply not meant to be used in a library style package that's being written for external consumption?

Duplicate

Most helpful comment

@mhegazy I understand the reasoning behind, but from a developer perspective, this is really not convenient.

All 16 comments

The compiler does not re-write your module names for you. they are considered resource identifiers that should not be messed up with. The configuration in the config file tells the compiler where to find the module, not what the module output name should be.

Please see similar discussions in #18951 and #16640.

I did review those issues actually. I've been scouring the internet to understand this. In those issues the concern was about the module names in the actual runtime js. My issue was with the type files themselves so I thought it may be different because otherwise I'm not sure how using paths could ever be valid in declaration files (aka when used in conjunction with declaration : true).

What I'm failing to understand is how the usage of paths in a module that is meant for consumption could ever be valid? Like is this feature meant only to be used in an app style module that would never be consumed? Or is the expectation that I would manually rewrite the module names? and if so why even use paths?

This feature is frequently cited in articles around the web as the typescript corollary for webpack aliases and it works well for that in an app, but is it not meant to be used that way in library? just trying to understand how this config option can work

In those issues the concern was about the module names in the actual runtime js. My issue was with the type files themselves

The compiler has one view of a module, and it does not make much sense to have the .js an .d.ts have different identities.

so I thought it may be different because otherwise I'm not sure how using paths could ever be valid in declaration files (aka when used in conjunction with declaration : true).

The whole path mapping configuration is about telling the compiler where files will be at runtime. It is not a feature to shorten your file names, so tha tyour write ~\mod instead of ..\..\..\mod.
The place to use path mapping is if you have a post-build script that will move your modules around, and will change their names. this is common for requirejs users. for these use cases, i would expect you apply the same transformation to the .d.ts as you do to the .js files.

This feature is frequently cited in articles around the web as the typescript corollary for webpack aliases and it works well for that in an app, but is it not meant to be used that way in library? just trying to understand how this config option can work

I am not familiar with the webpack resources you mention, so can not comment on them. but again, if nothing is going to move the modules to match the path mappings in the tsconfig.json, consuming the .d.ts with the mapped name will not work.

I am not familiar with the webpack resources you mention,

To fill in the gap: Webpack has the resolve field to say "an import of this form should be resolved specially to XYZ". TypeScript's path mapping functionality is available so users can model that type of behavior specifically.

@DanielRosenwasser that's exactly what i'm referring to and in the case of webpack these aliases are persisted into the bundle that gets published for consumption, but that doesn't seem to be true for the matching types that use paths

@mhegazy ok thanks for the info. I had a suspicion that folks were slightly abusing this feature to make it look a bit like webpack aliases (although daniel's comment above makes me wonder if it isn't still meant to fill that need partially).

so not to waste your time too much further but the official recommendation for importing local modules is to always use relative paths if the module is going to be published? and just suss out how many ../'s you need?

the official recommendation for importing local modules is to always use relative paths if the module is going to be published? and just suss out how many ../s you need?

yes. that would be my recommendation.

@DanielRosenwasser just cause you sparked my hope slightly, would it be worth considering proposals to push the paths feature over the line to make it fully match webpack's resolve.alias functionality? (aka make it usable in libraries and not just non-reusable apps)

@mhegazy I understand the reasoning behind, but from a developer perspective, this is really not convenient.

@mhegazy I understand the reasoning behind, but from a developer perspective, this is really not convenient.

This is what "paths" and "baseUrl" feature addresses. seems like you are looking for a different feature.

@mhegazy what would be the proper approach to solicit that feature?

We have had a few issues around that, but nothing clearly asking for a rewrite of module names. feel free to create a new suggestion for it.

Hello,

Chiming in as the author of https://github.com/chmln/flatpickr, typescript declarations have been my most painful interaction with the language.
Consider this basic usecase of typings.

index.d.ts

import { FlatpickrFn } from "./src/types/instance";
export { Instance } from "./src/types/instance";
export { Options, Plugin } from "./src/types/options";

declare var flatpickr: FlatpickrFn;

export default flatpickr;

The tsc complains about https://github.com/chmln/flatpickr/issues/1054#issuecomment-336124936

TLDR:

the files references contain imports which rely on the baseUrl feature.
These imports are not resolved correctly when they're installed as a part of an npm package. Even if I include the tsconfig.json file inside the package.

@chmln, for this example, baseUrl is not relevant, it is only relevant if you have non-relative module names, i.e. types/options. I am not sure i quite understand the issue.

The issue i usually see ppl run into, is they use baseUrl and paths as a convince tool to shorten module names in import. this is an incorrect usage of this feature; the compiler does nothing to make the path mapping work at runtime, nor does it do anything in the generated .d.ts files.
only use paths or baseurl if you have a post-build script to move files and/or a runtime loader (e.g. requirejs) that will look up modules with their mapped names at runtime.

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

May be this helps. I wrote hacky ts-transform-paths, which rewrites aliased import paths accordingly tsconfig baseUrl and paths in d.ts files too.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

CyrusNajmabadi picture CyrusNajmabadi  路  3Comments

bgrieder picture bgrieder  路  3Comments

weswigham picture weswigham  路  3Comments

siddjain picture siddjain  路  3Comments

kyasbal-1994 picture kyasbal-1994  路  3Comments