salesforce / lwc

⚡️ LWC - A Blazing Fast, Enterprise-Grade Web Components Foundation
https://lwc.dev
Other
1.63k stars 395 forks source link

Add TypeScript support to LWC authoring format #2078

Open AllanOricil opened 3 years ago

AllanOricil commented 3 years ago

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?

eight-molecules commented 3 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.

diervo commented 3 years ago

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.

AllanOricil commented 3 years ago

@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?

diervo commented 3 years ago

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.

timothywlewis commented 2 years ago

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

nolanlawson commented 1 year ago

This is still planned

zenibako commented 1 day ago

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.

wjhsf commented 1 day ago

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.

FabienTaillon commented 1 day ago

@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

wjhsf commented 1 day ago

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.

FabienTaillon commented 1 day ago

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.

FabienTaillon commented 1 day ago

@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 🙂

FabienTaillon commented 1 day ago

On the other hand, no error prevents compilation so even "real" ones ends up as a compiled LWC:

compile
import { LightningElement } from 'lwc';
export default class MyTypescriptLwc extends LightningElement {
    myNumber;
    handleClick() {
        return this.myNumber / 2;
    }
}
wjhsf commented 1 day ago

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 and module parts of the compilerOptions 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.)

FabienTaillon commented 1 day ago

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.

AllanOricil commented 1 day ago

relates: https://github.com/forcedotcom/cli/discussions/3032

wjhsf commented 1 day ago

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.

SCWells72 commented 1 day ago

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?

wjhsf commented 1 day ago

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.

FabienTaillon commented 23 hours ago

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);
wjhsf commented 23 hours ago

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.