Property "at" does not exist on Array - javascript

According to MDN Web Docs Array.prototype#at, is a valid method. But for some reasons, TypeScript refuses to compile, stating that it does not exist.
public at(index: number): V {
index = Math.floor(index);
const arr = [...this.values()];
return arr.at(index);
}
Console output from tsc:
I can't use bracket notation, since the method is meant to be able to handle negative numbers. I've tried lots of things, including setting the target in the tsconfig.json file to ESNext, ES2021, and ES6 but to no avail. The lib option doesn't help either.
json
// tsconfig.json
{
"compilerOptions": {
"target": "ES2021",
"outDir": "./dist",
"declaration": true,
"declarationDir": "./typings",
"lib": ["ES2021", "ESNext"]
}
}
What can I do? Am I doing something wrong?

According to MDN Web Docs Array.prototype#at, is a valid method.
MDN does not get to decide what is part of TypeScript and what isn't. The TypeScript developers do that.
Whether or not MDN says something is a "valid method" is relevant to Mozilla, and only Mozilla, but has no bearing on TypeScript.
But for some reasons, TypeScript refuses to compile, stating that it does not exist.
That's because the method doesn't exist in any ECMAScript version supported by TypeScript.
I've tried lots of things, including setting the target in the tsconfig.json file to ESNext, ES2021, and ES6 but to no avail.
That's because the method doesn't exist in either ECMAScript 6 or ECMAScript 2021. It does exist in ES2022 (which is what ESNext is at the moment), but it was only added eight weeks ago, and thus after TypeScript 4.4 was finalized (and possibly also too late for TypeScript 4.5).
The lib option doesn't help either.
Again, that's because the method does not exist in any library version supported by TypeScript at the moment.
Even in the current main branch, which is going to become TypeScript 4.6, lib/lib.esnext.d.ts only corresponds to ECMAScript 2021 + the latest Internationalization extensions.
What can I do? Am I doing something wrong?
You can wait until the method actually becomes part of a released version of ECMAScript and/or TypeScript.

Array#at is indeed a new method available only in ESNext or by using a polyfill. If you're using latter, you can just augment global array interface like this:
declare global {
interface Array<T> {
at(index: number): T;
}
}

Related

Target latest version of EcmaScript with Typescript

Is there a way to set your typescript build to compile to either the latest or the latest stable version of EcmaScript?
i.e. tsc --target <get latest version>
Or for a tsconfig file:
{
"compilerOptions": {
"outDir": "./built",
"target": <latest>
},
"include": ["./src/**/*"]
}
There is ESNext:
The special ESNext value refers to the highest version your version of TypeScript supports. This setting should be used with caution, since it doesn’t mean the same thing between different TypeScript versions and can make upgrades less predictable.
Note that this does not use the highest version of EcmaScript that exists in the published standard, but the highest version of EcmaScript that your TypeScript supports - which makes sense. TS can't just automatically support new tweaks to EcmaScript without compiler changes to TypeScript.

Clarification on functionality of Typescript's target and lib settings

When tsconfig.json has the following
"target": "es5",
"lib": [ "es6", "dom", "es2017" ]
it doesn't seem to convert es2017 constructs to es5. For instance, the following will fail on IE11:
var foo = [1, 2, 3].includes(1);
Is this by design or am I missing a setting in tsconfig.json?
Clarification on functionality of Typescript's target and lib settings
The simplified way I think about it is that target says what syntax the output JavaScript will have, and lib says what API members your TypeScript source code can use. There is more detail in the answers to these two questions:
Typescript- What is target in tsconfig?
What does the tsconfig option "lib" do?
...it doesn't seem to convert es2017 constructs to es5... Is this by design or am I missing a setting in tsconfig.json?
You're right. This is by design. TypeScript transpiles to the target syntax; it does not polyfill the API members that are missing from the target. Here is a quote from a core member of the TypeScript team:
I think you're confusing transpilation with auto-polyfilling. TypeScript doesn't automatically polyfill for you like Babel does, but does perform syntactic downleveling (e.g. for arrow functions). If you want to use ES6 runtime prototype methods, I'd simply include an appropriate ES6 polyfill and its accompanying definition file.
If your lib includes API members (such as Array.prototype.include) that aren't present in the target runtime, then you need to install polyfills that provide those API members.
You can see list Browser compatibility for includes method here.
In this, it did not support IE.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes

What does the "target" property in tsconfig.json actually represent?

I have been developing an Angular app using Typescript, and I have come to realise I need to pay more attention to the tsconfig in regards to compilation (transpilation).
Currently in the source code I am using some es6 features (such as Array.prototype.find) and the TSLint-er is picking up these as errors.
I'm trying to reconfigure my tsconfig so that it allows me to use es6 features but transpile to es5. In doing this, I'm failing to understand what the "target" property actually is. What does the "target" property mean?
Does "target" represent what the desired, transpiled output will be? Or does it declare what the Typescript syntax should conform to in order to be transpiled?
TypeScript is a superset of ES6, so you’re essentially writing TS code using ES6 version of JavaScript. However, when compiled, the resulting JS code can be in ES5 or earlier. You need to define which version of JS the compiler should transpile into. This can be set using target option:
{
"compilerOptions": {
"target": "es6"
}
}
You can read more about configuration here.
However, it also is used for validation indirectly. This parameter defines which libraries are used during compilation. If you specify target:es5, it uses ES5 library which doesn't contain Array.prototype.find. You can manually set the library you want to be used:
{
"compilerOptions": {
"lib": ["es6", "dom"],
}
}
In this case you will not have an error even if you specify es5 as a target.

Does Typescript transpilation handle transpiling ES6 to ES5?

I am writing an app using Angular 2 and TypeScript. I want to use a js method (particularly 'filter' for arrays) which is supported by IE 11+, Chrome 45+, etc.
Will my code be able to run on older browsers? As Typescript transpiles to vanilla js, I am not sure what it does with ES6 features.
TypeScript allows you to use new language features from ES6 and it will transpile those language features to ES5; however, it does not add polyfills for built in functions that exist in ES6, but don't exist in ES5.
If you are using built in functions that only exist in ES6 and are targeting ES5, then you will need to include the necessary polyfills in order for the code to work in ES5 environments.
Example
For example, fill is a new function found on Array's prototype in ES6.
const result = ["first", "second"].fill("all");
When you target ES6 everything is fine. The compiler will include lib.es6.d.ts with the definition for fill and not complain because it assumes you will be running the code in ES6 environments where the fill function exists.
When you target ES5, however, it won't include lib.es6.d.ts and will tell you that function doesn't exist in ES5 environments:
error TS2399: Property 'fill' does not exist on type 'string[]'.
To fix that, you'll need to add fill to the Array<T> interface in a definition file in your project:
interface Array<T> {
fill(value: T, start?: number, end?: number): this;
}
And include a polyfill. Or use something that does it automatically for you.
Yes it can if you set tsconfig.json to target ES5
set target: "ES5" in tsconfig.json compiler options or --target ES5 for CLI
As for built-in features of ES6, IDK, but I would add the babel-loader to webpack, grunt, gulp, etc, to automatically polyfill those features.

Are ES6 features compiled to ES5 when used within TypeScript?

When I use ES6 features like for example template string, arrow functions, destructuring within a TypeScript file. Afterward I compile the file to normal JavaScript ...
Are the ES6 syntax compiled too by the TypeScript compiler? Or do I have to use an additional compiler (Babel)?
Are the ES6 syntax compiled too by the TypeScript compiler? Or do I have to use an additional compiler (Babel)?
I disagree with the Fylax's answer. The TypeScript compiler doesn't require an additional tool for converting the ES6 syntax to ES 3 or 5.
The TypeScript compiler tranpiles the new syntax (let, for … of, arrow functions, rest parameters, etc.) to ES 3 or 5. But it doesn't provide any polyfill by itself. In order to use a recent API (like Promise) on a old VM ES 3 or 5, you have to:
Load a polyfill (like es6-promise) that makes the API available;
Say the compiler to use the standard typings for this API.
It is a robust design option. With typeScript, you have to choose carefully the polyfills you need, and to test them on the different browsers you target.
By default, when the target is ES 3 or ES 5, the compiler doesn't use the definitions for the recent ECMAScript API. See the documentation:
Note: If --lib is not specified a default library is injected. The default library injected is:
► For --target ES5: dom,es5,scripthost
If a polyfill makes an API available, then we can configure the compiler to use it. Here is an example of configuration file tsconfig.json for using promises on ES5 VM:
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "es5", "es2015.promise"]
}
}
However, Babel can convert a few more features to ES 5 than TypeScript does. See the compatibility table from Kangax.
You need additional compilers that downport your code from ES6 to ES5.
TypeScript is pretty smart and will do most of the work for you (i.e. translate let to var or arrow functions to standard functions with right scope and bindings).
EDIT: as #Paleo pointed out, on 99% you don't need any external compiler as you can provide to TypeScript an extra library (polyfill) which makes everything work fine.
You will need an extra compiler on very rare cases when you are not covered neither by transpiler nor by polyfill's.

Categories