Sam
Sam

Reputation: 4339

Importing a module breaks local types with Property does not exist on type

I have an enumeration called CarType, which is in a file called CarType.ts. The code is as follows:

namespace MyProduct.Search {
    export enum CarType {
        Sedan = 0,
        Wagon = 1,
        Cabrio = 2,
        /* etc */
    }
}

In another file, called SearchMain.ts in the same directory, I have the following code:

/// <reference path="../../d.ts/jquery.d.ts" />
/// <reference path="../../d.ts/knockout.d.ts" />
/// <reference path="../../d.ts/fullcalendar.d.ts" />
namespace MyProduct.Search {
    export module SearchMain {
        export function Init() {

             /* snip */
             let type: CarType = CarType.Sedan;

        }
        /* snip */
    }
}

The above works fine, until I try to import a Full Calendar type. As soon as I add the following line, beneath the /// reference lines

import { EventObjectInput } from 'fullcalendar/src/types/input-types';

CarType becomes unavailable. The Intellisense error is Property 'CarType' does not exist on type 'typeof Search'.

How can I use the EventObjectInput from Full Calendar with my code?

Upvotes: 2

Views: 3028

Answers (1)

Aviad Hadad
Aviad Hadad

Reputation: 1717

This is a confusing one:

Typescript has internal modules or namespaces, identified by the keywords namespace and module. Those two keywords have the same meaning. Which is the confusing part, since module doesn't actually mean a JS module. Namespaces are basically just syntactic sugar for implementing the "revealing module pattern". This the type of stuff we used to do before we had actual modules in javascript, and it's usage is discouraged.

I recommend looking at Typescript playground to understand what the namespace keyword actually does when it's transpiled to javascript. (example)

Basically, namespace just creates an object, where you can control what is seeing outside the object. Another property is if you have two namespace declarations, with the same name on the same scope, the declarations are merged. But at the end of the day it's implemented as just another variable in the transpiled javascript.

Typescript also has "real" modules, also called external modules. Real modules meaning they are isolated, and you need some sort of mechanism to load them at runtime. It could be webpack or browserify, it could be require function if your code is meant to run in node.js, could be reuqire.js library, but you need something.

Any file that has top-level export or import statements is a module. (export inside namespace is not top-level!)

So, we have two options: 1. a file is not a module. 2. a file is a module.

If a file is not a module, every variable/type declared in the file is declared on the global scope.

If a file is a module, every variable/type is scoped just to that file.

So, once you added: import { EventObjectInput } from 'fullcalendar/src/types/input-types'; you turned your SearchMain.ts into an external module. And now the namespace MyProduct.Search in SearchMain.ts is isolated, defined in a different scope then the one defined in CarType.ts. So you get your error.


TL;DR - what you can do?

It's very annoying to mix internal modules and external modules. Pick one. It's greatly recommended to use just external modules. Don't use namespace, just import and export statements. It might take a while to migrate the code, but it's worth it.

The other option is to continue using namespace. Don't import EventObjectInput. Make it available on some other namespace. But again, it's not recommended.


for more info from Typescript documentation

related stack overflow answer

Upvotes: 1

Related Questions