How to add custom HTML tag TSX - tsx

While trying to render a custom HTML tag <my-element> in JSX an error displayed
Property does not exist on type 'JSX.IntrinsicElements'
I've found some examples of how to do that using
declare global {
interface IntrinsicElements {
"my-element": any
}
}
but this produced another error:
ES2015 module syntax is preferred over custom TypeScript modules and namespaces #typescript-eslint/no-namespace

I've found the useful link to Typescript guide which helped me a lot:
The main idea is to create a new file with extension d.ts (e.g. myModule.d.ts) which should contain the following
export as namespace JSX;
export interface IntrinsicElements {
"my-element": any;
}

Related

TypeScript 2 TSX preserve and noimplicitany error TS2602: the global type 'JSX.Element' does not exist

I'm using TypeScript 2 and TSX with the preserve (not React) setting and with "noImplicitAny" enabled:
"noImplicitAny": true,
"jsx": "preserve"
The problem is, I keep getting this error when trying to build a simple TSX file:
error TS2602: JSX element implicitly has type 'any' because the global type 'JSX.Element' does not exist.
Here's an example of my TSX file:
'use strict';
import m from './m';
export default {
view() {
return (
<h1>Hello Mithril!</h1>
);
}
};
I'm trying to get TSX working with a non-React stack (Mithril). Thanks in advance!
Answer to original question: (how to solve the TS2602 error)
This is quite simple, as explained here:
As the errors say: "because the global type 'JSX.Element' does not exist"
you can define those types:
declare namespace JSX {
interface Element { }
interface IntrinsicElements { div: any; }
}
I recommend getting the react-jsx.d.ts file from DefinitelyTyped
You can use this file as a source for more complete typings (you'll need definitions for every sub-element in IntrinsicElements, i.e. div, p, a, etc.)
Getting farther with TSX and Mithril:
Once you've solved the typing issues, you'll find that you're not quite there. If you use the "jsx": "preserve" setting, the HTML code will be written directly in the generated js file, without any translation. This of course can't be loaded by a web browser (because it's a javascript file, not an html file).
I think there are two ways to make it work:
First solution that comes to mind is to use "jsx":"react" and write a small wrapper that will forward the calls to mithril, like this:
class React {
public static createElement(selector: string, attributes: object, ...children: Mithril.Child[]): Mithril.Child {
return m(selector, attributes, children);
}
}
This is the solution I'm currently using because it doesn't involve additional tools.
The other solution is to keep "jsx":"preserve" and use Babel, as described in mithril documentation, to translate the jsx file (which is generated by typescript from the tsx file) to a valid js file.
In the end, I've managed to make it work, but I found the process quite messy, with typescript/npm module system getting in the way to have JSX types extend Mithril types (so that your functions can return mithril-compatible types), etc. I had to modify the mithril typings (and drop npm #types/mithril), and add a few modules of my own.
I'm interested to know if someone solved this problem in an elegant and simple way!
Due to the lack of reputation it won't let me comment on youen's answer above so I'll include this in an answer of my own.
First of all, you can include this gist at the top of your project and name it something like mithril-jsx.d.ts so that typescript will see it as a type definition and won't compile it. The linked gist simply declares JSX.element as m.Vnode<any, any> and lists every HTML element under JSX.IntrinsicElements as any. Not a huge deal but a time saver.
Second, and the reason I'm even posting this: You do not need gulp or any other tool to compile .tsx files to mithril-using .js ones. All you have to do is specify these in your tsconfig.json
{
"compilerOptions": {
//...your other stuff here
"jsx": "react",
"jsxFactory": "m"
}
}
More information about the compiler options here: http://www.typescriptlang.org/docs/handbook/compiler-options.html
I am also developing with Mithril(2.0.3) and TypeScript(3.5.3).
TS7026: JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.
This error message can be resolved by installing #types/react.
npm install --save-dev #types/react
tsconfig.json has the following settings.
{
"compilerOptions": {
"jsx": "react",
"jsxFactory": "m"
}
}

Include a component as part of a plugin

Initial Question
How, if at all, can we expose a view/view-model component from a plugin? For instance, we have the following:
// ./open-id/foo-bar.html
<template>
<p>FooBar</p>
</template>
// ./open-id/foo-bar.ts
export class FooBar { }
In the consuming application, we would like to do this:
// ./app.html
<require from="open-id/foo-bar"></require>
<foo-bar></foo-bar>
Edits
Simpler Name
Based on Robinson Collado's answer, we using a simpler name (foo not foo-bar) to reduce complexity.
// ./open-id/foo.html
<template>
<p>Foo</p>
</template>
// ./open-id/foo.ts
export class Foo { }
// ./app.html
<require from="open-id/foo"></require>
<foo></foo>
That approach threw this error:
Unhandled rejection Error: Load timeout for modules: template-registry-entry!open-id/foo.html,text!open-id/foo.html
Global Resource
Based on the Installing Plugins documentations, we tried adding the component as a global resource.
// ./open-id/open-id.ts
function configure(config: FrameworkConfiguration) {
config.globalResources('./foo');
}
That approach threw this error:
GET http://localhost:9000/src/open-id/open-id/foo.js 404 (Not Found)
That means Aurelia is looking for the component in open-id/open-id/, which is one directory too deep.
Loading as a Plugin
During development of the plugin, we're loading the plugin like this, which may be why Aurelia is looking one directory too deep. How can we load the plugin differently during developent?
// ./main.ts
aurelia.use.plugin("./open-id/open-id");
Loading as a Feature
aurelia.use.feature("./aurelia-open-id-connect");
The error now is this for each constructor that receiving an injection from our feature.
Message: key/value cannot be null or undefined. Are you trying to inject/register something that doesn't exist with DI?
Try changing the name of the custom tag <foo-bar></foo-bar> to <foobar></foobar>. The name of the tag should match the name of it's view-model class. Unless, if you use the #customElement decorator to explicitly declare the tag name for the custom element.

How and where to instantiate a custom class that extends the WP_REST_Controller

I have a plugin that I created and I want to use the WP rest api controller pattern in order to extend the api.
<?php
/**
* Plugin Name: myplugin
* Plugin URI: h...
* Description: A simple plugin ...
* Version: 0.1
* Author: Kamran ...
* Author ....
* License: GPL2
function myplugin_register_endpoints(){
require_once 'server/controllers/my_ctrl.php';
$items=new items();
$items->register_routes();
}
add_action('rest_api_init','myplugin_register_endpoints');
.
.
I created a class a folder called server/controllers and inside it my_ctrl.php file with a class that extends WP_REST_Controller that looks like this
// server/controllers/my_ctrl.php
class items extends WP_REST_Controller {
/**
* Register the routes for the objects of the controller.
*/
public function register_routes() {
.....
}
}
However I am receiving the following error in sublime xdebuge call stack:
[Fatal error] Class 'myplugin\WP_REST_Controller' not found
I am not sure how to solve this issue, where to put the files for my custom controller, where to create the instance of the custom class etc?
Stumbled upon this and thought I'd provide my solution in case someone else encounters this.
The idea is to postpone the instantiation of the class extending WP_REST_Controller by not instantiating it until the actual rest_api_init hook is called.
Code example:
add_action( 'rest_api_init', function () {
require_once(plugin_dir_path(__FILE__) . '/VideoImageApi.php');
VideoImageApi::instance()->register_routes();
});
Note the require_once from within the callback.
I have manged to solve the issue,
I checked the wp-content\plugins folder and I couldn't find the \rest-api folder and although I found the folder inside \wp-includes\rest-api it seems that this folder that integrates the "wp rest api" into core doesn't include all the classes that the api can expose (it includes only 3 php files), So it didn't include \wp-content\plugins\rest-api\lib\endpoints\class-wp-rest-controller.php . I installed the "wp rest api" plugin and it was added to wp-content\plugins and now I don't have the error anymore. (It was strange because I don't know when it was deleted from my project)
Thank you Dan your comments really helped me to recheck everything and scan the folders included in my wordpress and realize that the plugin is missing and that the folder \wp-includes\rest-api doesnt contain all the needed classes.

RquireJS with Module in TypeScript

I'm studing TypeScript and RequireJS.
I want to simple module require but module type information missing.
Is there smart solution in such situation?
requirejs(['backbone'], (Backbone) => {
// In this function.
// Backbone is 'any'
});
requirejs(['backbone'], (BackboneRef: Backbone) => {
// error : Type reference cannot refer to container
// 型参照でコンテナー 'Backbone' を参照できません。
});
To do so you need to do the following:
Download backbone.d.ts from https://github.com/borisyankov/DefinitelyTyped, the backbone.d.ts provides typescript strongly-typed interface, if you use IDE like Visual Studio, you can have all the intellisense support to Backbone
(Optional) Config backbone in RequireJS
In your TypeScript class, you can reference Backbone like the following
`
/// <amd-dependency path="backbone" />;
/// <reference path="path/to//backbone.d.ts" />;
export class YourModel extends Backbone.Model {
}
The amd-dependency tells the compiler how to reference backbone, so it would generate the correct define statement in JavaScript.
The reference provides a way to Backbone's definition for typed check.
Hope this helps! TypeScript eliminates the hell of writing long define or require js statement, which can be error-prone in situation where there are lots of dependencies.

is this the correct declarative, data-dojo-type syntax to require a package?

I have a package, defined in dojoConfig like this:
packages: [
{ name: 'Widget', location: '/widgets/Widget' }
]
The /widgets/Widget/main.js file defines my main module. With this config, in Javascript i can require the module Widget/main directly by its package name like this:
require(["Widget"], function(Widget){
var widget = new Widget();
// all is well
});
But doing the same using declarative syntax throws a Unable to resolve constructor for: 'Widget' error:
<div data-dojo-type="Widget"></div>
Am I doing something wrong, or is this expected behaviour?
It would be easier to see how widget is created, but the complaint is that you have no constructor.
a constructor is required for a widget. If you extend WidgetBase its done for you.
check the doc:
http://dojotoolkit.org/reference-guide/1.9/quickstart/writingWidgets.html
You need to add the
require(["Widget"], function(Widget){});
part in a script tag in the HTML document you are using
<div data-dojo-type="Widget"></div>
It should look like something:
<script> require(["Widget"], function(Widget){}); </script>
You have to require the module/widget before you can call it in an HTML page the same way you need to do it in a script tag.