I want to use Kotlin to generate some JavaScript like following:
function MyComponent() {
self.constructor = function() {}
}
The problem is constructor is a keyword in Kotlin, I can't just write like:
class MyComponent {
fun constructor() {}
}
I also tried:
class MyComponent {
#JsName("constructor")
fun ctor() {}
}
It still report errors like:
JavaScript name generated for this declaration clashes
with built-in declaration {1}
How to generate a javascript function which has name constructor?
There should be no problem with the top-level functions. The fun constructor() {} should just work, yielding function constructor(){}. At least that's what it does in Kotlin 1.2.31.
On the other hand member functions named constructor are prohibited (e.g. you cannot get A.prototype.constructor = function () {} in the output js file). For one thing that would ruin the is-check implementation.
Modifying the constructor property inside the class constructor should be possible:
// Kotlin
class A {
init{
this.asDynamic().constructor = fun(a: Int) { println(a) }
}
}
// JS
function A() {
this.constructor = A_init$lambda;
}
function A_init$lambda(a) {
println(a);
}
Hope that helped.
Related
In python there's something like __call__ for this. Consider the following example:
class MyClass {
__call__() { return 'called!' }
}
const myType = new MyClass();
myType(); // called!
The question is what should I replace __call__ with?
I was doing some research, and most of the answers recommend __proto__, but it doesn't seem to work.
It is not possible out-of-the-box, but you can extend Function, and use the Function constructor to forward a call to __call__. If you have multiple classes that need this feature, extend Function only once into -- let's say -- a Callable class, and then inherit your other classes from that:
class Callable extends Function {
constructor() {
super("...args", "return this.__call__(...args)");
return this.bind(this);
}
}
class Class extends Callable {
__call__() { return 'called!' }
}
let inst = new Class();
console.log(inst());
Background
In JavaScript an object is callable when, and only if, it has the [[Call]] internal slot. But there is (currently) no way to give any given object this slot via JavaScript code. One must start with a function object and extend that.
Adding a constructor, inheritance
The above solution allows the constructor to define properties in the usual way: the constructed object is an instance of the class:
class Callable extends Function {
constructor() {
super("...args", "return this.__call__(...args)");
return this.bind(this);
}
}
class Class extends Callable {
constructor(data) {
super();
this.x = data;
}
__call__() { return 'called!' }
}
let inst = new Class(42);
console.log(inst instanceof Class); // true
console.log(inst.x); // 42
console.log(inst());
You can use constructor.
class Example {
constructor() {
// gets called on class initialization
}
}
Inside the constructor you can also call other methods if you want.
However this won't create an invoke function like using PHP's __invoke if that's what you meant. If that's what you're looking for then I don't know.
First of all I'm a newbie in Typescript so title may be inaccurate because I don't know how that code works properly.
I'm using the Klasa framework which is a Discord bot framework made top of Discord.js. They recently added plugin functionality and there is lots of examples written in regular ES6...
const { Client, util: { mergeDefault } } = require('klasa');
const DriverStore = require('../lib/structures/DriverStore');
const { OPTIONS } = require('../lib/util/constants');
class MusicClient extends Client {
constructor(config) {
super(config);
this.constructor[Client.plugin].call(this);
}
static [Client.plugin]() {
mergeDefault(OPTIONS, this.options);
this.drivers = new DriverStore(this);
this.registerStore(this.drivers);
}
}
module.exports = MusicClient;
Type of Client.plugin is Symbol. How this code work? And how can I achieve something similar to this with TypeScript or it is doable?
I tried doing it like this:
import { KlasaClientOptions, Client } from "klasa"
export class ExtendedClient extends Client {
public prop: string;
constructor(options: KlasaClientOptions) {
super(options);
// Element implicitly has an 'any' type 'typeof KlasaClient' has no index signature.
this.constructor[Client.plugin].call(this);
// Also trying to use ExtendedClient.plugin.call() gives me
// Property 'call' does not exist on type 'symbol'
}
static [Client.plugin]() {
// Property 'prop' does not exist of type 'typeof ExtendedClient'
this.prop = "somestring";
}
}
Edit: I fixed the error after I found out static [Client.plugin]() has the context of KlasaClient so I changed it as
import { KlasaClientOptions, Client } from "klasa"
export class ExtendedClient extends Client {
public prop: string;
constructor(options: KlasaClientOptions) {
super(options);
(this.constructor as any)[Client.plugin].call(this);
}
static [Client.plugin](this: ExtendedClient) {
this.prop = "somestring";
}
}
and the problems are solved...
The code is fairly odd, and unless there's a strong design constraint forcing it to be that way, it's not best practice.
It's defining a static method, but calling it as though it were an instance method. Which is part of why TypeScript is having issues with it.
The key parts are:
This:
static [Client.plugin]() {
this.registerStore(this.drivers);
}
...which defines a static method (a method on the constructor function) with the name from Client.plugin (which you've said is a Symbol, but it doesn't really matter whether it's a Symbol or a string).
And this in the constructor:
this.constructor[Client.plugin].call(this);
...which is what's calling it as though it were an instance method. this.constructor accesses the constructor function that created this (loosely speaking, that's not entirely accurate), and so this.constructor[Client.plugin] accesses the static method with the name from Client.plugin. Then .call(this) calls that method, but sets this as this during the call, a though it were an instance method. So within the call to Client.plugin, this is an instance of the class (whereas normally this would be the constructor function).
Which is very odd.
In terms of making TypeScript okay with it, I think I'd probably approach it like this:
import { KlasaClientOptions, Client } from "klasa";
export class ExtendedClient extends Client {
public prop: string;
constructor(options: KlasaClientOptions) {
super(options);
(this.constructor as any)[Client.plugin].call(this);
}
static [Client.plugin]() {
const inst = this as ExtendedClient;
// Now use `inst` instead of `this` where your example code uses `this`, so:
inst.prop = "somestring";
}
}
The key bits are:
This:
(this.constructor as any)[Client.plugin].call(this);
...by using any we defeat TypeScript's type checking, but we basically have to for this odd structure.
And this:
const inst = this as ExtendedClient;
...by using as ExtendedClient we're telling TypeScript that although normally it would expect this to be the constructor function, it's actually an ExtendedClient. We do that once with a constant to avoid having to repeat it everywhere.
How can I avoid the creation of global functions when compiling typescript into javascript.
Benefit: obfuscator won't have to provide a public API
Foo.ts:
class Foo {}
Foo.js:
var Foo = (function () {
function Foo() {
}
return Foo;
}());
Foo.obfuscated.js (using npm jsobfuscator):
var _0xcd14=[];var _0x12e2=[];var Foo=(function(){function Foo(){}return Foo}())
Foo is still visible. I understand why (public API). Solution would be:
Foo.isolated.js:
(function() { /* code from Foo.js */ })();
Foo.isolated.obfuscated.js (what I want):
var _0xe1f1=[];var _0xa6a8=[];(function(){var _0x64f8x2=(function(){function _0x64f8x2(){}return _0x64f8x2}())})()
Is there a typescript setup for tsconfig.js / compiler options like isolation: true or something?
Wrapping the class inside a function scope should do the trick.
(function(){
class Foo {}
})
There isn't built in support in the form of an option as far as I am aware. Let me know if doing the above does not achieve your aim - in which case some more details about exactly what the goal is will help us understand what you're going for here.
nested (private) class ?
module MyModule {
export class MyPublicClass {
private myPrivateClass: PrivateClass;
constructor() {
this.myPrivateClass = new PrivateClass;
}
public test() {
this.myPrivateClass.test();
}
}
class PrivateClass {
public test() {
console.log('it works');
}
}
}
Given the following TypeScript code snippet:
export class MyClass {
myMethod() {
// ...
$myQuery.each(function(idx, elm) {
$(this)... // Original javascript code which obviously not correct in typescript
}
}
}
However in TypeScript this in a class method "this" always refers to the class instance. I would like to access the very same object what it would be in pure javascript.
In general: What is the way to access the original javascript context (this) in a callback when using TypeScript?
It is not accurate.
When using this in lambda expression or in a class method, it refers to the class itself. Examples:
class A{
public a:number;
public foo(){
this.a = 1;//this here is A
var lambda = () => { this.a = 2; } //this here is A
var fn = function() { this.a = 3; } // this here is not the A
}
}
You can look at the transpiled code here: Typescript Playground
Can I get the name of a method in TypeScript? For example like this:
class C {
test() { return this.test.name; } // should return "test";
}
I can make it work for functions by extending the Function interface:
interface Function {
name: string;
}
function f() {
return f.name; // returns "f"
}
Can I make that work for methods as well, in some way?
Short answer is NO, there is no name for anonymous functions.
If your concern is compilation error then overriding the interface would be sufficient as interface is partial. But the way Typescript transpiles the class there won't be any name for the instance methods as they are just references to anonymous functions. i.e
It gets compiled to
var C = (function () {
function C() {
}
C.prototype.test = function () {
// ^__ There is no name for the function so
return this.test.name; // this will just be empty
};
return C;
})();
You will see it printing function.name with value only if it had been like this:
C.prototype.test = function test() {
// ^__ Not anonymous anymore
One way i can think of is to use a decorator and set value for a property say propName.
interface Function {
propName?: string;
}
function setName(target: Object, propertyKey: string, descriptor: TypedPropertyDescriptor<any>) {
descriptor.value.propName = propertyKey;
//or target[propertyKey].propName = propertyKey;
return descriptor;
}
class C {
#setName
test() {
return this.test.propName;
}
}
It all ties down to how Typescript transpiles the class. For instance if you were using Babel, due to the way it transpiles the specs you will see that it assigns the value of the function reference as is to the property descriptor so it will retain the name.
Note: Not all browsers support Function.name anyways (ex: older IE), also property name is not Writable across browsers as well, i.e you cannot change the name. So configure with another property.