Search code examples
typescriptrequirehandlebars.jsbrowserify

Handlebars Templates and Browserify with TypeScript


I'm in the process of porting a project to TypeScript. The project combines Handlebars and Browserify with hbsfy to handle templates. This means I have JavaScript code like this:

var photosTemplate = require('./views/photos.hbs');

Which requires in a template file called "photos.hbs" from a views folder which looks a little like this:

{{#each this}}
    <span class="title">{{title}}</span>
{{/each}}

There's a gulpfile that runs as part of the build step to convert the template into a function that can be used. This means that in my consuming file I can write code like this which uses the function template:

var newHtml = photosTemplate([{title: "test title 1"}, {title: "test title 2"}]);

The question is, how can this be made to work for TypeScript? I've switched my var for an import and moved to using the --module commonjs compiler flag:

import photosTemplate = require('./views/photos.hbs');

At this point I encounter the error:

error TS2307: Cannot find external module './views/photos.hbs'.

Fair enough you might think. So I created a photos.hbs.d.ts to sit alongside that looks like this:

interface photos {
    (context: any, options?: any): string;
}

export = photos;

This is a simple interface that illustrates how the photos template is being used. However, with this in place the compiler yields the error:

error TS2304: Cannot find name 'photosTemplate'.

There's a similar question here: https://stackoverflow.com/a/23957928/761388 I think the problem is my photos.hbs.d.ts file but I'm not sure what's wrong with it. I feel like I'm missing something very simple in the definition to do with external modules. But for the life of me I can't work out what it is...


Solution

  • This worked for me...

    photos.hbs.d.ts

    declare function photos (context: any, options?: any): string;
    
    export = photos; 
    

    Using it:

    import photosTemplate = require('./views/photos.hbs');
    
    photosTemplate("");
    

    The reason being you want to actually call the function, not just define a signature.

    You could also do:

    interface Photos {
        (context: any, options?: any): string;
    }
    
    declare var photos: Photos;
    
    export = photos;