6

I have a very small node Typescript project with the following structure:

App structure

When attempting to import the "diff" module in my index.ts file like so:

import * as diff from 'diff';

Atom-typescript suddenly loses the ability to locate my "fgtApp.Interfaces" namespace:

Loses autocomplete of internal namespace

As soon as I remove the import statement, node is able to resolve the "fgtApp.Interfaces" namespace no issue like so:

Autocomplete working properly without import declaration

Is this a bug in atom-typescript or a bug in my understanding of the way importing these external modules work?

3
  • 1
    Because it becomes a file module : basarat.gitbooks.io/typescript/content/docs/project/… Commented Sep 13, 2015 at 23:53
  • @basarat so there's no way this SHOULD work? Seems like it would really help keep code tidy. I've written a similar question here: stackoverflow.com/questions/34852909/… Commented Jan 18, 2016 at 16:30
  • @askanison4 possible workaround could be removing import * as diff from 'diff'; and using diff module like diff.property Commented Jul 3, 2017 at 11:44

1 Answer 1

6

This isn't specifically a problem with atom-typescript. The atom-typescript plugin uses the official TypeScript language service (Same as Visual Studio), so this would be a problem in any editor.

The trouble is that once you have an import or export specified, the file becomes a module ("external module" pre-TypeScript 1.5). This means that when the import * as diff from 'diff'; is present, things declared in index.ts become local only and don't consider/merge into the global namespace.

TypeScript spec, section 11.1: ...source files that contain at least one external import declaration, export assignment, or top-level exported declaration are considered separate external modules. Entities declared in an external module are in scope only in that module, but exported entities can be imported into other modules using import declarations

When you're not using external modules, TypeScript allows namespaces in different files to build upon each other. Once you start using external modules, this is no longer the case without using workarounds. In this case, it's often best to just switch to using external modules - if you're making a Node project, this is particularly easy because you don't have to worry about bundling.

Instead of a "deep namespace" (e.g. fgtApp.category.thing) like C# and .NET encourage - start thinking about each TypeScript source file as its own module. If you really do want a hierarchy, implement one with a folder structure.

This code will work as you expect even with noImplicitAny active:

interfaces.d.ts

// Notice that this is a d.ts file.  Since it will only contain interfaces,
//  making it a d.ts file means TypeScript doesn't have to worry about
//  emitting it and you also can't accidentally put executable code here.
export interface IFgtService {
    authenticateDisable: boolean;
    failedAttempt: boolean;
    authenticate: (username: string, password: string) => boolean;
}

export interface IAnotherInterfaceAsAnExample {
    isCool: boolean;
}

service.ts

// since there is no "relative path", diff will come from node_modules.
import * as diff from 'diff';
// since there IS a relative path, interfaces will come from ./interfaces.d.ts
import * as interfaces from './interfaces';

// You can still use namespaces inside an "external module", but mainly they
//  serve as a convenient way to bundle stuff for a one-line export (see
//  the last line of this file).
namespace Service {
  export class FgtService implements interfaces.IFgtService {
    authenticateDisable = true;
    failedAttempt = true;
    authenticate = (username: string, password: string) => {
      let d = diff.d;
      return true;
    }
  }
}
export = Service;

index.ts

import {FgtService} from './Service';

const myService = new FgtService();
console.log(myService.authenticateDisable);
Sign up to request clarification or add additional context in comments.

2 Comments

Out of curiosity; you mention workarounds. What would they be? NOTE: I've raised a similar question here: stackoverflow.com/questions/34852909/…
An example of a workaround might be writing all of your code in the global namespace manner and then writing a preprocessor to merge namespaces together under a set of modules in the external style. I didn't mean to indicate that this would be easy or well-tooled.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.