Can't declare function with a name 'lang' in Polymer 2.0 - polymer

I have been using Polymer 1.0 for about 2 years, so it's time to upgrade to 2.0. But, I have found propably reserved function that can't be declared. ( I am using now 2.3.1 version)
I tried to create a function with a simple name lang. This function works in all of my projects as an internationalization function ( in Polymer 1.x ). So I would like to continue with using it. Unfortunately the name lang is ignored by Polymer. Whenever I call this.lang() or in binding [[lang()]] I get undefined
You can reproduce it with Polymer starter kit. Install it, and wherever in the project declare a function named lang and call it.
I found out that this has something to do with extending Polymer.element because when I extended HTMLElement, it worked. Is there something I am missing? I tried to search in documentation but I didn't find anything. Even on the internet no one is complaining even this name could be (propably) oftenly used
Is there a way to solve this? or do I have to just use different name of function? Are there other reserved names for functions?
Some example:
class MyGuidepost extends Polymer.Element {
static get is() {
return 'my-guidepost';
}
static get properties() {
return {
prop1: {
type: String,
value: 'my-guidepost'
}
};
}
ready() {
super.ready();
this.test();
}
lang(param) {
return "asdsad";
}
test() {
console.warn(this.lang()); // calling lang function returns undefined
}
}
window.customElements.define(MyGuidepost.is, MyGuidepost);
}

Related

Typescript return HTML Template Element from Constructor typing not working

It is 'illegal' to use new when creating an instance of Template, where Template extends an HTMLTemplateElement.
To overcome this limitation, I get and return an HTMLTemplateElement using document.getElementById(id) from the Template constructor as below:
export class Template {
private htmlTemplateElement: HTMLTemplateElement;
constructor(id: string) {
this.htmlTemplateElement = document.getElementById(id) as HTMLTemplateElement;
return Object.assign(this.htmlTemplateElement, this)
}
public test = () => this.htmlTemplateElement.innerHTML
}
Providing an HTML Template Element exist in the DOM,
I can create a new instance of Template and use the extension method test() as illustrated below:
const template = new Template(id)
console.log(template.test())
console.log(template.innerHTML)
Both console.log() works just fine and prints the correct text to the console.
HOWEVER, the typescript compiler complains about template.innerHTML.
The error I get, saying innerHTML does not exist on type Template
Question: How can I add type information so I do not get a compiler error?
I have tried to use export class Template extends HTMLTemplateElement.
That does not work since it is illegal to create an instance using new.
I love typescript, but sometimes the type checking gets in my way.
Help me out here, please.
Although not ideal, I was able to keep the typescript compiler happy by implementing the following interface:
export interface ITemplate {
[key:string]: any;
test(): string;
}
and then using the interface:
export Template implements ITemplate {
...
}
Note:
Why did I not use customElements.define(<tag-name>,Template)?
I do not intend to create a new custom instance of HTMLTemplateElement, I just want to return an existing HTMLTemplateElement with additional utility extension methods.
Also, It might very well be possible that my approach is completely wrong.
However, that is a different topic than the question asked here.

Specifying a property as `type: Array` doesn't seem to parse the string property value correctly

Update: Filed a bug at https://github.com/Polymer/lit-element/issues/411
I'm having trouble implementing the default and custom converter for properties given the information in the guide. With the current version (0.6.5), it says that you can assign the type Array to a property and it will automatically be parsed as JSON from the string value of the attribute, but that doesn't seem to be the case as described in the code sample.
Here's an illustration of the problem. In the console, it should report an array with three elements, yet it returns an array of one element, the string that contains the attribute value. The result of the render method also shows just one <p> element containing the single string.
<script type="module">
import { LitElement, html } from 'https://unpkg.com/#polymer/lit-element#0.6.5/lit-element.js?module';
class Histogram extends LitElement {
static get properties() {
return {
values: { type: Array }
};
}
constructor() {
super();
this.values = [];
}
render() {
console.log(Array.isArray(this.values), this.values);
return html`
<div>
The elements:
${this.values.map(item => html`<p>item: ${item}</p>`)}
</div>
`;
}
}
customElements.define('x-histogram', Histogram);
</script>
<x-histogram values="[1,2,3]"/>
I've also tried modifying the example by providing a converter, but that doesn't seem to get invoked, either. What am I doing wrong?
I had a look. The 0.6.5 updating-element.js file doesn't even have converting for array. And looking at the npm package of 0.6.5 it doesn't mention an array either.
But the master branch does map the array type, and it has it mentioned in the readme. It should work for you if you pull it and use it directly.

Custom error classes not extending correctly [duplicate]

I'm trying to throw a custom error with my "CustomError" class name printed in the console instead of "Error", with no success:
class CustomError extends Error {
constructor(message: string) {
super(`Lorem "${message}" ipsum dolor.`);
this.name = 'CustomError';
}
}
throw new CustomError('foo');
The output is Uncaught Error: Lorem "foo" ipsum dolor.
What I expect: Uncaught CustomError: Lorem "foo" ipsum dolor.
I wonder if that can be done using TS only (without messing with JS prototypes)?
Are you using typescript version 2.1, and transpiling to ES5? Check this section of the breaking changes page for possible issues and workaround: https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
The relevant bit:
As a recommendation, you can manually adjust the prototype immediately after any super(...) calls.
class FooError extends Error {
constructor(m: string) {
super(m);
// Set the prototype explicitly.
Object.setPrototypeOf(this, FooError.prototype);
}
sayHello() {
return "hello " + this.message;
}
}
However, any subclass of FooError will have to manually set the prototype as well. For runtimes that don't support Object.setPrototypeOf, you may instead be able to use __proto__.
Unfortunately, these workarounds will not work on Internet Explorer 10 and prior. One can manually copy methods from the prototype onto the instance itself (i.e. FooError.prototype onto this), but the prototype chain itself cannot be fixed.
The problem is that Javascript's built-in class Error breaks the prototype chain by switching the object to be constructed (i.e. this) to a new, different object, when you call super and that new object doesn't have the expected prototype chain, i.e. it's an instance of Error not of CustomError.
This problem can be elegantly solved using 'new.target', which is supported since Typescript 2.2, see here: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html
class CustomError extends Error {
constructor(message?: string) {
// 'Error' breaks prototype chain here
super(message);
// restore prototype chain
const actualProto = new.target.prototype;
if (Object.setPrototypeOf) { Object.setPrototypeOf(this, actualProto); }
else { this.__proto__ = actualProto; }
}
}
Using new.target has the advantage that you don't have to hardcode the prototype, like some other answers here proposed. That again has the advantage that classes inheriting from CustomError will automatically also get the correct prototype chain.
If you were to hardcode the prototype (e.g. Object.setPrototype(this, CustomError.prototype)), CustomError itself would have a working prototype chain, but any classes inheriting from CustomError would be broken, e.g. instances of a class VeryCustomError < CustomError would not be instanceof VeryCustomError as expected, but only instanceof CustomError.
See also: https://github.com/Microsoft/TypeScript/issues/13965#issuecomment-278570200
As of TypeScript 2.2 it can be done via new.target.prototype.
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#example
class CustomError extends Error {
constructor(message?: string) {
super(message); // 'Error' breaks prototype chain here
this.name = 'CustomError';
Object.setPrototypeOf(this, new.target.prototype); // restore prototype chain
}
}
It works correctly in ES2015 (https://jsfiddle.net/x40n2gyr/). Most likely, the problem is that the TypeScript compiler is transpiling to ES5, and Error cannot be correctly subclassed using only ES5 features; it can only be correctly subclassed using ES2015 and above features (class or, more obscurely, Reflect.construct). This is because when you call Error as a function (rather than via new or, in ES2015, super or Reflect.construct), it ignores this and creates a new Error.
You'll probably have to live with the imperfect output until you can target ES2015 or higher...
I literally never post on SO, but my team is working on a TypeScript project, and we needed to create many custom error classes, while also targeting es5. It would have been incredibly tedious to do the suggested fix in every single error class. But we found that we were able to have a downstream effect on all subsequent error classes by creating a main custom error class, and having the rest of our errors extend that class. Inside of that main error class we did the following to have that downstream effect of updating the prototype:
class MainErrorClass extends Error {
constructor() {
super()
Object.setPrototypeOf(this, new.target.prototype)
}
}
class SomeNewError extends MainErrorClass {}
...
Using new.target.prototype was the key to getting all of the inheriting error classes to be updated without needing to update the constructor of each one.
Just hoping this saves someone else a headache in the future!
I ran into the same problem in my typescript project a few days ago. To make it work, I use the implementation from MDN using only vanilla js. So your error would look something like the following:
function CustomError(message) {
this.name = 'CustomError';
this.message = message || 'Default Message';
this.stack = (new Error()).stack;
}
CustomError.prototype = Object.create(Error.prototype);
CustomError.prototype.constructor = CustomError;
throw new CustomError('foo');
It doesn't seem to work in SO code snippet, but it does in the chrome console and in my typescript project:
I was having this problem in a nodejs server. what worked for me was to transpile down to es2017 in which these issues seems to be fixed.
Edit tsconfig to
"target": "es2017"
Try this...
class CustomError extends Error {
constructor(message: string) {
super(`Lorem "${message}" ipsum dolor.`)
}
get name() { return this.constructor.name }
}
throw new CustomError('foo')

Typescript: how to get function name as string without TS2339 error

I found the following example in the Function.name documentation
const o = {
foo(){}
};
o.foo.name; // "foo";
The problem in typescript (typed here):
const o: { foo: () => void } = {
foo: () => {
}
};
o.foo.name;
comes when I want to retrieve
o.foo.name, where I will get an error
TS2339 (property "name" does not exist)
How can I deal with it, keeping the object typing?
I want to avoid having to cast the property "foo" like (<any>o.foo).name
PS: The use case is to keep the typing for further refactoring. For instance the following is safe to be refactored:
spyOn(o, (<any>o.foo).name)
While this one is not
spyOn(o, "foo")
PS 2: It seems retrieving function name could be problematic on ts: Get name of function in typescript
The problem is that this code only works for newer versions of Javascript. If you change the target on the typescript compiler settings to es2015 the problem goes away. If you target es5 the definitions for that version do not include the name property because it might not work on older Javascript runtimes.
If you are ok with targeting es2015, that is ok, if not you should come up with a different solution that works for es5.
If you are targeting an environment that supports this property but you don't yet trust the es2015 implementation for all features, you could just add the the Function interface the missing property. At the top level in one of your files you can redefine the Function interface, and this will be merged into the default definition, adding the extra property:
interface Function {
/**
* Returns the name of the function. Function names are read-only and can not be changed.
*/
readonly name: string;
}
Post ES2015, this:
const o: { foo: () => void } = {
foo: () => { }
};
console.log(o.foo.name);
should work just fine.
Check it in the Typescript Playground, and observe the produced JavaScript. You will see the common sections with the foo example you mentioned.
Here is the console, nice and clean:
Pre-ES2015, this wouldn't work and I think you would have to cast it, if targeting post-ES2015 is not an option.

ES6 Class, extending Ractive

I'm using ES6 (via Babel), and looking for the "proper" way to define a Class that extends Ractive (without calling Ractive.extend(...) if possible), since Ractive allows new (http://docs.ractivejs.org/latest/new-ractive).
Tried the following approach, which almost seems to work:
class Home extends Ractive {
constructor() {
super();
this.template = "<h1>{{message}}</h1>";
}
data() {
return {
message: 'This is sample data'
}
}
oninit() {
console.log('ok');
}
}
Ractive instance is initialized without error, and oninit is called, however the template seems undefined.
I would like to start using that syntax as well. :)
But I spent an hour a month ago trying to see if it was hackable, but concluded it's not currently possible due to how some of the component extension works. But it's on our radar and should land in a near-future version.