69

I have two classes in two separate files and one extends from another. The base class contains some import statements using node modules. It is unclear to me why the derived class (which is in a separate file) does not recognize the base class!!!???

Can someone clarify this please?

// UtilBase.ts

/// <reference path="../typings/node.d.ts" />
/// <reference path="../typings/packages.d.ts" />

import * as path from "path"; // <---- THIS LINE BREAKS THE BUILD!!!!

namespace My.utils {

    export class UtilBase {

        protected fixPath(value: string): string {
            return value.replace('/', path.sep);
        }
   }
}

And then

// UtilOne.ts
/// <reference path="UtilBase.ts" />

namespace My.utils {

    export class UtilOne extends My.utils.UtilBase {

    }
}

After compiling I get:

src/UtilOne.ts(6,47): error TS2339: Property 'UtilBase' does not 
exist on type 'typeof utils'
Paleo
  • 21,831
  • 4
  • 65
  • 76
gevik
  • 3,177
  • 4
  • 25
  • 28
  • See also: http://stackoverflow.com/questions/37295778/how-to-use-a-class-from-the-same-module-or-namespace-in-typescript/37297485#37297485 – Paleo Jun 01 '16 at 10:18
  • I am not sure where the answer might be in the post, but thanks for replying. – gevik Jun 01 '16 at 12:23
  • `import * as X from ...` helped me. See https://stackoverflow.com/a/39473101/470749 – Ryan Apr 15 '23 at 20:24

1 Answers1

65

A solution with namespaces (not recommended)

To resolve your issue, you can export your namespace:

// UtilBase.ts
import * as path from "path";
export namespace My.utils {
    export class UtilBase {
        protected fixPath(value: string): string {
            return value.replace('/', path.sep);
        }
   }
}

Then, you should be able to import it:

// UtilOne.ts
import {My} from './UtilBase';
namespace My.utils {
    export class UtilOne extends My.utils.UtilBase {
    }
}

However, if the purpose is to organize the code, it is a bad practice to use namespaces and (ES6) modules at the same time. With Node.js, your files are modules, then you should avoid namespaces.

Use ES6 modules without namespaces

TypeScript supports the syntax of ES6 modules very well:

// UtilBase.ts
import * as path from "path";
export default class UtilBase {
    protected fixPath(value: string): string {
        return value.replace('/', path.sep);
    }
}

// UtilOne.ts
import UtilBase from './UtilBase';
export default class UtilOne extends UtilBase {
}

It is the recommended way. ES6 modules prevent naming conflicts with the ability to rename each imported resource.

It will work on Node.js.

For a good introduction to the ES6 modules syntax, read this article.

Use a file tsconfig.json instead of /// <reference

Notice: The syntax /// <reference is replaced by the file tsconfig.json. An example for Node.js:

// tsconfig.json
{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es6"
  },
  "exclude": [
    "node_modules"
  ]
}
Paleo
  • 21,831
  • 4
  • 65
  • 76
  • 6
    Thanks. After reading about a dozen posts and answers about how to use namespaces and modules, finally a post that makes things clear. – Krisztián Balla Jul 25 '17 at 09:43
  • 9
    How can you prove that `it is a bad practice to use namespaces and (ES6) modules at the same time`? – Pavel_K Sep 29 '18 at 16:14
  • 11
    @Pavel_K [In the TypeScript handbook](https://www.typescriptlang.org/docs/handbook/namespaces-and-modules.html#needless-namespacing): _"To reiterate why you shouldn’t try to namespace your module contents, the general idea of namespacing is to provide logical grouping of constructs and to prevent name collisions. Because the module file itself is already a logical grouping, and its top-level name is defined by the code that imports it, it’s unnecessary to use an additional module layer for exported objects."_ – Paleo Sep 29 '18 at 17:27
  • In a web app, you generally want to bundle everything in a small number of scripts... so namespaces make sense there. – Yepeekai Feb 15 '19 at 20:30
  • 3
    @Paleo Then the whole namespace solution in typescript is kind of messy. The namespace and the module border should be the same. It should help remove the messy import/export story not not add some additional complexity. – minus one Feb 12 '21 at 11:02
  • I think namespaces are a great replacement for nested classes – Paul May 04 '21 at 12:10
  • @Paul In JavaScript (and TypeScript), classes can be nested. And a namespace can't be instantiated several times, it is not a class. – Paleo May 04 '21 at 14:37
  • @Paleo Oh, I didn't know that. Thanks. Here is a link to nested classes: https://stackoverflow.com/a/32494175/10343490 – Paul May 05 '21 at 07:22