0

I am learning typescript and I am facing a problem when I try to define this class in a typescript definition file, and then use it in a typescript file.

I have a javascript "class" which is "Facade" (well known design pattern) of the OpenLayers Library. This facade is like that :

lib/carto-facade/OpenLayerFacade.js :

function OpenLayerFacade() {
    this.map =   new ol.Map({
        layers: [
            new ol.layer.Tile({source: new ol.source.OSM()})
        ],
        view: new ol.View({
            center: [43.5, 5.0],
            zoom: 2
        }),
        target: 'map'
    });
}

OpenLayerFacade.prototype.setViewCenter = function(latitude, longitude)  {
    this.map.getView().setCenter(ol.proj.fromLonLat([longitude, latitude]));
}

Then, I would like to be able to use this facade in a typescript project. So I wrote my .d.ts file as follow :

lib/carto-facade/OpenLayerFacade.d.ts

declare interface OpenLayerfacade {
    setViewCenter(latitude:number, longitude:number):void;
}

declare interface OpenLayerfacadeFactory {
    new(divName:string): OpenLayerfacade;
}

export var OpenLayerfacade:OpenLayerfacadeFactory;

Now I want to use it in a TS script loaded by a browser :

/// <reference path="../typings/jquery/jquery.d.ts" />
/// <reference path="../lib/carto-facade/OpenLayerFacade.d.ts" />

import oli = require('../lib/carto-facade/OpenLayerFacade');

$(document).ready(function () {
    var ol = new oli.OpenLayerFacade("map");
    ol.setViewCenter(43.5, 6.0);
    console.log("Map displayed");
});

Everythings compile (oups, tranpile) fine, but when I load my web page containing the script I have the following error :

Uncaught TypeError: Cannot read property 'OpenLayerFacade' of undefined

This occurs when I try to instantiate a new "OpenLayerFacade".

I am using require.js, so I transpile with the option "module": "amd" in my tsconfig.json

My HTML File is :

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <script src="/bower_components/jquery/dist/jquery.min.js"></script>
    <script src="/node_modules/openlayers/dist/ol.js"></script>
    <script src="/lib/carto-facade/OpenLayerFacade.js"></script>
    <script data-main="/test_ts/cartotester" src="/bower_components/requirejs/require.js"></script>
</head>
<body>
<div id="map" style="width: 75%; height: 400px"></div>
</body>
<input type="number" id="latitude" placeholder="43.5000"><br>
<input type="number" id="longitude" placeholder="5.5000"><br>
</html>

What am I doing wrong ?

I am sure that answer of my question is somewhere on the Typescript Handbook but if someone can help me to find where, I would really appreciate.

Regards,

Adrien BARRAL
  • 3,474
  • 1
  • 25
  • 37

2 Answers2

1

This code:

import oli = require('../lib/carto-facade/OpenLayerFacade');

means you are importing external module. But your JS code is not external module (you could write such in pure JS if needed). Simple solution should be just omit that line in your TS code. Then you must ensure that JS code creates global variable OpenLayerfacade (as declared in d.ts) in runtime - you are responsible for loading the JS code. Then in TS use declared global variable.

/// <reference path="../typings/jquery/jquery.d.ts" />
/// <reference path="../lib/carto-facade/OpenLayerFacade.d.ts" />

$(document).ready(function () {
    var ol = new OpenLayerFacade("map");
    ol.setViewCenter(43.5, 6.0);
    console.log("Map displayed");
});

That's how would do it without commonjs - as internal modules aka namespaces.

In case you are using commonjs - and want to use external modules - then your JS code must be commonjs module (external module in terms of TypeScript). Also your declaration file must be wrapped in module to indicate that to compiler.

export modules 'oli' {
    declare interface OpenLayerfacade {
        setViewCenter(latitude:number, longitude:number):void;
    }

    declare interface OpenLayerfacadeFactory {
         new(divName:string): OpenLayerfacade;
     }

    export var OpenLayerfacade:OpenLayerfacadeFactory;
}

and import it in commonjs way:

import oli = require('oli');

You still need

/// <reference path="../lib/carto-facade/OpenLayerFacade.d.ts" />

to tell compiler where to search for 'oli' module (can be simplified with tsc >1.6).

If your JS snippet is accurate and you don't want or can't change JS to commonjs module you still can mix it in your application using commonjs. Just use first described approach with namespaces.

ahz
  • 940
  • 1
  • 6
  • 12
1

When using commonjs, an Ambient External Module definition is required. Check this doc: http://www.typescriptlang.org/Handbook#writing-dts-files (§ Ambient External Modules)

There is a way of creating definitions that work for both Internal Modules and External Modules. Something along the lines of (I personnally would have described a class)

//Internal module
declare namespace Oli {
    class OpenLayerfacade {
        constructor(divName:string);
        setViewCenter(latitude:number, longitude:number):void;
    }
}

//External module
declare module "oli" {
    export = Oli
}

Then import (new 1.6 syntax)

import { OpenLayerfacade } from 'oli'
Bruno Grieder
  • 28,128
  • 8
  • 69
  • 101
  • Should I transform my JS code to become an external module importable with require.js as suggested by @ahz ? – Adrien BARRAL Oct 29 '15 at 14:54
  • Yes and make it support all formats so it can be reused "anywhere": http://stackoverflow.com/questions/13673346/supporting-both-commonjs-and-amd (check the highest rated answer) – Bruno Grieder Oct 29 '15 at 15:11
  • It is a little confusing that you are using require.js with commonjs as require.js uses AMD by default. Still if you are considering transforming JS to became external module transform it to TS - then module transformation is just compile step... – ahz Oct 29 '15 at 18:23