If I define some class:
class MyClass {
static myVar = { someKey: someVal };
...
}
but instead of defining the static variable from within the class, I want to import it from another file:
// utils.js
export const someObject = { someKey: someVal };
...
Will this work?:
import { someObject } from './utils.js';
class MyClass {
static myVar = someObject;
...
}
edit: The title to this question would more accurately be: "Can I define an ES6 static field from an imported object?" Static fields are currently a stage-2 proposal for JS. See #T.J.Crowder's answer below. and require the "babel-preset-stage-2" transpiler.
That's not a static method, it's a static field (which isn't standard yet; static fields are currently at Stage 2, although it's common to transpile the syntax as the basics of it seem unlikely to change).
But provided the semantics when static fields are standardized are as they currently stand (and it would be weird if they weren't), yes, you can do that. The initializer of a field can be any expression, including one that uses an imported binding.
If you want to do it now, without transpiling or waiting for static fields to become standard, just do the assignment afterward:
import { someObject } from './utils.js';
class MyClass {
// ...
}
MyClass.myVar = someObject;
Related
How do I import function from a typescript interface?
In the example below, how to call foo from another file?
namesapce a{
export interface b{
foo():number;
}
class c implements b{
public foo():number{...}
}
}
I tried with import = and import require and import {} from as but nothing works.
I get not a module and imported type used as a value but from the solutions I see it seems like I need to add an export or change code that I cannot change.
You cannot use import or export here. The instance method foo belongs to the class c, which is a member of the namespace a inside the global script scope (so the file is not a module).
Also c as containing class is not exported, so foo is not public as well. The only namespace member available is interface b, which just contains type declarations for c and no implementation (b will be erased at runtime).
In general, a namespace a inside the script scope can be used like this (no import):
a.c; // implicit global
window.a.c; // (explicit window global)
globalThis.a.c // environment independent global (e.g. for node and browser)
Assuming, you want to use namespaces and change the export of c, foo can be invoked with following code snippet - paste that into a file without import/export at top-level:
namespace a {
export interface b {
foo(): number;
}
export class c implements b {
public foo(): number {
return 42;
}
}
}
const cInstance = new a.c()
cInstance.foo()
Even though Namespaces are simply named JavaScript objects in the global namespace. from typescript documentation you can revise your code and use it as follows (let's say this code exists in namespace-a.ts file)
Example 1:
export namespace a {
export interface b {
foo(): number;
}
export class c implements b {
public foo(): number {
console.log('calling foo from class c');
return 123;
}
}
}
and in another file you can import it like this
import * as namespaceA from '../location-to-file/namespace-a';
and in your code you can use it like following:
let testVar = new namespaceA.a.c();
testVar.foo();
NOTE: The above is considered a bad practice if you read this from the documentation.
To reiterate why you shouldn’t try to namespace your module contents, the general idea of namespacing is to provide logical grouping of constructs and to prevent name collisions. Because the module file itself is already a logical grouping, and its top-level name is defined by the code that imports it, it’s unnecessary to use an additional module layer for exported objects.
You should just revise your code as follows. In your file fileBC.ts:
Example 2:
export interface b {
foo(): number;
}
export class c implements b {
public foo(): number {
console.log('calling foo from class c');
return 1;
}
}
and in another file use it as follows:
import * as fileBC from '../location-to-file/fileBC';
let testVar = new fileBC.c();
testVar.foo();
P.S.: Sorry for the bad names I use for files and variables but you can get the point. I hate my variable naming. I hope it helped.
I wanted to create type definitions of a library that exists just in form of javascript.
Being quite new I read the documentation on the official manual along with all examples.
There is a case, though, that I don't really understand:
The library offers the programmer a global variable, foo that contains both classes and references to instances, so that one could, for example, trigger a search by using (in javascript)
var filter = new foo.Filter();
var results = foo.search.performSearch(filter);
the same foo, however, is also available in the window object so using window.foo.[...] helds the same results.
How can I represent this fact?
I initially started by defining the following (by basically copying what's in the library's API reference):
interface FooObject {
foo: Foo;
}
interface Window extends FooObject { }
interface Foo {
search: Search;
}
interface Search {
performSearch(): void;
}
declare const foo: Foo;
This way the syntax foo.search.performSearch() is accepted and so is window.foo.search.performSearch().
The only ways (that I read about) to be able to represent the first part, new foo.Filter(), would be to
declare namespace /*or module*/ foo {
class Filter {
// [...]
}
}
But namespaces are also declared as values, so this overrides the first foo definition and the compiler complains. Plus, even if it worked, I don't think that new window.foo.filter() would behave as expected and would surely throw a compiler error.
Of course I could just do the following
declare namespace /*(or module)*/ foo {
const search: Search;
interface Search {}
class Filter {}
}
without declaring the const foo: Foo or the class Foo but I have no way in a d.ts file to assign the module to window.foo, the compiler complains.
Plus this is not a solution that allows me to stick to the API Doc of the library that explicitly tells what I explained above about the hierarchy of Window, having the Foo class etc.
How can I represent this structure?
You represent a class by its constructor.
interface FooObject {
foo: Foo;
}
interface Window extends FooObject { }
interface Foo {
search: Search;
Filter: new () => Filter;
}
interface Filter {
}
interface Search {
performSearch(filter: Filter): void;
}
declare const foo: Foo;
Is there a way to write a Typescript definition for an ES6 mix-in?
I've this pattern in library.js, and I'd like to create the library.d.ts
// declaration in `library.js`
class Super extends Simple {
constructor() {}
static Compose(Base = Super) {
return class extends Base {
// ...
}
}
}
// usage in `client.js`
class MyClass extends Super.Compose() {}
let myInstance = new MyClass();
class MyOtherClass extends Super.Compose(AnotherClass) {}
No, Typescript type system is not expressive enough for that - see the discussion in https://github.com/Microsoft/TypeScript/issues/7225 and https://github.com/Microsoft/TypeScript/issues/4890.
The idiomatic 'type of classes' in typescript is written as
interface Constructor<T> {
new (...args): T;
}
So one way to write declaration for Compose is
export declare class Simple {}
export declare class Super extends Simple {
static Compose<T>(Base?: Constructor<T>): Constructor<T & {/*mixed-in declarations*/}>
}
That is, Compose return type is declared to be a constructor for intersection type - a type which must have all the properties of parameter (Base) together with all the properties of the mixin.
You can use that declaration (assuming it's in the library.d.ts file) like this
import {Super} from './library'
let MyComposed = Super.Compose(Super)
let myInstance = new MyComposed
The minor inconvenience is that you always have to provide argument for Super.Compose() because type inference does not work without knowing the value for default parameter, and you can't provide value for default parameter in the declaration file.
But the big problem is that you can't really use the result of Compose as a class:
class MyClass extends Super.Compose(Super) {}
does not compile due to the issues mentioned above:
error TS2509: Base constructor return type 'Super & {}' is not a class or interface type.
This question already has an answer here:
ES6 classes : what about instrospection?
(1 answer)
Closed 6 years ago.
I just can't find out where the references to declared ES6 classes are stored, I would have expected them in the window Object, bit they don't appear there.
I don't think it's a duplicate of ES6 classes : what about instrospection? since he is asking for a existance check of a class, what I want is a list of available classes.
For example:
class Test {
constructor() {
}
}
window.Test // undefined
What I want is a list of all classes that extend a class of mine
To clarify that I have a structure that looks something like this:
class Base {
constructor(components) {
for(let component of components) {
window[component](); // window.Test2 not defined
}
}
start() {
new this();
}
}
class Test2 extends Base {
constructor() {
super();
}
}
class Test extends Base {
constructor() {
super(['Test2','Test2']);
}
}
Test.start();
That's just an abstraction of my structure, in short I have to use strings at super(['Test2', 'Test2'])
At the moment I'm doing something like this
Base.register(Test2);
for every class and I want to get rid of that.
You can use Class expressions to store them in some sort of array, although I probably wouldn't do it, if I were you.
var Test = class Test {
constructor() {
}
}
allClasses.push(Test);
JavaScript classes are introduced in ECMAScript 6 and are syntactical sugar over JavaScript's existing prototype-based inheritance. The class syntax is not introducing a new object-oriented inheritance model to JavaScript. JavaScript classes provide a much simpler and clearer syntax to create objects and deal with inheritance.
Basically ES6 classes are compiled to plain old-fashioned Javascript functions. You may "store" them at the window object but this is a major pitfall as you are killing the whole module patter ES6 introduced.
If you want sort of a "module" of classes, you could theoretically do something like this:
// some-class.js
export default class SomeClass {}
Then:
// another-class.js
export default class AnotherClass {}
And your entry file:
// index.js
import SomeClass from './some-class.js' // extensions optional; here just for clarity
import AnotherClass from './another-class.js'
export default {
SomeClass,
AnotherClass
}
If you have all of those embedded in the same directory (we'll call the directory example), you can just import that entire module wherever you need it:
// something-that-requires-your-module.js
// this will by default enter through the `index.js` file of your `example` directory
import Example from './example';
console.log(Example.SomeClass);
console.log(Example.AnotherClass);
From within an existing Dojo widget, I want to create an instance of some other widget, which has been transpiled from TypeScript. Sounds simple -- but for some reason the transpiled widget is not "newable", unless I specify the classname twice. I can't update all of that existing code, so I need the transpiled widget to be "newable".
Here is the very simple MyTypeScriptWidget.ts:
import _WidgetBase = require("dijit/_WidgetBase");
export class MyTypeScriptWidget extends dijit._WidgetBase
{
constructor(params?: Object, srcNodeRef?: HTMLElement)
{
super(params, srcNodeRef);
}
}
Then, over in the existing Dojo JavaScript (not TypeScript) widget, I want to new up an instance of MyTypeScriptWidget. So, here's what I have to do in MyJavaScriptWidget.js
var myInstance = new MyTypeScriptWidget.MyTypeScriptWidget();
Notice how I have to type it twice? Why? What am I doing wrong? How can I change MyTypeScriptWidget.ts so that MyJavaScriptWidget.ts can use AMD to create an instance like I did before, like this:
define(['dijit/_WidgetBase', 'tool/MyTypeScriptWidget'], function(_WidgetBase, MyTypeScriptWidget) {
return declare([_WidgetBase], {
var myInstance = new MyTypeScriptWidget();
});
});
In TypeScript, export normally defines members on the module's export object. If you want to define a single thing to be exported, you need to use export =.
class MyTypeScriptWidget ... {
...
}
export = MyTypeScriptWidget;