jsjslim jsjslim - 3 months ago 34
TypeScript Question

Trouble getting typings to work in Typescript

Edit: I rewrote the post to better explain the steps I've taken and the issues I'm facing

I started out with a simple html/js page.
I'm also using

xregexp
from a cdn.



var reg = XRegExp("^lights:(?<option>on|off)$", "xgi");
var match = XRegExp.exec("lights:on", reg);
document.body.innerText = "Lights are: " + match["option"];

<!DOCTYPE html>
<html lang="en">
<head></head>
<body></body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/xregexp/3.1.1/xregexp-all.js"></script>
<script src="app.js"></script>

</html>





Next, I want to convert the js file to typescript. So I:


  • rename
    .js
    to
    .ts

  • added
    declare var XRegExp:any;
    to the top of the
    ts
    file (described here)

  • ran
    tsc --init
    to create tsconfig file

  • ran
    tsc



So far, Typescript compiles fine, and page is still working.

Next, I want to replace the
declare var XRegExp:any
with an interface, one from a community-maintained declaration file.

I install the typings for
xregexp
via
typings install dt~xregexp -SG
(d.ts file link)

But I'm unable to access the interfaces declared in the
.d.ts
file, because the declaration uses
declare module 'xregexp'
and
export =
.

According to Typescript's documentation on Modules,
export =
is used with
import = require()
. But that involves module loading, which is not what I want.

I've tried Pelle's suggestion, but still have no idea how to declare an ambient variable using it.

declare var XRegExp:xregexp.OuterXRegExp; // <-- TS error: Module 'xregexp' has no exported member 'OuterXRegExp'


I would like to know:


  1. The
    d.ts
    declaration uses
    declare module "xregexp"
    . Can I refer to any of its inner interfaces/types without an
    import
    ?

  2. is it possible for me to create an additional
    d.ts
    to declare the ambient variable? (like what
    dt~react-global
    does) How do I do this, without modifying the downloaded
    d.ts
    ?


    • reason: I would rather not change the declaration file pulled via
      typings
      in case of future changes.




At this point, I'm not doing Node development, nor do I want to set up a module loader and/or browserify/webpack.

I simply want to convert an already-working js file to ts, and would like to make Typescript aware of an ambient variable
XRegExp
, which has an interface as specified in the downloaded
d.ts
. Is it possible for me to achieve this without too much ceremony?

[Edit]

So, I rewrote the declaration file, using
jquery.d.ts
as a guide.

This appears to work when loading via script or import, but doesn't answer my original question.



interface TokenOpts {
scope ? : string;
trigger ? : () => boolean;
customFlags ? : string;
}

interface XRegExp {
(pattern: string, flags ? : string): RegExp;
(pattern: RegExp): RegExp;

addToken(regex: RegExp, handler: (matchArr: RegExpExecArray, scope: string) => string, options ? : TokenOpts): void;

build(pattern: string, subs: string[], flags ? : string): RegExp;
cache(pattern: string, flags ? : string): RegExp;
escape(str: string): string;
exec(str: string, regex: RegExp, pos ? : number, sticky ? : boolean): RegExpExecArray;
forEach(str: string, regex: RegExp, callback: (matchArr: RegExpExecArray, index: number, input: string, regexp: RegExp) => void): any;
globalize(regex: RegExp): RegExp;

install(options: string): void;
install(options: Object): void;

isInstalled(feature: string): boolean;
isRegExp(value: any): boolean;
match(str: string, regex: RegExp, scope: string): any;
match(str: string, regex: RegExp, scope: "one"): string;
match(str: string, regex: RegExp, scope: "all"): string[];
match(str: string, regex: RegExp): string[];
matchChain(str: string, chain: RegExp[]): string[];
matchChain(str: string, chain: {
regex: RegExp;
backref: string
}[]): string[];
matchChain(str: string, chain: {
regex: RegExp;
backref: number
}[]): string[];
matchRecursive(str: string, left: string, right: string, flags ? : string, options ? : Object): string[];

replace(str: string, search: string, replacement: string, scope ? : string): string;
replace(str: string, search: string, replacement: Function, scope ? : string): string;
replace(str: string, search: RegExp, replacement: string, scope ? : string): string;
replace(str: string, search: RegExp, replacement: Function, scope ? : string): string;
replaceEach(str: string, replacements: Array < RegExp | string > []): string;

split(str: string, separator: string, limit ? : number): string[];
split(str: string, separator: RegExp, limit ? : number): string[];

test(str: string, regex: RegExp, pos ? : number, sticky ? : boolean): boolean;

uninstall(options: Object): void;
uninstall(options: string): void;

union(patterns: string[], flags ? : string): RegExp;
version: string;
}

declare module "xregexp" {
export = XRegExp;
}

declare var XRegExp: XRegExp;




Answer

This is not really a typescript problem: if you want to use npm packages, it is strongly recommended to use a dependency manager like browserify or webpack to them. (I would recommend browserify, if you're only looking for something to handle your modules).

If you really don't want to use the amazing power of npm, there is also a hacky way of solving this specific problem: changing the declaration file to declare a namespace instead of a module. Now you can effectively use the types anywhere in your project.

Just make these two changes to your declaration file, and you should be good to go:

  • declare a namespace instead of a module:

    declare module 'xregexp' {
    //becomes
    declare namespace xregexp {
    
  • get rid of the export on the bottom of the page:

    // remove this: 
    export = OuterXRegExp;
    

Now you can call anywhere in your project. Eg:

var outerxregexp: xregexp.OuterXRegExp.TokenOpts = { trigger: () => true }

But again, it is strongly recommended to pull in browserify or so