Typescript: The typescript auto complete doesn't respond to typeRoots folders properly other than node_modules/@types

Created on 28 Dec 2016  Â·  7Comments  Â·  Source: microsoft/TypeScript

TypeScript Version: 2.1.1 / nightly (2.2.0-dev.201xxxxx)

Folder tree:

C:.
│  .gitignore
│  index.ts
│  package.json
│  tsconfig.json
│
├─.vscode
│      settings.json
│
└─@types
    └─foo
            index.d.ts
            types-metadata.json

content of tsconfig.json

{
  "compilerOptions": {
    "module": "commonjs",
    "moduleResolution": "node",
    "target": "es5",
    "lib": [ "es2015.promise", "es5", "scripthost"],
    "typeRoots": [
      "./@types"
    ]
  }
}

content of foo/index.d.ts

declare module "foo" {
    export function getElements(options:any, element: any, recurse:boolean, limit:number): any[];
}

content of index.ts

import * as foo from 'foo';

Expected behavior:

foo should be able to autocomplete

Actual behavior:
No action when use foo. to trigger autocomplete

Working as Intended

Most helpful comment

@vladima I can reproduce this issue in the latest typescript 2.2.1. I have a folder custom-types where I have put definition files written by myself. If I copy these files to node_modules/@types folder, everything works as it should. But the typescript compiler doesn't pick up the types from the custom-types folder.

This is my tsconfig.json:

{
  "compilerOptions": {
    "module": "commonjs",
    "moduleResolution": "node",
    "target": "es2017",
    "sourceMap": true,
    "strictNullChecks": true,
    "sourceRoot": "src",
    "outDir": "build",
    "typeRoots": [
      "./custom-types"
    ]
  },
  "exclude": [
    "node_modules"
  ]
}

package.json

{
  "name": "twilio-service",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "amqplib": "0.5.1",
    "twilio": "2.11.1"
  },
  "devDependencies": {
    "@types/amqplib": "0.5.1",
    "typescript": "^2.2.1"
  }
}

index.ts

import * as twilio from "twilio" // or import twilio = require("twilio") -> Both yield the same result.
const client = new twilio.RestClient("", "") // Client is of `any` type.

index.d.ts inside custom-types->twilio folder

export = TwilioLib

declare function TwilioLib(accountSID: string, authToken: string): TwilioLib.ITwilioClient

declare namespace TwilioLib {

  interface IMessageOptions {
    /**
     * Any number twilio can deliver to.
     */
    to: string
    /**
     * Any number you bought from Twilio and can use for outbound communication.
     */
    from: string
    /**
     * Body of the SMS message.
     */
    body: string
  }

  interface IMessageResponseData {
    sid: string
    date_created: string
    date_updated: string
    date_sent: string | null
    account_sid: string
    to: string
    from: string
    body: string
    status: string
    num_segments: string
    num_media: string
    direction: string
    api_version: string
    price: string | null
    price_unit: string
    error_code: string | null
    error_message: string | null
    uri: string
    subresource_uris: { media: string }
  }

  interface ICallOptions {
    /**
     * Any number twilio can call
     */
    to: string
    /**
     * A Twilio number you own
     */
    from: string
    /**
     * A URL containing TwiML instructions for the call
     */
    url: string
  }

  type CallStatus = "queued" | "initiated" | "ringing" | "answered" | "completed"

  interface ICallResponseData {
    sid: string
    date_created: string
    date_updated: string
    parent_call_sid: string | null
    account_sid: string
    to: string
    formatted_to: string
    from: string
    formatted_from: string
    phone_number_sid: string
    status: CallStatus
    start_time: string | null
    end_time: string | null
    duration: string | null
    price: string | null
    direction: string
    answered_by: string | null
    api_version: string
    forwarded_from: string | null
    caller_name: string | null
    uri: string
    subresource_uris: {
      notifications: string
      recordings: string
    }
  }

  interface ITwilioClient {
    sendMessage(options: IMessageOptions, callback: ((err: Error, responseData: IMessageResponseData) => void))
    makeCall(options: ICallOptions, callback: ((err: Error, responseData: ICallResponseData) => void))
  }

  export class RestClient {
    constructor(accountSID: string, authToken: string)
    sendMessage(options: IMessageOptions): Promise<IMessageResponseData>
    makeCall(options: ICallOptions): Promise<ICallResponseData>
  }

  export function availablePhoneNumbers(country: string): any
  export function request(requestOptions: any, cb?: (error, responseData) => void)

  interface IWorkspaceTasks {
    create(options: any)
  }

  interface IWorkspaceWorkers {
    get(cb: (err, result) => void)
  }

  interface IWorkspaceStatistics {
    get(options: any)
  }

  interface IWorkspace {
    tasks: IWorkspaceTasks
    workers: IWorkspaceWorkers
    statistics: IWorkspaceStatistics
  }

  export class TaskRouterClient {
    workspace: IWorkspace;
    constructor(accountSID: string, authToken: string, workspaceSID: string)
  }

  export class TwimlResponse {
    say(message: string): this
    say(message: string, options: any): this
    pause(options: any): this
    play(soundURL: string): this
    /**
     * Exports the TwimlResponse to XML String.
     */
    toString(): string
  }
}

All 7 comments

cannot repro this locally, can you please provide more information about your setup?

anim

@vladima I can reproduce this issue in the latest typescript 2.2.1. I have a folder custom-types where I have put definition files written by myself. If I copy these files to node_modules/@types folder, everything works as it should. But the typescript compiler doesn't pick up the types from the custom-types folder.

This is my tsconfig.json:

{
  "compilerOptions": {
    "module": "commonjs",
    "moduleResolution": "node",
    "target": "es2017",
    "sourceMap": true,
    "strictNullChecks": true,
    "sourceRoot": "src",
    "outDir": "build",
    "typeRoots": [
      "./custom-types"
    ]
  },
  "exclude": [
    "node_modules"
  ]
}

package.json

{
  "name": "twilio-service",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "amqplib": "0.5.1",
    "twilio": "2.11.1"
  },
  "devDependencies": {
    "@types/amqplib": "0.5.1",
    "typescript": "^2.2.1"
  }
}

index.ts

import * as twilio from "twilio" // or import twilio = require("twilio") -> Both yield the same result.
const client = new twilio.RestClient("", "") // Client is of `any` type.

index.d.ts inside custom-types->twilio folder

export = TwilioLib

declare function TwilioLib(accountSID: string, authToken: string): TwilioLib.ITwilioClient

declare namespace TwilioLib {

  interface IMessageOptions {
    /**
     * Any number twilio can deliver to.
     */
    to: string
    /**
     * Any number you bought from Twilio and can use for outbound communication.
     */
    from: string
    /**
     * Body of the SMS message.
     */
    body: string
  }

  interface IMessageResponseData {
    sid: string
    date_created: string
    date_updated: string
    date_sent: string | null
    account_sid: string
    to: string
    from: string
    body: string
    status: string
    num_segments: string
    num_media: string
    direction: string
    api_version: string
    price: string | null
    price_unit: string
    error_code: string | null
    error_message: string | null
    uri: string
    subresource_uris: { media: string }
  }

  interface ICallOptions {
    /**
     * Any number twilio can call
     */
    to: string
    /**
     * A Twilio number you own
     */
    from: string
    /**
     * A URL containing TwiML instructions for the call
     */
    url: string
  }

  type CallStatus = "queued" | "initiated" | "ringing" | "answered" | "completed"

  interface ICallResponseData {
    sid: string
    date_created: string
    date_updated: string
    parent_call_sid: string | null
    account_sid: string
    to: string
    formatted_to: string
    from: string
    formatted_from: string
    phone_number_sid: string
    status: CallStatus
    start_time: string | null
    end_time: string | null
    duration: string | null
    price: string | null
    direction: string
    answered_by: string | null
    api_version: string
    forwarded_from: string | null
    caller_name: string | null
    uri: string
    subresource_uris: {
      notifications: string
      recordings: string
    }
  }

  interface ITwilioClient {
    sendMessage(options: IMessageOptions, callback: ((err: Error, responseData: IMessageResponseData) => void))
    makeCall(options: ICallOptions, callback: ((err: Error, responseData: ICallResponseData) => void))
  }

  export class RestClient {
    constructor(accountSID: string, authToken: string)
    sendMessage(options: IMessageOptions): Promise<IMessageResponseData>
    makeCall(options: ICallOptions): Promise<ICallResponseData>
  }

  export function availablePhoneNumbers(country: string): any
  export function request(requestOptions: any, cb?: (error, responseData) => void)

  interface IWorkspaceTasks {
    create(options: any)
  }

  interface IWorkspaceWorkers {
    get(cb: (err, result) => void)
  }

  interface IWorkspaceStatistics {
    get(options: any)
  }

  interface IWorkspace {
    tasks: IWorkspaceTasks
    workers: IWorkspaceWorkers
    statistics: IWorkspaceStatistics
  }

  export class TaskRouterClient {
    workspace: IWorkspace;
    constructor(accountSID: string, authToken: string, workspaceSID: string)
  }

  export class TwimlResponse {
    say(message: string): this
    say(message: string, options: any): this
    pause(options: any): this
    play(soundURL: string): this
    /**
     * Exports the TwimlResponse to XML String.
     */
    toString(): string
  }
}

@mhegazy: Why was this closed? There didn't seem to be any resolution and I am experiencing the same issue.

I'm having the same issue.

If it helps, here's a repo where I'm having this issue.

I'm under the impression that typeRoots is ignored while resolving ambient modules and types. I have the following configuration:

{
  "compilerOptions": {
    "target": "es6",
    "jsx": "preserve",
    "strict": true,
    "moduleResolution": "node",
    "typeRoots" : ["./public"],
    "types": ["node"],
    "baseUrl": ".",
    "paths": {},
    "allowSyntheticDefaultImports": true,
    "skipLibCheck": true,
    "preserveConstEnums": true,
    "sourceMap": true,
    "experimentalDecorators": true
  },
  "include": ["src/**/*"]
}

and I can still see that the node modules types are resolved from @types/node/index.d.ts

Module 'http' was resolved as locally declared ambient module in file '[...]/node_modules/@types/node/index.d.ts'.

They should not be resolved since there is nothing under ./public even if the if node is specified in types, is that right?

Per https://github.com/Microsoft/TypeScript/issues/13581#issuecomment-273923793, it looks like typeRoots are intended for global type definitions only. If you import something, Typescript will ignore the typeRoots directory when resolving types for it.

The solution for this is to use paths instead:

{
  "compilerOptions": {
    "moduleResolution": "node",
    "module": "es6",
    "target": "es6",
    "baseUrl": "./",
    "paths": {
      "*" : ["my-type-roots/*"]
    }
  }
}

This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

CyrusNajmabadi picture CyrusNajmabadi  Â·  3Comments

Antony-Jones picture Antony-Jones  Â·  3Comments

siddjain picture siddjain  Â·  3Comments

kyasbal-1994 picture kyasbal-1994  Â·  3Comments

Roam-Cooper picture Roam-Cooper  Â·  3Comments