Open AllanOricil opened 4 years ago
+1 I would love TypeScript support, but I can understand how this might be difficult on the platform because of the non-standard import scheme used before compilation.
For the record, LWC does allow Typescript, here is an example (it might be a bit outdated but will be good enough for making the point): https://github.com/diervo/lwc-typescript-boilerplate
In a nutshell you just need to remove the types before running LWC compiler.
As for the platform usage: We will never be able to natively support Typescript (meaning we won't be able to ever allow you to save components in original .ts format) fundamentally due to the fact that typescript can have breaking changes from version to version, which means that we will have to support N+1 versions of platform until the ends of time, which is something we can't afford to do.
We do want to provide types and tooling to make it as seamless as possible, so all developers can choose to use Typescript from a "practically full" e2e development in platform.
@diervo thank you. I googled it and could not find anywhere. It would be cool if we could develop using typescript, and during the push command sfdx would build the lwc source to javascript before saving the components on the org. This could be an sfdx extension but it would be necessary to have all the Types defined. Wouldn't it work?
Yes, that's the goal. I would suggest you create an item in the IdeaExchange https://trailblazer.salesforce.com/ideaSearch (if there is not one already) and get a lot of people voting it up so it gets prioritize accordingly since the LWC team does not own the DX integration.
Even if LWC on platform doesn't "natively" run TypeScript, tooling could make it a near-native solution:
Here's the most applicable idea to upvote: https://ideas.salesforce.com/s/idea/a0B8W00000GdZyWUAV/enable-lightning-component-developers-to-use-typescript-and-ecmascript-6
This is still planned
Where can I provide feedback for this implementation? https://developer.salesforce.com/docs/platform/lwc/guide/ts.html#compile-your-typescript-components-to-javascript
I'm trying to use decorators and it's not clear how they are supposed to be handled. It works if I enable experimentalDecorators
, but the docs above say it should be disabled.
What version of TypeScript are you using? LWC currently uses non-experimental decorators, so you must be using v5 or later. You also must have target
set to "ESNext"
. I see that we don't mention target
in the documentation; I'll make sure to have that updated.
For some tips on adopting TypeScript, you can see the LWC v7 release notes.
@wjhsf I have the exact same problem, had a quick chat with Clay Martin about it, but wasn't able to make it work yet.
I've added ESNext too (which is not in the doc here), but that didn't helped. Setting experimentalDecorators
does work though (well at least compile doesn't fail).
Even with a basic component using a decorator (@api
):
import { LightningElement, api } from 'lwc';
export default class MyTypescriptLwc extends LightningElement {
@api recordId;
}
I'm getting the following error with experimentalDecorators
set to false
:
TS1240: Unable to resolve signature of property decorator when called as an expression. Argument of type 'ClassFieldDecoratorContext<MyTypescriptLwc, any> & { name: "recordId"; private: false; static: false; }' is not assignable to parameter of type 'string | symbol'.
Beside that, with this expected setup:
"compilerOptions": {
"target": "ESNext",
"experimentalDecorators": false
}
and with the same component but without the @api
, my LWC is compiled to this JS:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const lwc_1 = require("lwc");
class MyTypescriptLwc extends lwc_1.LightningElement {
}
exports.default = MyTypescriptLwc;
Deploying this to the org (Winter '25 Scratch Org, LWC and sfdx-project.json set to API 62.0), adding the component to a FlexiPage crashes with the following error:
Uncaught Action failed: flexipageEditor:node$controller$generate [exports is not defined]
throws at https://customer-speed-5460-dev-ed.scratch.lightning.force.com/auraFW/javascript/ZzhjQmRxMXdrdzhvS0RJMG5qQVdxQTdEcXI0cnRHWU0zd2xrUnFaakQxNXc5LjMyMC4y/aura_prod.js:139:15. Caused by: Action failed: flexipageEditor:node$controller$generate [exports is not defined]
eval()@modules/c/myTypescriptLwc.js:3:53
Object.buildFqnList()@https://customer-speed-5460-dev-ed.scratch.lightning.force.com/components/flexipageEditor/component.js:14:25
Object.generate()@https://customer-speed-5460-dev-ed.scratch.lightning.force.com/components/flexipageEditor/component.js:2:328
generate()@https://customer-speed-5460-dev-ed.scratch.lightning.force.com/components/flexipageEditor/node.js:1:443
We are several trying to use it and so far I've seen no one succeeding. cc @SCWells72
That's just the result of the TypeScript compilation step, correct? You'll need to update your module
to emit ESM (import
/export
stateements) rather than CommonJS (using exports
). Depending on your project setup, you might use "ESNext"
, "NodeNext"
, or "Preserve"
. See the TypeScript documentation for details on each option. You may also need to update the moduleResolution
option.
Yes correct.
According to the diagram on this page I understand that after the TypeScript compilation step I can just deploy the compiled JS to the org.
I already tried to add "module": "NodeNext"
to the tsconfig.json
as I saw it was configured like this at Dreamforce (even though it's not on the doc), so I have this config:
{
"extends": "../../../../.sfdx/tsconfig.sfdx.json",
"include": ["**/*.ts", "../../../../.sfdx/typings/lwc/**/*.d.ts"],
"exclude": ["**/__tests__/**"],
"compilerOptions": {
"target": "ESNext",
"module": "NodeNext",
"experimentalDecorators": false
}
}
Is it what you're talking about ? I also tried to had "module": "NodeNext"
but whatever config I use, the compiled JS is always the same.
It ends up, for a myTypescriptLwc LWC, in the myTypescriptLwc.js file, and this is what I deploy and makes the page crash in Lightning Experience.
@wjhsf ok it works with this config 🙂:
{
"extends": "../../../../.sfdx/tsconfig.sfdx.json",
"include": ["**/*.ts", "../../../../.sfdx/typings/lwc/**/*.d.ts"],
"exclude": ["**/__tests__/**"],
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"experimentalDecorators": false
}
}
I think the target
and module
parts of the compilerOptions
section are missing in the doc, that may be where we are struggling 🙂
The TypeScript error I reported before is still there, but it doesn't prevent the file from being generated. So I guess the error shouldn't be there but at least the compilation works 🙂
On the other hand, no error prevents compilation so even "real" ones ends up as a compiled LWC:
import { LightningElement } from 'lwc';
export default class MyTypescriptLwc extends LightningElement {
myNumber;
handleClick() {
return this.myNumber / 2;
}
}
I also tried to had "module": "NodeNext" but whatever config I use, the compiled JS is always the same.
NodeNext
is tricky -- it can emit both ESM and CJS, and it looks in your package.json
to pick which one. To use NodeNext
and emit ESM, you need to have "type": "module"
in your package.json
. You may encounter runtime errors, if you do this, because that setting also controls the behavior of node.
I think the
target
andmodule
parts of thecompilerOptions
section are missing in the doc, that may be where we are struggling 🙂
Yep, updated docs should be available soon. I think the next scheduled update is Tuesday.
The TypeScript error I reported before is still there, but it doesn't prevent the file from being generated.
Yep, that's the default behavior of TypeScript. If you want errors to prevent emitting JavaScript, you can set the compiler option noEmitOnError
to true
. (Depending on your tooling, this option may or may not be respected, but it's what to do if you use tsc
directly.)
Awesome, the noEmitOnError
did the trick.
On my side everything is working now except the @api
error that @zenibako also reported (I have to set experimentalDecorators
to false
to make it work). I'm using TypeScript 5.6.3 so it should work.
If you're using the Salesforce VS Code extension, then experimentalDecorators: true
is currently still required. The extension still uses an older version of LWC. (Using true
is required for up to v6, both values are supported in LWC v7, and false
is required since v8.) By the time TypeScript support graduates from developer preview, the VS Code extension will be up to date and require using false
, but for now I'll ensure our documentation reflects the current state of affairs.
If you're not using the VS Code extension, ensure your project is using LWC v7 or later.
Hi, @wjhsf. Semi-related, is there already an open issue here for making this work better with third-party tooling, i.e,. not having a concrete dependency on the contents of .sfdx/typings/lwc
? I tried to add support to Illuminated Cloud and while I could get things working quite well in the editor -- using its own bundled *.d.ts
files that already exist for strongly-typed dev in ES6 -- the need to set up a tsconfig.json
against files that are only created by the VS Code Extensions and that shouldn't ever be checked into version control creates an impediment to both interactive and automated third-party tooling integration.
I've already heard that that's tracked internally at Salesforce, but is there an outward-facing issue in some issue tracker -- this one or otherwise -- that I could use to track progress on third-party tooling enablement?
I don't know that we have an external issue for removing all of the .sfdx/typings/lwc
types, but I did open https://github.com/forcedotcom/lightning-language-server/issues/602 to track the LWC package, specifically.
Longer term, the goal is to entirely replace the extension's types with the @salesforce/lightning-types npm package, which will work with any toolchain. We're currently working on populating the package (which will eventually have types for ~200 components/modules!). As we add type definitions that conflict with the extension's types, we will remove them from the extension.
Thank you @wjhsf !
I'll live with that in the dev preview, but there may be some issues in VS Code until we get to the correct LWC version.
Switching experimentalDecorators
back to true
breaks the way the TS is compiled.
Basically, just for information, for a component being:
import { LightningElement, api } from 'lwc';
export default class MyTypescriptLwc extends LightningElement {
@api myNumber: string;
}
with this tsconfig.json
:
{
"extends": "../../../../.sfdx/tsconfig.sfdx.json",
"include": ["**/*.ts", "../../../../.sfdx/typings/lwc/**/*.d.ts"],
"exclude": ["**/__tests__/**"],
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"experimentalDecorators": false
}
}
I'm getting the decorator error, but if don't enable noEmitOnError
the LWC is compiled correctly:
import { LightningElement, api } from 'lwc';
export default class MyTypescriptLwc extends LightningElement {
@api
myNumber;
}
If I only change experimentalDecorators
to true
in tsconfig.json
, I'm getting this:
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { LightningElement, api } from 'lwc';
export default class MyTypescriptLwc extends LightningElement {
myNumber;
}
__decorate([
api
], MyTypescriptLwc.prototype, "myNumber", void 0);
It's admittedly less than ideal, but one thing you can do to paper over the issue is add // @ts-expect-error Modern decorators are incompatible with the Salesforce VS Code extension
. Or use // @ts-ignore
if you want it to work in VS Code and in other environments.
One issue I'm also seeing is that the default ESLint rules don't seem to like TypeScript very much. They yield a nice, generic ESLint: Parsing error: Unexpected token (xx:yy)
on the first strongly-typed property (or likely member) encountered in the type body. It looks like the resolution is to add dependencies on @typescript-eslint/parser and @typescript-eslint/eslint-plugin, though when I tried to do so, I ended up with version conflicts with what's already installed by the default package.json
from a CLI-created project.
Has anyone resolved this short of just disabling ESLint in TypeScript-based projects? Is this something that will be rolled back into the CLI project template's sfdx-project.json
and/or .eslintrc.json
?
It looks like the resolution is to add dependencies on @typescript-eslint/parser and @typescript-eslint/eslint-plugin, though when I tried to do so, I ended up with version conflicts with what's already installed by the default
package.json
from a CLI-created project.
What gets installed with a CLI-created project? And can you elaborate more on the conflicts? Is it just breaking changes to rules between versions of typescript-eslint, or is it something else?
Something to be aware of is that eslint-plugin-lwc was written using the babel parser. Because eslint only supports one parser per file, you will end up with some rules not working correctly. Most of the incompatibility is in code involving TypeScript features, because the parsers build different ASTs (their ASTs are mostly compatible for JS features). I've only seen false negatives occur, no false positives, so you shouldn't have to deal with any invalid errors.
One mitigation for this is to run eslint in two passes, one with each parser. I'm also hoping to eventually add typescript-eslint support to the plugin (https://github.com/salesforce/eslint-plugin-lwc/issues/153).
@wjhsf, I'm able to reproduce this very easily as follows:
$ sf project generate -d . -n eslint_typescript_error
$ cd eslint_typescript_error
Edit package.json
to use "eslint": "^8.57.1"
or the next step will fail due to https://github.com/forcedotcom/cli/issues/3065.
$ npm install
$ sf lightning generate component --type lwc -n tsComponent -d force-app/main/default/lwc
$ cd force-app/main/default/lwc/tsComponent
$ mv tsComponent.js tsComponent.ts
Edit tsComponent.ts
to be:
import { api, LightningElement } from 'lwc';
export default class TsComponent extends LightningElement {
@api myProperty: string;
}
Run ESLint and it fails as noted previously due to a presumptive lack of TypeScript config:
$ npx eslint tsComponent.ts
/path/to/eslint_typescript_error/force-app/main/default/lwc/tsComponent/tsComponent.ts
4:19 error Parsing error: Unexpected token (4:19)
✖ 1 problem (1 error, 0 warnings)
If you then try to add the aforementioned dependencies, you get the following failures:
$ npm install @typescript-eslint/parser --save-dev
npm warn ERESOLVE overriding peer dependency
npm warn While resolving: salesforce-app@1.0.0
npm warn Found: @typescript-eslint/parser@6.21.0
npm warn node_modules/@typescript-eslint/parser
npm warn @typescript-eslint/parser@"^6.11.0" from @salesforce/wire-service-jest-util@4.1.4
npm warn node_modules/@salesforce/wire-service-jest-util
npm warn @salesforce/wire-service-jest-util@"4.1.4" from @salesforce/sfdx-lwc-jest@5.1.0
npm warn node_modules/@salesforce/sfdx-lwc-jest
npm warn 2 more (@typescript-eslint/eslint-plugin, the root project)
npm warn
npm warn Could not resolve dependency:
npm warn peer @typescript-eslint/parser@"^6.0.0 || ^6.0.0-alpha" from @typescript-eslint/eslint-plugin@6.21.0
npm warn node_modules/@typescript-eslint/eslint-plugin
npm warn @typescript-eslint/eslint-plugin@"^6.11.0" from @salesforce/wire-service-jest-util@4.1.4
npm warn node_modules/@salesforce/wire-service-jest-util
npm warn 1 more (eslint-plugin-jest)
npm warn ERESOLVE overriding peer dependency
npm warn While resolving: salesforce-app@1.0.0
npm warn Found: @typescript-eslint/parser@6.21.0
npm warn node_modules/@typescript-eslint/parser
npm warn @typescript-eslint/parser@"^6.11.0" from @salesforce/wire-service-jest-util@4.1.4
npm warn node_modules/@salesforce/wire-service-jest-util
npm warn @salesforce/wire-service-jest-util@"4.1.4" from @salesforce/sfdx-lwc-jest@5.1.0
npm warn node_modules/@salesforce/sfdx-lwc-jest
npm warn 2 more (@typescript-eslint/eslint-plugin, the root project)
npm warn
npm warn Could not resolve dependency:
npm warn peer @typescript-eslint/parser@"^6.0.0 || ^6.0.0-alpha" from @typescript-eslint/eslint-plugin@6.21.0
npm warn node_modules/@typescript-eslint/eslint-plugin
npm warn @typescript-eslint/eslint-plugin@"^6.11.0" from @salesforce/wire-service-jest-util@4.1.4
npm warn node_modules/@salesforce/wire-service-jest-util
npm warn 1 more (eslint-plugin-jest)
and:
$ npm install @typescript-eslint/eslint-plugin --save-dev
npm error code ERESOLVE
npm error ERESOLVE unable to resolve dependency tree
npm error
npm error While resolving: salesforce-app@1.0.0
npm error Found: @typescript-eslint/parser@6.21.0
npm error node_modules/@typescript-eslint/parser
npm error dev @typescript-eslint/parser@"^6.21.0" from the root project
npm error
npm error Could not resolve dependency:
npm error peer @typescript-eslint/parser@"^8.0.0 || ^8.0.0-alpha.0" from @typescript-eslint/eslint-plugin@8.11.0
npm error node_modules/@typescript-eslint/eslint-plugin
npm error dev @typescript-eslint/eslint-plugin@"*" from the root project
npm error
npm error Fix the upstream dependency conflict, or retry
npm error this command with --force or --legacy-peer-deps
npm error to accept an incorrect (and potentially broken) dependency resolution.
npm error
npm error
npm error For a full report see:
npm error C:\Users\Scott\AppData\Local\npm-cache\_logs\2024-10-22T19_26_11_914Z-eresolve-report.txt
npm error A complete log of this run can be found in: C:\Users\Scott\AppData\Local\npm-cache\_logs\2024-10-22T19_26_11_914Z-debug-0.log
Let me know if that doesn't provide a sufficient level of detail around this issue.
Short version: Upgrade to @salesforce/sfdx-lwc-jest
to latest (currently 7.0.0
) and then you should be able to install as normal. This might break your tests, but only because of dependency bumps.
Long version:
Problem
@salesforce/sfdx-lwc-jest@5.1.0
.@salesforce/sfdx-lwc-jest@5.1.0
uses @salesforce/wire-service-jest-util@4.1.4
(source).@salesforce/wire-service-jest-util@4.1.4
has a dependency on @typescript-eslint/eslint-plugin@^6.11.0
and @typescript-eslint/parser@^6.11.0
(source).
@typescript-eslint/eslint-plugin
has a peer dependency on @typescript-eslint/parser
, but not vice versa.npm install @typescript-eslint/parser
seems to identify that we already have the package in our dependencies, and installs a matching version, 6.21.0
.npm install @typescript-eslint/eslint-plugin
does not identify that we're already using 6.21.0
and tries to install latest, 8.11.0
. It has a peer dependency on @typescript-eslint/parser@8.11.0
, but we have 6.21.0
installed, so npm complains.I'm not 100% sure of what's going on with npm in those last two steps, but that's what seems to be happening.
Solution
Any of these actions will fix the issue:
npm install @salesforce/sfdx-lwc-jest@latest @typescript-eslint/eslint-plugin @typescript-eslint/parser
. This updates @salesforce/wire-service-jest-util
to the fixed version.
@salesforce/sfdx-lwc-jest@5.1.0
, but latest is currently 7.0.0
. Be aware that this may break your tests, although per the release notes, it seems like mostly dependency upgrades.Add "@salesforce/wire-service-jest-util": "^4.1.5"
to your package.json overrides
, run npm install
, and then run npm install @typescript-eslint/eslint-plugin @typescript-eslint/parser
.
npm install @typescript-eslint/eslint-plugin @typescript-eslint/parser --legacy-peer-deps
. Legacy peer dep behavior is more lenient.
npm install @typescript-eslint/eslint-plugin; npm install @typescript-eslint/parser
. I don't know why it works if you install them this way around, but it does!
Do nothing! The typescript-eslint packages are already (accidentally) included. So you can use them in your eslint config without installing anything. They're older versions, though, so you may get a warning about unsupported TypeScript versions.
@salesforce/sfdx-lwc-jest
or @salesforce/wire-service-jest-util
.I'll also follow up internally to get generated projects using the latest sfdx-lwc-jest
.
Thanks so much for your in-depth analysis of this issue and the potential solutions/workaround. With your recommendation plus this config info:
https://typescript-eslint.io/getting-started/
I've been able to get it going nicely. Again, much appreciated, and anything you can do to help make sure this is on the appropriate teams' lists.
I've verified that you can now install the typescript-eslint
packages without issue when using the newest nightly
build of the CLI, v2.65.1.
Is your feature request related to a problem? Please describe. No
Describe the solution you'd like Add typescript support
Describe alternatives you've considered NA
Additional context The industry of frontend frameworks are going towards Typescript, Thinking on that, will LWC support typescript in the near future?