How to create with jscodeshift a Typescript interface - jscodeshift

I would like to create interface with few properties but with a constructor rather than with pure text, something in this form:
const interfaceDeclaration = j.interfaceDeclaration(j.identifier("props"), /* ... */);
I have problem with ObjectTypeAnnotation and the last parameter

Related

Re-export all imports with changed type definition

We have a git submodule of React components written in poorly typed TS. It has a single index file, which re-exports everything like this:
export * from './blocks'
Then, in the main app, we use import * as allBlocks from '../packages/blocks' or import {SomeBlock} ....
But now, I have added some type declarations to the mentioned index file, which, however, need to be imported separately to use.
Is there a way to tell TS to re-export as the new type?
for example something like this:
export * from './blocks' as TheNewType
This is how the index file looks currently.
export * from './blocks' // I want to export all as different type
// all this is just for the types
import * as allBlocks from './blocks'
type AllBlocks = typeof allBlocks
type BlockNames = keyof AllBlocks
export type BlockComponents = {
[key in BlockNames]: BlockComponent<ExtractComponentGenerics<AllBlocks[key]>>
}
type BlockComponent<P = {}> = React.ComponentType<P> & BlockComponentStatics<P>
/** This interface types some static properties used by our library, that are not normally present on React.ComponentType */
interface BlockComponentStatics<P = {}> {
getInitialProps?(): P | Promise<P>;
// ...
}
function assertIsBlocks(val: any): asserts val is BlockComponents {/*...*/}
assertIsBlocks(allBlocks)
export allBlocks // This unfortunatelly produces named export, which I don't want.
export default allBlocks // I don't want this neither. I want the * export.
Since this package doesn't come as a module, I am not sure if and how I can use Ambient module declaration or Module augmentation.

SetInternalFieldCount on constructor->InstanceTemplate() did not work for the instantiated object

This post is about exposing C++ objects to the v8 javascript engine. To attach a C++ object to a javascript object, I make use of the GetInternalField() and External APIs. Before you can set or get any internal field, you have to call SetInternalFieldCount() on the corresponding ObjectTemplate. Since I want to expose a constructor function to the JS, I created a FunctionTemplate, set a C++ function that attache the native object to the JS object to that template, and finally SetInternalCount() on the InstanceTemplate() of that function template. Too much words for the description, here is what I did:
struct Point {
int x, y;
Local<FunctionTemplate> CreatePointContext(Isolate* isolate) {
Local<FunctionTemplate> constructor = FunctionTemplate::New(isolate, &ConstructorCallback);
constructor->InstanceTemplate()->SetInternalFieldCount(1); // I set internal field count here.
constructor->SetClassName(String::NewFromUtf8(isolate, "Point", NewStringType::kInternalized).ToLocalChecked());
auto prototype_t = constructor->PrototypeTemplate();
prototype_t->SetAccessor(String::NewFromUtf8(isolate, "x", NewStringType::kInternalized).ToLocalChecked(),
XGetterCallback);
return constructor;
};
// This callback is bound to the constructor to attach a C++ Point instance to js object.
static void ConstructorCallback(const FunctionCallbackInfo<Value>& args) {
auto isolate = args.GetIsolate();
Local<External> external = External::New(isolate, new Point);
args.Holder()->SetInternalField(0, external);
}
// This callback retrieves the C++ object and extract its 'x' field.
static void XGetterCallback(Local<String> property, const PropertyCallbackInfo<Value>& info) {
auto external = Local<External>::Cast(info.Holder()->GetInternalField(0)); // This line triggers an out-of-bound error.
auto point = reinterpret_cast<Point*>(external->Value());
info.GetReturnValue().Set(static_cast< double>(point->x));
}
// This function creates a context that install the Point function template.
Local<Context> CreatePointContext(Isolate* isolate) {
auto global = ObjectTemplate::New(isolate);
auto point_ctor = Point::CreateClassTemplate(isolate);
global->Set(isolate, "Point", point_ctor);
return Context::New(isolate, nullptr, global);
}
When I tried to run the following JS code with the exposed C++ object, I got Internal field out of bounds error.
var p = new Point();
p.x;
I wonder setting internal field count on the instance template of a function template has nothing to do with the object created by the new expression. If so, what is the correct way to set the internal field count of the object created by new while exposing the constructor function to javascript? I want to achieve the following 2 things:
In javascript, a Point function is avaible so we can var p = new Point;.
In C++ I can make sure the JS object has 1 internal field for our C++ Point to live in.
Edit: As #snek pointed out, I changed Holder() to This() and everything started to work. But later When I changed SetAccessor to SetAccessorProperty, it worked even with Holder.
Although the behaviour are very confusing, I think the major problem may not lie in the difference between Holder and This, but rather in SetAccessor and SetAccessorProperty. Why? Because in many docs I have read, Holder should be identical to This in most cases and I believe without using Signature and given that my testing js code is so simple (not with any magic property moving), in my case This should just be Holder.
Thus I decided to post another question about SetAccessor and SetAccessorProperty and leave this post as a reference.
For why I am so sure about in my case This() == Holder() should hold, here are some old threads:
https://groups.google.com/forum/#!topic/v8-users/fK9PBWxJxtQ
https://groups.google.com/forum/#!topic/v8-users/Axf4hF_RfZo
And what does the docs say?
/**
* If the callback was created without a Signature, this is the same
* value as This(). If there is a signature, and the signature didn't match
* This() but one of its hidden prototypes, this will be the respective
* hidden prototype.
*
* Note that this is not the prototype of This() on which the accessor
* referencing this callback was found (which in V8 internally is often
* referred to as holder [sic]).
*/
V8_INLINE Local<Object> Holder() const;
Note in my code there is not Signature, literally. So This and Holder should make no difference, but with SetAccessor, they made a difference.

Sapui5 use my new controle in xml

this is how i create new control:
https://sapui5.hana.ondemand.com/sdk/#docs/guide/91f0a8dc6f4d1014b6dd926db0e91070.html
sap.ui.somelib.SomeControl.extend("my.OwnControl", {
...
init: function() {
if (sap.ui.somelib.SomeControl.prototype.init) { // check whether superclass implements the method
sap.ui.somelib.SomeControl.prototype.init.apply(this, arguments); // call the method with the original arguments
}
//... do any further initialization of your subclass...
}
Is possible to use some controle in my xml view ?
When defining a control like this:
sap.ui.core.Control.extend("my.Square", ...
and declaring the XML namespace
xmlns:my="my"
you can use your control like this:
<my:Square text="test" size="100px"/>
BUT only if you also declare your control as existing module:
jQuery.sap.declare("my.Square");
(this last part is missing in the other answer)
Here is a running example with the "Square" control from the documentation:
http://jsbin.com/zuxebev/1/edit?html,output

v8: can't get calling function name in a functioncallback

I want to make a log of every function called when i run a js script.
So i want to make a callback for all the functions in javascript like this:
global->Set(v8::String::NewFromUtf8(isolate, "print"), v8::FunctionTemplate::New(isolate, LogName));
global->Set(v8::String::NewFromUtf8(isolate, "eval"), v8::FunctionTemplate::New(isolate, LogName));
global->Set(v8::String::NewFromUtf8(isolate, "unescape"), v8::FunctionTemplate::New(isolate, LogName));
I define my function like this:
void LogName(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::String::Utf8Value str_caller(args.Callee());
printf("%s", str_caller);
}
This is printed when unescape is called: function unescape() { [native code] }
But if do:
object = v8::Handle<v8::Object>::Cast(context->Global()->Get(v8::String::NewFromUtf8(isolate, "String")));
object->Set(v8::String::NewFromUtf8(isolate, "fromCharCode"), v8::FunctionTemplate::New(isolate, LogName)->GetFunction());
This is printed when String.fromCharCode is called: function () { [native code] }
Why in the second example i don't have the functions name, like for example "fromCharCode" ?
I'm still pretty new to V8 but have run into this exact same problem. I've found only one solution so far. I'm not sure if it is ideal, but there are no other solutions so here goes...
Notice the functions where getting the name works are where you are adding a FunctionTemplate value to an ObjectTemplate (that is presumably then used as the global template parameter when you create the Context). Also notice in the ones that don't work you are trying to add a Function to the global Object for that existing Context, and this is when getting the Callee name fails (returns a blank string).
The only solution I've found so far is to keep a persistent handle to the ObjectTemplate you create for global scope, add the FunctionTemplate to that when you go to register your new function, and then create a new Context that uses that modified ObjectTemplate. After this then calls to the function will return the Callee name as desired.
To try to illustrate this with some code:
Isolate *gIsolate;
Persistent<ObjectTemplate> gTemplate;
Persistent<Context> gContext;
// Adds a new function to the global object template
void AddFunction(const char *name, FunctionCallback func)
{
// Get global template
Local<ObjectTemplate> global = ObjectTemplate::New(gIsolate, gTemplate);
// Add new function to it
global->Set(String::NewFromUtf8(gIsolate, name), FunctionTemplate::New(gIsolate, func));
}
void FirstTimeInit()
{
gIsolate = Isolate::New();
HandleScope handle_scope(gIsolate);
Handle<ObjectTemplate> global = ObjectTemplate::New(gIsolate);
// Store global template in persistent handle
gTemplate.Reset(gIsolate, global);
// Register starting functions
AddFunction( "print", LogName );
AddFunction( "eval", LogName );
AddFunction( "unescape", LogName );
// Create context
Handle<Context> context = Context::New(gIsolate, NULL, global);
gContext.Reset(gIsolate, context);
}
void AddOtherFunction()
{
AddFunction( "fromCharCode", LogName );
Local<ObjectTemplate> global = ObjectTemplate::New(gIsolate, gTemplate);
// Create a new context from the modified global template
Local<Context> newContext = Context::New(gIsolate, nil, global);
// Update persistent context handle to reference the new one
gContext.Reset(gIsolate, newContext);
}

Add method to Array in TypeScript 1.0

I'm trying to create a TypeScript 1.0.2 (VS 2013 Update 2 RTM) definition file for the ASP.NET Ajax library, and I'm getting hung up on how to define the additional methods that MS Ajax adds to base JS types such as Array. I created a AspNetAjax.d.ts and a AspNetAjax-tests.ts file. When I attempt to use the "add" method in the test file, I get the compiler error that is listed below.
AspNetAjax.d.ts
interface Array<T> {
add(array: T[], item: T): void;
}
AspNetAjax-tests.ts
///<reference path="AspNetAjax.d.ts" />
var a: string[] = ['a', 'b', 'c', 'd'];
Array.add(a, 'e');
console.log(a.toString());
Error 1 The property 'add' does not exist on value of type '{ isArray(arg: any): boolean; prototype: any[]; (arrayLength?: number): any[]; (arrayLength: number): T[]; (...items: T[]): T[]; new(arrayLength?: number): any[]; new(arrayLength: number): T[]; new(...items: T[]): T[]; }'. c:\path\AspNetAjax-tests.ts 4 7 TypeScriptHTMLApp1
Other definitions from the same d.ts file are working in the tests file so I know that the reference is physically working. TypeScript also doesn't complain about the way I've declared the d.ts file (no red squiggles there).
I am aware of these other questions and I thought I was doing what they suggested, but it seems they're from late 2012/early 2013 so perhaps the way to do this has changed since then?
Extending Array in TypeScript
Adding a property to Array in Typescript
How can I add a static method to an existing type?
I need to code the d.ts so that the code in the .ts file will work. Any ideas?
The code you've written adds an add member to Array instances, not the built-in Array object.
The Array built-in object is defined in lib.d.ts near line 1134:
declare var Array: {
new (arrayLength?: number): any[];
new <T>(arrayLength: number): T[];
new <T>(...items: T[]): T[];
(arrayLength?: number): any[];
<T>(arrayLength: number): T[];
<T>(...items: T[]): T[];
isArray(arg: any): boolean;
prototype: Array<any>;
}
If you want to add a member to Array, you can modify the declaration as it appears in lib.d.ts.
If you're thinking that messing with lib.d.ts seems like a bad idea, it's because you shouldn't modify the built-in objects. There's no way to be sure that you and someone else haven't both decided that you have a great idea for an Array.add method that have completely different behavior.
You can take advantage of the declaration merging logic in TypeScript to extend Array as shown :
declare module Array{
export var add:Function;
}
var a: string[] = ['a', 'b', 'c', 'd'];
Array.add(a, 'e'); // Okay now
console.log(a.toString());
Between #basarat and #Ryan-Cavanaugh's answers I was able to come up with a minimally-horrible-to-the-user solution that works with TypeScript 1.0, as long as one is willing to accept that modifying the built-in JS objects is generally bad idea so it's OK if supporting that in TypeScript is slightly awkward.
Assuming that I am defining the ASP.NET AJAX extensions on the built-in JS Array object:
I will declare a module in the d.ts file called AspNetAjaxExtensions
I will create a non-exported interface inside that module declaration called JavaScriptArray<T>
I will create an exported interface inside that module declaration called Array<T> that extends JavaScriptArray<T>
I will copy all of the definitions for Array and Array<T> from the lib.d.ts that ships with TypeScript into the new JavaScriptArray<T> interface.
I can then proceed to model out only the extended functionality inside the new Array<T> interface.
This is somewhat anti-DRY for the d.ts author, but in reality these things very infrequently change and the stuff in JavaScriptArray<T> that was copied from lib.d.ts is self-contained. Technically, a build step could even be created to dynamically fill-in this interface.
For the user, they have to change their code that calls the extended Array object from this:
//will be an error in TypeScript 1.0
Array.add(x, 'thing');
to this:
//This works in TypeScript 1.0 after importing the above-described d.ts file.
(<AspNetAjaxExtensions.Array<string>>Array).add(x, 'thing');
Feasibly one could even Find+Replace Array.add( with (<AspNetAjaxExtensions.Array<any>>Array).add(.
They only need to do this whenever any of the extended methods on the built-in Array object are called (which will be called out by TypeScript syntax errors). Calls to normal Array methods will still use the "normal" definition in lib.d.ts.
Example new AspNetAjax.d.ts file:
declare module AspNetAjaxExtensions {
/** This interface definition was copied from lib.d.ts */
interface JavaScriptArray<T> {
new (arrayLength?: number): any[];
new <T>(arrayLength: number): T[];
/* -- Snip out many copied and pasted lines of code from lib.d.ts -- */
}
/** JavaScript Array object as extended by ASP.NET Ajax */
export interface Array<T> extends JavaScriptArray<T> {
/** Adds an element to the end of an Array object. This function is static and is invoked without creating an instance of the object.
http://msdn.microsoft.com/en-us/library/bb310854(v=vs.100).aspx
#param array The array to add the item to.
#param item The object to add to the array.
*/
add(array: T[], item: T): void;
/* -- etc... defining remaining extended methods -- */
}
}

Resources