I'm trying to define the TypeScript type definitions for the StarUML tool. I've managed to make it work for a good part of the API but I'm getting stuck on the following problem: how to make the link between a (JavaScript) global variable ("type" below) and a TypeScript namespace containing classes?
(A) The problem to solve
StarUML makes available a global variable type
that register hundreds of class coming from unknown places. For instance type.Element
is a class (not an Element!) as well as type.Model
. We use these two types as an example below:
- In JavaScript, these classes are mostly used in statements like
if (x instanceof type.Element)
. - With TypeScript, I want to be able to define signatures like
f(e : type.Element)
(I would be happy to remove the type prefix but that's another story) and want to have intelli-sense for expressions likemyElement._id
(_id
is an attribute of the classElement
).
(B) First attempt: modeling "type" as a variable
I first tried to define type as being a variable (as this is actually a variable):
// type.d.ts
declare class Element {
_id: string
// ...
}
declare class Model extends Element {
name: string
// ...
}
declare const type = {
"Element" = Element,
"Model" = Model
// ...
}
This does not work, as it produces the following error:
S1254: A 'const' initializer in an ambient context must be a string or numeric literal or literal enum reference
I mention this solution because it makes it clear what type
is all about: a register that gives for each class name (string), and the class itself. The classes are defined somewhere else in an unknown place.
(C) Second attempt: modeling "type" as a Namespace.
After reading the TypeScript documentation, and after various trials, I came up with the following TypeScript file types.d.ts
(That might be where I'm wrong).
// types.ts
export namespace type {
class Element {
_id: string
// ...
}
class Model extends Element {
name: string
}
// ...
}
(D) Client code
Below is an example code (main.ts
) that uses this API definition.
To simplify the file type.d.ts
and main.ts
are at both at the top level.
// (1) /// <reference path="./types.d.ts" />
// (2) import {type} from "./types"
// (3) declare var type
function hello(m: type.Element): void {
console.log(" hello: (" + e._id + ')')
}
console.log(type)
console.log(type.Element)
I don't manage to "make it work" I've tried various combinations uncommenting some of the first 3 lines (see below).
(D.2) My expectations
- (a) the type in the
function hello
should be defined properly (TypeScript) - (b) the intelli sense should work on the next line
e._id
(TypeScript) - (c) the last line should display the
type.Element
class (JavaScript)
I can't make it work all at the same time, irrespective of the "importing" first lines.
(D.3) What I got
(1) I didn't manage to make line (1)
/// <reference ...
"work" at all. I also tried solutions provided in forums such as usingtsconfig
withtypeRoots
andpaths
. I don't know if the solution should come from there.(2)
import {type} ...
is ok for the namespace but then lineconsole.log(type.element)
return undefined at runtime.(3)
declare var type
makes the JavaScript code run ok, but in conflict with (2).
When (2) and (3) are present at the same time, a TypeScript error is generated because of conflict between type
as namespace and type
as variable.
(D.4) What is the solution?
After reading the TypeScript documentation and some other blogs I'm still confused. I don't know if the problem is in my approach (C), "modeling the variable type" as a namespace, or I don't know how to call this namespace/variable at compilation/runtime.