The code of `for-of` circlation transport by babel not support some browser - reactjs

I had some es6 codes in my react project like this:
``` javascript
for (const v of [0, 1, 2,]) { /* doSth*/ }
```
and it works well after transport to es5 every where.
however, if I change them like those:
``` javascript
const arr = [0, 1, 2,];
for (const v of arr) { /* doSth*/ }
```
then it throw an error that ReferenceError: Can't find variable: Symbol in iPhone 5c.
I tried both foreach for-in and the pure for circulation, they all works well in that old device.
But, I need do something like async () => { for (...) { await doSthAysnc; } }, and it cannot use foreach.
What's more, for-of was very dangerous, and pure for is very ugly. so if possibly, I prefer to use for-of.
thanks.
#Bergi Well, that's the codes it transpiles to:
javascript
("================================================================"),m=["a","b","c"],e.prev=38,v=c.a.mark(function e(n){return c.a.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,new Promise(function(e){return setTimeout(function(){return e(n)},1e3)});case 2:case"end":return e.stop()}},e,t)}),y=!0,b=!1,w=void 0,e.prev=43,_=m[Symbol.iterator]();case 45:if(y=(x=_.next()).done){e.next=51;break}return E=x.value,e.delegateYield(v(E),"t2",48);case 48:y=!0,e.next=45;break;case 51:e.next=57;break;case 53:e.prev=53,e.t3=e.catch(43),b=!0,w=e.t3;case 57:e.prev=57,e.prev=58,!y&&_.return&&_.return();case 60:if(e.prev=60,!b){e.next=63;break}throw w;case 63:return e.finish(60);case 64:return e.finish(57);case 65:e.next=69;break;case 67:e.prev=67,e.t4=e.catch(38);case 69:console.log("================================================================"),

Using an array literal directly inside for … of enables an extra transpiler optimisation that causes it not to use iterables. In the default cause, it will try to find the m[Symbol.iterable] method which fails in legacy environments.
In can see three solutions:
Import the babel-polyfill to enable symbols and iterators
Use loose mode which hopefully always uses the array path
Use the transform-for-of-as-array babel plugin instead which always transpiles to array loops

Related

Unable to get type-safety (CustomTypeOptions) working with react-i18next

I recently successfully implemented using react-18next for localization needs inside my app. I have a small package that contains the localization files, react-i18next setup, and exports a class which is referenced in another application to get the i18n instance and pass it to the which wraps my components.
This has been deployed and works as expected.
I stumbled upon the documentation here (https://react.i18next.com/latest/typescript#create-a-declaration-file), which says that you are able to make the t function fully type safe. I would love to implement this, so that I am able to catch mis-matched key errors at compilation time, rather than needing to hunt for each case within the application.
I am having some trouble achieving this desired type-safety though, and wasnt sure if it was something that I am doing wrong or possibly a bug in the typing (I assume the former, as others seem to get the safety working without any issue).
Versions:
react-i18next “^11.15.6”
i18next “^21.6.14”
react “16.14.0”
typescript 4.1+
Repo structure (excluding package.json, tsconfig.json, etc) :
src
translations
translations_en.json
translations_es.json
MyTranslationManager.ts
react-i18next.d.ts
The translation files do not use any nested strings, and are separated by language (”_en” vs “_es”). Each language has all the needed strings in their localized format. The files are in this format:
{
"string1": "First string",
"string2": "Second string"
}
In my live (working) setup, this is how I initialize my instance:
import translationEN from "./translations/translations_en.json";
export class MyTranslationManager {
private readonly i18nInstance: i18nType;
constructor() {
this.i18nInstance = i18n.createInstance();
const defaultResources = {
en: { translation: { ...translationEN } },
};
this.language = "en";
this.i18nInstance
.use(initReactI18next)
.init({
resources: defaultResources,
lng: "en",
keySeparator: false, // we do not use nested translation resources
interpolation: {
escapeValue: false, // React already prevents XSS
},
});
}
// WORKING ON TYPE SAFETY
As directed in the docs, I create a react-i18next.d.s file to redeclare the “react-i18next” module - specifically the CustomTypeOptions interface:
import "react-i18next";
import translationEN from "./translations/translations_en.json";
declare module "react-i18next" {
interface CustomTypeOptions {
resources: typeof translationEN;
}
}
I do not declare a “defaultNS” option to CustomTypeOptions because I rely on the default namespace, “translation”.
When I attempt to compile the project with the above code, I get the following TS2344 issue:
node_modules/react-i18next/ts4.1/index.d.ts:203:25 - error TS2344:
Type 'string' does not satisfy the constraint 'Namespace<"btn_cancel" | "btn_save" | ... 86 more ... | "msg_unsavedChanges">'.
203 N extends Namespace = DefaultNamespace,
The error is thrown from each line in react-i18next/ts4.1/index.d.ts that attempts to set Namespace = DefaultNamespace.
I copied over as much of the code in index.d.ts as I could into the Typescript playground to try and get some insight into what is happening here, and I am able to get the compilation error to repro.
Hovering over the following items in the Typescript playground gives some interesting insight:
DefaultResources will resolve to { “btn_cancel”: string; “btn_ok”: string; }
DefaultNamespace will resolve to “ type DefaultNamespace<T = "translation"> = T extends "btn_cancel" | "btn_ok" ? T : string “
I assume this gets set by the use of the Fallback type, which gets passed in each key from DefaultResources via the keyof...
Link to playground.
My question is, why are the keys for the language files being set as the namespace? Is this by design? Am I importing the resources in an incorrect manner?
I noticed that the example here (https://github.com/i18next/react-i18next/blob/master/example/react-typescript4.1/no-namespaces/%40types/react-i18next/index.d.ts) only shows what the docs point to as an older version, i.e using the DefaultResources type instead of CustomTypeOptions. Any guidance on using the new method without namespaces would be greatly appreciated :)
For anyone working with i18next#v22.0.0 and react-i18next#v12.0.0, following the documentation here was successful for me (as of the time this was posted):
https://www.i18next.com/overview/typescript
I am now getting types for my translations inside of the t() function

Get Class Name of Class Subclassing Array in TypeScript?

I have the following class (ported from JavaScript wherein this works [prior to adding the types]) within TypeScript:
class A extends Array {
private _foo: string;
constructor(settings: any) {
super();
this._foo = 'foo';
}
get Foo() {
return (this._foo);
}
set Foo(value) {
this._foo = value;
}
}
let test = new A({ });
test.push('1');
test.push(2);
test.push('3');
// outputs "A" in JavaScript
// outputs "Array" in TypeScript
console.log(test.constructor.name);
The issue is outlined at the bottom of the code snippet, I'm expecting to get the class name back from test.constructor.name of "A" but am instead getting "Array". It seems like in the specific case of subclassing Array objects, TypeScript blows away the constructor name. Is there a way to find the class name within TypeScript which doesn't rely upon .constructor.name?
This happens because you have es5 as a target in compilerOptions and Typescript polyfills class
Have a look at generated code for es5 vs ES2015.
If you don't need to support really old browsers you can safely use a higher target than es5
I've tested your code in the Playground here
And it correctly outputs "A". I suspect something else is going on, possibly with your build tools or config.
I would suggest trying to use just tsc to narrow down the problem. If tsc correctly outputs, you can then move on to any other plugins, or build steps

Typescript Class.prototype.MyFunction makes errors but working

I can't explain why I get an error but code works. Is that compiler bug? (I use Visual Studio Code with Angular 2)
class A
{
fun(a: number)
{
return a+2;
}
}
A.prototype.F = function() { return "F here!"+this.fun(1); } // This makes error: The property 'F' does not exist on value of type 'A'
var a: A = new A();
console.log(a.F());
And bonus: This is not working at all! (no access to this.fun())
A.prototype.F2 = () => { return "F2 here!"+this.fun(1); } // ()=>{} is not working! cause _this is not defined!
...
console.log(a.F2());
Edit #1
As #seangwright said I need to use Module Augmentation but...
As far as it's working with simple example with my A class I can't make it work with Angular's ComponentFixture. This should solve my problem if I do this like in Typescript example:
declare module '#angular/core/testing' // I was trying without this line and with 'global' instead of '#angular/core/testing' but nothing helps
{
interface ComponentFixture<T>
{
TestOf(cssSelector: string): string;
}
}
But I still get an error:
'ComponentFixture' only refers to a type, but is being used as a value
here.'
at this point:
ComponentFixture.prototype.TextOf = function(cssSelector: string): string
{
...
}
There is even more errors, for example when I try to use it:
let fixture: ComponentFixture<EditableValueComponent>;
fixture = TestBed.createComponent(EditableValueComponent);
I got:
'ComponentFixture' is not assignable to type
'ComponentFixture'. Two different types with
this name exist, but they are unrelated. Property 'TestOf' is
missing in type 'ComponentFixture'
So again: Code works but has many compilation errors. Or maybe I'm missing something obvious?
I get the feeling you are a C# developer based on how you format your code.
Part 1
In Typescript once you declare your class, the type system expects it to have the properties (shape) you define and that's it.
The more of the type system you want to use, the less dynamic your objects will/can be.
That said, the reason your code runs (transpiles) correctly is because this is an error in the context of Typescript's structural type system, not Javascript's dynamic type system. So Typescript will tell you A doesn't have a property F at compile time, but Javascript doesn't care that it's added at runtime.
One solution is to merge the class with an interface
class A {
fun(a: number) {
return a + 2;
}
}
interface A {
F(): string;
}
A.prototype.F = function () { return "F here!" + this.fun(1); }
var a: A = new A();
console.log(a.F());
Another would be to temporarily abandon the type system
class A {
fun(a: number) {
return a + 2;
}
}
(A.prototype as any).F = function () { return "F here!" + this.fun(1); }
var a: A = new A();
console.log((a as any).F());
But that becomes verbose and prone to errors and loses the benefits that a type system brings.
You mention you are using Typescript with Angular 2. You could write in ES2015 if you wanted a more dynamic syntax. But then you will lose some of the benefits that Angular 2 gets from using Typescript (better tooling, smaller deployments).
Part 2
The reason your second example doesn't work at all has nothing to do with Typescript and everything to do with Scope (or execution context) in Javascript, specifically ES2015 arrow functions.
An arrow function does not create its own this context, so this has its original meaning from the enclosing context.
Unlike in your first example you are not using the traditional function declaration syntax and instead are using the () => {} arrow function syntax. With your first example
A.prototype.F = function() { return "F here!"+this.fun(1); }
this refers to whatever context F() is going to be executing in. Since you define it on the prototype of A it is going to be executing in the context of A. A has a .fun() method so this.fun() is going to be the same one defined in your class above.
With your second example, F2 is not going to be executing in the context of A despite being defined as a method of its prototype. The arrow function syntax is instead going to allow F2 to run in the context of the enclosing context which is the global window object unless you are running in strict mode in which case
in browsers it's no longer possible to reference the window object through this inside a strict mode function.
So this will be undefined and calling fun() on undefined is going to throw an error.
Try adding a console.log(this) to your F2 function.
A.prototype.F2 = () => { console.log(this); return "F2 here!"+this.fun(1); }
When you run the transpiled Javascript you will probably see Window logged out to the console, and then probably an error like Uncaught TypeError: _this.fun is not a function
Use the Typescript Playground to write some Typescript, see what the tooling tells you, what transpiled Javascript is created and then run it to see if your Javascript is correct.

Evaluation Error while using the Hiera hash in puppet

I have the following values in my hiera yaml file:
test::config_php::php_modules :
-'soap'
-'mcrypt'
-'pdo'
-'mbstring'
-'php-process'
-'pecl-memcache'
-'devel'
-'php-gd'
-'pear'
-'mysql'
-'xml'
and following is my test class:
class test::config_php (
$php_version,
$php_modules = hiera_hash('php_modules', {}),
$module_name,
){
class { 'php':
version => $php_version,
}
$php_modules.each |String $php_module| {
php::module { $php_module: }
}
}
While running my puppet manifests I get the following error:
Error: Evaluation Error: Error while evaluating a Function Call, create_resources(): second argument must be a hash at /tmp/vagrant-puppet/modules-f38a037289f9864906c44863800dbacf/ssh/manifests/init.pp:46:3 on node testdays-1a.vagrant.loc.vag
I am quite confused on what exactly am I doing wrong. My puppet version is 3.6.2 and I also have parser = future
I would really appreciate any help here.
Looks like your YAML was slightly off.
You don't really need quotes in YAML.
Your indentation was two instead of one.
Your first colon on the first time was spaced. This will throw a syntax error.
it should look more like this:
test::config_php::php_modules:
- soap
- mcrypt
- pdo
- mbstring
- php-process
- pecl-memcache
- devel
- php-gd
- pear
- mysql
- xml
In the future try and look up YAML parsers like this: link
The problem was with my puppet version, somehow version 3.6 acts weird while creating resources, for instance it was failing on the following line,:
create_resources('::ssh::client::config::user', $fin_users_client_options)
The code snippet above is part of ssh module from puppet labs, which I assume is throughly tested and shouldn't be a reason for the an exception.
A further analysis led to the fact that the exception was thrown when the parameter parser = future was set in the config file
I cannot iterate using each without setting future as the parser, therefore I decided to change my source as follow:
I created a new class:
define test::install_modules {
php::module { $name: }
}
and then I changed the config config_php to:
class test::config_php (
$php_version,
$php_modules = [],
){
class { 'php':
version => $php_version,
}
install_modules { $php_modules: }
}
Everything seems to be much better now.

Extending Array in Typescript breaks constructor

while playing around with typescript I ran into then following interesting behavior:
class ExtArray<U> extends Array<U> {
constructor(...args : U[]) {
super(...args);
}
public contains(element : U) : boolean {
var i = this.indexOf(element);
return i !== -1;
}
}
var test : ExtArray<string> = new ExtArray("a", "b", "c");
test.push("y");
console.log(test.length); // 1
console.log(test[0]); // y
console.log(test[1]); // undefined
console.log("Has a: " + test.contains("a")); // Has a: false
console.log("Has y: " + test.contains("y")); // Has y : true
I've added the output of the console.log statements as comments.
See this typescript playground for an executable example and the javascript code.
As you can see it seems as if the elements passed to the constructor are not added to the array.
The section about extending expression in Whats new in Typescript suggests that it should be possible to extend the native Array type like that in typescript 1.6.
Also I didn't find anything in the typescript language reference,
that explains this behavior.
Most of the other questions about extending Arrays I found here are at least one year old and usually talk about a pre-1.0 version of typescript and therefore suggest to set up the prototype chain directly.
I seriously don't see what is going wrong here and I'm starting to suspect a typescript bug.
Or at least some kind of undocumented restriction for extending Arrays.
What goes wrong here?
It's a little easier to understand what's going on if you JSON.stringify() your object:
var test : ExtArray<string> = new ExtArray("a", "b", "c");
test.push("y");
// outputs {"0":"y","length":1}
document.writeln(JSON.stringify(test));
If you instead new-up a native Array, the resulting object is quite a bit different:
var test : Array<string> = new Array("a", "b", "c");
test.push("y");
// outputs ["a","b","c","y"]
document.writeln(JSON.stringify(test));
I agree with you that the documentation seems to imply that the subclass's constructor should behave the way you're expecting. Even stranger, I seem to get inconsistent results when testing whether or not the subclass is an Array using the methods described here:
test.constructor === Array // false
test instanceof Array // true
Array.isArray(test) // false
I would suggest opening an issue on the TypeScript GitHub repository. Even if this is the expected behavior, the official documentation is misleading and should clarify what exactly is expected when native objects are subclassed.

Resources