Unable to define CommonJS module of prototype extensions

Topics: General
Mar 5, 2014 at 10:15 AM
Hi,

I have the following simple requirement. I'd like to create a module for use in NodeJS that contains the following string extension.

// stringEx.ts

export declare class String {

    endsWith(substring: string): boolean;
}

String.prototype.endsWith = function (suffix) {
    return this.indexOf(suffix, this.length - suffix.length) !== -1;
};
Then I'd like to import this in another file in order to make the string extension available:
import str = require('./stringEx');

var endsWithFoo = 'bar-foo'.endsWith('foo');
However, this errors with 'The property endsWith does not exist on value of type 'string'.

The JavaScript equivalent of this works correctly, but no matter what I try, I get one error or the other.

Is there a way to get this working or is this a missing feature?

Thanks.
Coordinator
Mar 5, 2014 at 4:18 PM
Edited Mar 5, 2014 at 4:49 PM
You may have to split out the change to String as a separate .d.ts file you ///<reference...> Modules tend to create their own namespaces and scopes in TypeScript, which might be just enough to throw off this pattern.
// StringExtension.d.ts
interface String {
    endsWith(substring: string): boolean;
} 
///<reference path="StringExtension.d.ts"/>
// stringEx.ts

export var tmp;  // had to cheat so this is still an external module

String.prototype.endsWith = function (suffix) {
    return this.indexOf(suffix, this.length - suffix.length) !== -1;
};
///<reference path="StringExtension.d.ts"/>
import str = require("./stringEx");

var endsWithFoo = "bar-foo".endsWith("foo");
Mar 5, 2014 at 4:55 PM
@jon, thanks for the response.

This was one of the things I tried. However, there is a subtlety here. If you were to F12 on the endsWith in the last of your code snippets it actually takes you to the interface declaration in file StringExtension.d.ts.

This in turn causes the compiler to infer endsWith as a compile-time annotation, with the end result the generated JavaScript file looks like this:
   var endsWithFoo = 'bar-foo'.endsWith('foo');
Notice that there is no 'var str = require('./app');` since the import has been optimised away.

Perhaps this has been logged already, but I couldn't find a bug ticket for.
Coordinator
Mar 5, 2014 at 7:23 PM
Ahh, I see. For something like this where you're modifying globals instead of creating and using a module, you're right that you'll have to do something like this as a workaround:
import str = require('./app');
str;