Typescript: Import types in d.ts files contain relative paths to node_modules

Created on 2 Jun 2018  Â·  31Comments  Â·  Source: microsoft/TypeScript


TypeScript Version: 3.0.0-dev.20180601


Search Terms: declaration import types

Code
src/one/two/export.ts:

import * as CSS from 'csstype';
export const use = (f: CSS.Properties['fontWeight']) => f;

src/one/two/consumer.ts:

import { use } from "./export";
export const asdf = use ;

Expected behavior:
consumer.d.ts has something like

export declare const asdf: (f: import("csstype/index").FontWeightProperty) => import("csstype/index").FontWeightProperty;

Actual behavior:
It has relative paths to node_modules, based on the source file's location at compile time. This places an assumption on the location of the d.ts file when it's consumed externally.

export declare const asdf: (f: import("../../../node_modules/csstype/index").FontWeightProperty) => import("../../../node_modules/csstype/index").FontWeightProperty;

Playground Link: N/A

Related Issues: #24556, but csstype does specify a types key in package.json

Bug Declaration Emit Fixed

Most helpful comment

3.0 arriving soon is great, but allow me to second @OliverJAsh's request for a backport to the 2.9 series; I have a library that this broke and would strongly prefer _not_ to have to set a minimum requirement of 3.0 for people consuming its types.

All 31 comments

Small repro:

// @declaration: true
// @filename: node_modules/foo/index.d.ts
export interface SomeProps {
    x: number;
}

export function foo(): SomeProps;
// @filename: entry.ts
import { foo } from "foo";
export const x = foo();

emits

export declare const x: import("./node_modules/foo/index").SomeProps;

you'd much rather it use the node module resolution style "foo" than "./node_modules/foo/index" 😛

There is a wrench, though:

// @declaration: true
// @filename: node_modules/foo/other/index.d.ts
export interface OtherIndexProps {}
// @filename: node_modules/foo/other.d.ts
export interface OtherProps {}
// @filename: node_modules/foo/index.d.ts
import { OtherProps } from "./other";
import { OtherIndexProps } from "./other/index";

export function foo(): [OtherProps, OtherIndexProps];
// @filename: entry.ts
import { foo } from "foo";
export const x = foo();

in this case, writing import("foo/other"), by nodejs rules, always yields the loose file. So you need to know that you can't just write import("foo/other") to refer to import("foo/other/index"), since the loose file overrides it. All this means that to do this right, you pretty much need to check the full fs to ensure you don't shorten to something that refers to something else.

Minimally, stripping off the leading relative dir parts and node_modules part is safe, reduces verbosity, and improves portability, though.

I can reproduce this issue in 2.9.1, it's a major blocker for us in upgrading to TS2.9.

TypeScript 2.9.2 made some changes to this but it's still broken.

2.9.1 generated type import paths like ./node_modules/redux/index but now in 2.9.2 they are ../../../../../Users/esamatti/code/mylibrary/node_modules/redux

In my case just redux would be the desired output.

@epeli can you give typescript@next a try?

@mhegazy we still haven't published a nightly this week.

Sure. Can you ping me when it's out so I can test it?

I too am seeing this problem; my imports in the declarations are going all the way to my filesystem root. I think this might have something to do with the baseUrl config, pretty sure it shouldn't apply here because this is a relative path but it seems to impact what gets outputted (unless I'm misunderstanding the use case of baseUrl).

There's a nested dependency in my code, where I have this import:

// File: src/routes/listOperations.ts
import { createApiRoute } from "../";
export const listOperations() = createApiRoute<ListOperationsEndpoint>( //... );

// File: src/routes/index.ts"
import { ApiContext } from "../app";
export const createApiRoute<Endpoint extends ApiEndpoint> = (ctx: ApiContext<ApiEndpoint["authenticated"]>): Promise<void> => //...

// File src/app.ts
export type AuthenticatedContext = // ...
export type UnauthenticatedContext = // ...
export type ApiContext<Authenticated extends boolean> = Authenticated ? AuthenticatedContext : UnauthenticatedContext

If baseUrl is not present, then I get this output:

// File: build/routes/listOperations.d.ts
export declare const listOperations: (operations: CollectionReference) => (ctx: import("../../../../../../../../Users/omar/code/raha/raha-api/src/app").UnauthenticatedContext) => Promise<void>;

Not what I expect! I would expect it to be ../../app.

Then, if I set baseUrl: "./" in tsconfig.json I get:

export declare const listOperations: (operations: CollectionReference) => (ctx: import("src/app").UnauthenticatedContext) => Promise<void>;

Yeah, shouldn't be an absolute path now, since I have no paths entry in tsconfig. Pretty sure baseUrl shouldn't be affecting this since this is not an absolute path...

@osdiab do you see the same thing with typescript@next?

Yes, on typescript@^3.0.0-dev20180616
On Sat, Jun 16, 2018 at 17:10 Wesley Wigham notifications@github.com
wrote:

@osdiab https://github.com/osdiab do you see the same thing with
typescript@next?

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/Microsoft/TypeScript/issues/24599#issuecomment-397838964,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ABzabqAQjafbWX2yRrJVqY8K1DKJ60WNks5t9XQxgaJpZM4UXXxN
.

What're all the rest of your tsconfig options (outDir, rootDir, etc), and are there any symlinks in your project?

No symlinks, here’s the tsconfig:

{
  "compilerOptions": {
    "target": "es2015",
    "module": "esnext",
    "moduleResolution": "node",
    "lib": ["es2018"],
    "sourceMap": true,
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "noImplicitAny": true,
    "declaration": true
  },
  "exclude": ["node_modules", "dist"]
}

On Sat, Jun 16, 2018 at 14:39 Wesley Wigham notifications@github.com
wrote:

What're all the rest of your tsconfig options (outDir, rootDir, etc), and
are there any symlinks in your project?

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/Microsoft/TypeScript/issues/24599#issuecomment-397840238,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ABzabrbuKQgAuRiTpjWV3O9xSHNHf59sks5t9XslgaJpZM4UXXxN
.

If you need something to reproduce this with you can use my redux-stack lib. There is no baseUrl used in this project.

git clone https://github.com/epeli/redux-stack.git
cd redux-stack
git checkout 21879cfb8bb35a6fed4fcb62cf3cd3d2eaf1935a # current master at the time of writing
npm install
npm install typescript@next # get 3.0 dev version
./node_modules/.bin/tsc

and checkout the type imports in typings/src/configure-store.d.ts. With 3.0.0-dev.20180616 they are like ../../../../../Users/esamatti/code/redux-stack/node_modules/redux.

I am also seeing this issue.

//foo.d.ts
export declare const areTeamsLoading: import("../../../../../../../../../../npm/default/node_modules/reselect/src/index").OutputSelector<ITeamAwareState, boolean, (res: ITeamState) => boolean>;

We have a custom build process that copies the build files to a slightly different directory structure than the source code, so this relative path is incorrect.

Got biten by this.

I have a function whose return type is implicit and lives in another file my project.

When compiling with -d I get following output:

Typescript 2.9.1:

readonly scope: import("./scope").Scope;

Typescript 2.9.2:

readonly scope: import("../../../../../../Code/x/packages/lib/src/templating/scope").Scope;

This is a very bad regression!

2.9.1 worked well enough, because it stayed inside my declarations folder.
2.9.2 pulls in the src folder, which brings all my .ts lib sources into consuming projects and everything breaks hard.

Sorry to say that this will keep me on TypeScript v2.8.4 for an internal library I'm building.

I've been working with TS v2.9.x in a so-called main-project, which depends on certain types from an NPM-installed module interface-spec.

More About This Internal Project

// package.json looks something like this...
{
  "name": "main-project",
  "devDependencies": {
    "interface-spec": "git+https://github.com/org/interface-spec.git#semver:^2.0.0-alpha.0",
    // ...
  }
  // ...
}

The folder structure is like this:

  • node_modules

    • interface-spec

  • package.json
  • src

    • index.ts (imports and implicitly references types from interface-spec)

    • More source files

  • tsconfig.json
  • tsconfig.dts.json
  • tslint.json

Finally, interface-spec contains type definitions created using Protobuf.js, which can generate TypeScript definitions for Protobuf messages and services using the provided pbts command.

—



Types from interface-spec are being turned into import() statements with relative paths by TS v2.9.x when emitting declarations for main-project. (I suspect) the paths should be absolute.

For example, an import() like this was emitted by v2.9.1:

import("../node_modules/interface-spec/dist/index").google.protobuf.Empty

Here's one from v2.9.2:

import("../../../../../../Users/kohlmannj/Sites/main-project/node_modules/interface-spec").google.protobuf.Empty

Note that the path from TS v2.9.2 at least ends in interface-spec, but it just just be import('interface-spec'); that's what I'd type if I were manually writing this import() statement, anyway.

Here are the two tsconfig files I'm using:

tsconfig.json

{
  "compilerOptions": {
    "target": "esnext",
    "module": "commonjs",
    "allowJs": true,
    "checkJs": true,
    "jsx": "preserve",
    "sourceMap": true,

    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "strictPropertyInitialization": true,
    "alwaysStrict": true,

    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,

    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,

    "experimentalDecorators": true,
  },
  "exclude": [
    "**/__old__/**",
    "**/__wip__/**",
    "**/lib/**",
    "**/dist/**",
    "./scripts/**",
    "**/webpack.config.js"
  ]
}



tsconfig.dts.json

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "noEmit": false,
    "allowJs": false,
    "checkJs": false,
    "declaration": true,
    "declarationMap": true
  },
  "exclude": [
    "**/__mocks__/**/*",
    "**/__stories__/**/*",
    "**/__stubs__/**/*",
    "**/__tests__/**/*",
    "**/__old__/**",
    "**/__wip__/**",
    "**/lib/**",
    "**/dist/**",
    "./scripts/**",
    "**/webpack.config.js"
  ]
}



The command I'm using to generate type definitions is:

$ tsc -p ./tsconfig.dts.json --emitDeclarationOnly --outDir ./lib

Hope this helps, sorry I cannot share the project/s directly.

[Edit] Based on @walkerburgin's https://github.com/Microsoft/TypeScript/issues/24599#issuecomment-398901795, noting that this is on:

  • macOS High Sierra 10.13.5
  • An APFS encrypted volume

We are also affected by this issue.
But in our case it is not related to node_modules directory.

So far we've narrowed the repoduction scope a bit. It is reproduced on Windows when there's an uppercased letter somewhere in project path.

This behavior is reproducable with both typescript version 2.9.2 and 3.0.0-dev.20180619

Here is a reproduction repository https://github.com/nyrkovalex/ts-import-bug
It contains two identical folders — one containing uppercased letters in its name and another one with lowercased letters only.

The bug is repeatably reproduced inside uppercased folder and cannot be reproduced inside the lowercased one.

We've been hitting this as well on OSX & [email protected] ([email protected] if it matters).

I've got two different volumes mounted:

  • APFS Volume • APFS (Encrypted)
  • CoreStorage Logical Volume • Mac OS Extended (Case-sensitive, Journaled, Encrypted)

Moving the project that's failing from the first volume to the second "fixes" the build for me.

@nyrkovalex thanks for the simple repro! Case sensitivity propagation was the issue, as it turns out. #25110 is up with a fix, and I'll see if we can get it into the next 2.9 release, too.

@weswigham Thank you, glad to see that pull request, hope it makes it to the next release. We'll stay tuned.

The fix should be in tomorrow's typescript@next. please give it a quick test and let us know if you are still seeing issues.

Hello, I am bit confused as this issue seems to be tracking two different problems.

  1. paths to node_modules are showing up as relative paths ./node_modules/package instead of just package
  2. paths are going back to root, ../../../Users/user/package/file instead of just ./file

So does #25110 solve both or just the 2nd issue?

@tyler-johnson please give typescript@next a try tonight, and if you are still seeing issues please file a new ticket with more details.

@mhegazy I can confirm excessive ../../../ path generation is fixed in [email protected].

I still see

export declare const fetchFilterCategories: ({ entityType, entityId }: any) => import("node_modules/axios").AxiosPromise<any>;

to reproduce:

import Axios from 'axios'

export const fetchFilterCategories = ({ entityType, entityId }: any) => {
    return Axios.get(`sd`);
};

Also have such references

/// <reference path="../node_modules/@types/lodash/common/common.d.ts" />
/// <reference path="../node_modules/@types/lodash/common/array.d.ts" />
/// <reference path="../node_modules/@types/lodash/common/collection.d.ts" />
/// <reference path="../node_modules/@types/lodash/common/date.d.ts" />
/// <reference path="../node_modules/@types/lodash/common/function.d.ts" />
/// <reference path="../node_modules/@types/lodash/common/lang.d.ts" />
/// <reference path="../node_modules/@types/lodash/common/math.d.ts" />
/// <reference path="../node_modules/@types/lodash/common/number.d.ts" />
/// <reference path="../node_modules/@types/lodash/common/object.d.ts" />
/// <reference path="../node_modules/@types/lodash/common/seq.d.ts" />
/// <reference path="../node_modules/@types/lodash/common/string.d.ts" />
/// <reference path="../node_modules/@types/lodash/common/util.d.ts" />

compile to reproduce:

import {mapValues} from 'lodash';
export const mapDispatchToProps = mapValues

ts version: Version 3.0.0-dev.20180630

Should be fixed by #25364, please open a new issue if you see any ../../node_modules-like paths in typescript@next.

typescript@next _tomorrow_ for anyone watching the thread.

3.0.0-dev.20180705 seems to have fixed this, thank you!

Is there any chance of getting this out into a 2.9 patch release, so we don't have to wait for v3 (which some of us might not be able to upgrade to immediately)?

We've just upgraded to 2.9 and then stumbled into this which is a bit of a pain.

3.0 should be available next week.

3.0 arriving soon is great, but allow me to second @OliverJAsh's request for a backport to the 2.9 series; I have a library that this broke and would strongly prefer _not_ to have to set a minimum requirement of 3.0 for people consuming its types.

I still see https://github.com/Microsoft/TypeScript/issues/24599#issuecomment-401852343 for tsx files with next typescript.

Was this page helpful?
0 / 5 - 0 ratings