Reputation: 1828
Converting my JS to TS strict mode.
The following syntax looks fine to me but TS is complaining in the for
loop on allSubMenus
with:
[ts] Type 'NodeListOf<Element>' is not an array type or a string type.
What am I missing?
function subAct(target:Node){
const allSubMenus : NodeListOf<Element> = document.querySelectorAll('.subMenuItems')
for (const sub of allSubMenus){
sub.classList.remove('active')
}
}
Upvotes: 45
Views: 49848
Reputation: 16607
This assumes you want to keep ES5 target (if not, upgrading it to ES6 will solve the issue as well).
for
loopfunction subAct(target:Node){
const allSubMenus = document.querySelectorAll('.subMenuItems');
for (let i = 0; i < allSubMenus.length; i += 1){
const sub = allSubMenus[i];
sub.classList.remove('active')
}
}
Array.from
In order to use this, you have to add ES2015.core
to compilerOptions.lib
and add polyfill for Array.prototype.from
Note that this will loop on the collection twice - the first method is better.
function subAct(target:Node){
const allSubMenus = Array.from(document.querySelectorAll('.subMenuItems'));
for (const sub of allSubMenus){
sub.classList.remove('active')
}
}
Upvotes: 1
Reputation: 3535
According to your typescript target compiler, parsing error can be occured.
The for-of loop, introduced in the sixth edition of EcmaScript (ES6). Thus, old browsers' JS engine can not understand for-of loop syntax. https://hacks.mozilla.org/2015/04/es6-in-depth-iterators-and-the-for-of-loop/
To solve this issue,
change your TS target
//tsconfig.json
{
"compilerOptions": {
"target": "es6" //above es6 like ES2015,ES2018 ...
}
}
I presume that you are using next environment.
//.tsconfig.json
{
"compilerOptions": {
"target": "es5"
}
}
Note: "as any" will cast collection(Objects) to array and this will affect some type features within "for" scope.
//.ts
const allSubMenus : NodeListOf<SpecifiedElement> = document.querySelectorAll('.subMenuItems')
for (const sub of allSubMenus as any){ // then will pass compiler
sub.classList.remove('active')
}
The above TS script will be compiled to
//.js output
var allSubMenus = document.querySelectorAll('.subMenuItems');
for (var _a = 0, _b = forms; _a < _b.length; _a++) {
var sub = _b[_a];
sub.classList.remove('active');
}
https://stackblitz.com/edit/node-ywn1bq?file=main.js
const allSubMenus : NodeListOf<SpecifiedElement> = document.querySelectorAll('.subMenuItems')
for (let i = 0; i < allSubMenus.length; i++) {
allSubMenus[i].classList.remove('active');
}
In addition to the above, to avoid the following warning,
Property '<property name>' does not exist on type 'Element'
you may specify <Element> if you know element type and type define exists.
//for example,
NodeListOf<Element> => NodeListOf<HTMLFormElement>
https://codeburst.io/javascript-wtf-is-es6-es8-es-2017-ecmascript-dca859e4821c
Upvotes: 41
Reputation: 56
Set "downlevelIteration": true
in compilerOptions in your tsconfig.json file.
From https://www.typescriptlang.org/tsconfig#downlevelIteration
Downleveling is TypeScript’s term for transpiling to an older version of JavaScript. This flag is to enable support for a more accurate implementation of how modern JavaScript iterates through new concepts in older JavaScript runtimes
Upvotes: 2
Reputation: 7442
while compiling your code you may choose target: ES2017
or target: ES2015
. also you can set this property in tsconfig.json
file unser compilerOptions
.
So here is the command line code for that:
npx tsc path/to/file.ts --target ES2015
TIP:
if you are using babel along side with typescript, it is totally recommended that you always make typescript to compile to latest version of Javascript, and then let babel handles rest of transpiring process. With this technique you add another lever of assurance of supportive level to older browsers since typescript is not ok to compile the code that would run in for example ie6; so babel comes to rescue here and make you yo make sure that your js
code would run in even ie < 9 with it's helpful polyfills and other mechanisms that it takes to work!
Always let typescript compiles your code to latest javascript ( by setting target: ES2017
) and let babel transpiles your js code to support older browsers ( separate concerns properly and let each one does the related job).
Upvotes: 1
Reputation: 312
You can iterate over a NodeListOf
with the forEach
method.
const allSubMenus : NodeListOf<Element> = document.querySelectorAll('.subMenuItems')
allSubMenus.forEach(sub => sub.classList.remove('active'))
Upvotes: 1
Reputation: 114
You could try
const allSubMenus : NodeListOf<Element> = document.querySelectorAll('.subMenuItems')
Array.from(allSubMenus, subMenu => {/* */})
Upvotes: 8
Reputation: 30919
You need to set the target
compiler option to es6
or higher for NodeListOf<T>
to be iterable.
Upvotes: 44