forcedotcom/sf-skills/experience-lwc-typescript-migrate
Claude Code and agent skill by forcedotcom/sf-skills
- community
- ext:[{"type":"hook","description":"Pre/post lifecycle hooks"}]
Checking that the SKILL.md file is still in the repository.
Use when converting an existing JavaScript Lightning Web Component (.js, .html, .css) to TypeScript with full type annotations and a matching `.d.ts` file that exposes only the component's `@api` surface. TRIGGER when the user says \"convert LWC to TypeScript\", \"migrate LWC to TS\", \"rename .js to .ts for this component\", \"add types to my LWC\", \"generate .d.ts for this LWC\", \"type-annotate @api properties\", or \"produce declare module 'c/componentName' definitions\". DO NOT TRIGGER when the user is authoring a brand-new LWC from scratch (use experience-lwc-generate), generating Jest tests for an existing LWC (use experience-lwc-generate), or migrating an Aura component to LWC.
Install
npx vskill@latest install forcedotcom/sf-skills/experience-lwc-typescript-migrateSemi-extensibleMentions customization, but needs skill-specific knowledge. Read the extensible skills standard
SKILL.md
Converting LWC to TypeScript
Convert a Lightning Web Component bundle from JavaScript to TypeScript. The deliverable is a fully-typed .ts implementation plus a .d.ts file that only exposes @api members (the public surface other LWCs consume).
When to Use This Skill
- User wants to migrate a single component or a folder of components from
.js to .ts.
- User needs a
.d.tsfor an existing LWC so other components (or an
external TypeScript host) can import it safely.
- User is adding type annotations to an already-renamed
.tsLWC that
hasn't been properly typed yet.
- User wants JSDoc-style type hints upgraded to real TypeScript types.
Prerequisites
- The component builds and runs correctly in JavaScript today.
gitis available (the rename must preserve history viagit mv).- A TypeScript compiler is wired into the build (either the SFDX TS
pipeline or a standalone tsc step).
Workflow
Step 1 — Read the component
Open every file in the bundle:
Understand:
- What extends
LightningElement? What is the class name? - Which fields and methods carry the
@apidecorator? - Which properties/methods have existing JSDoc (use as a type hint
starting point, but validate against actual usage — JSDoc lies).
- Which parameters / return types can you infer from how the code is
called internally?
Step 2 — Rename .js → .ts using git mv
Repeat for any helper .js files in the bundle (unless they're already .ts). Never plain mv — that loses the history link TypeScript reviewers rely on.
Step 3 — Add type annotations in the .ts
Apply types in this priority order so you stop as soon as the public contract is solid:
- `@api` properties and methods first. Generate JSDoc if it's
missing, then translate JSDoc types to TS syntax (string, number, boolean, Promise<T>). Validate each JSDoc claim against the code before trusting it.
- Complex shapes become `interface` or `type` aliases — not inline
shapes repeated everywhere.
- Optional members use `?` only when the value is genuinely allowed
to be undefined. Do not sprinkle ? defensively.
- Private/internal state — still type it, but don't export the
types. Use private for members that must never be touched by consumers.
- Event handlers — prefer precise DOM event types:
MouseEventforonclick(and other click-like handlers).click
is dispatched as a MouseEvent — including keyboard-activated clicks — so typing it as PointerEvent would let handlers rely on pointer-only fields (pointerType, pressure, etc.) that are undefined in those cases.
PointerEventforonpointerdown/onpointerup/onpointermove
and other pointer* handlers where pointer-specific fields are actually meaningful.
CustomEvent<{ detail: ... }>for LWC custom events.Eventis the last resort; document why when using it.
- Async methods always return
Promise<T>— never bareT. - Avoid `any`. If you genuinely can't type something, use
unknown
and narrow with a type guard.
Reference patterns
Load [[assets/type-patterns.ts|assets/type-patterns.ts]] as an inline example covering property types, method types, and event handler types.
Step 4 — Generate the .d.ts
Create componentName.d.ts next to the .ts. It must:
- Contain only `@api` members — no private state, no internal
methods, no lifecycle hooks unless they are themselves @api.
- Preserve
@apiJSDoc verbatim (including@type,@required,
@default, @param, @returns tags) directly above each declaration.
- Declare the LWC module namespace
c/componentName(or the org's
namespace if different).
Template: load [[assets/dts-template.ts|assets/dts-template.ts]] as the starting .d.ts shape.
If the component has no @api members, still produce the module declaration with a comment explaining there's no public surface — don't skip the file.
Step 5 — Compile and test
- Run the TypeScript compiler (
tsc --noEmitor the build's equivalent).
Resolve every error before calling it done; no @ts-ignore patches.
- Run the component's existing Jest tests. The behavior should be
identical.
- Run the bundled consumer-finder unconditionally — empty output is a
valid result, not a reason to skip. The script resolves the search paths from sfdx-project.json's packageDirectories (or falls back to <project-root>), rejects any entry that escapes the project root, and performs the LWC-import search internally so the invocation is fully deterministic:
For each match, confirm the consumer's expected types still align with the new .d.ts public surface.
Step 6 — Expected final bundle shape
Verification Checklist
Before conversion:
- [ ] Component is valid JS and all tests pass.
- [ ] You've identified every
@apimember and its intended type.
After conversion:
- [ ]
git mvwas used so history is preserved. - [ ] Every variable and parameter in the
.tshas a concrete type
(no implicit any).
- [ ] Complex object shapes live in
interface/typealiases, not
inline repeats.
- [ ] Optional
?is only on genuinely optional fields. - [ ]
.d.tsexists, declaresc/componentName, extends
LightningElement, includes only @api members.
- [ ] Every
@apiJSDoc is preserved verbatim in the.d.ts. - [ ]
tscpasses with zero errors; no@ts-ignoreoranyused as a
workaround.
- [ ] Jest tests still pass.
Common Pitfalls
- Using `any` to silence errors. Solve the actual type instead.
If the value is truly unknown, use unknown + a type guard.
- Including private members in the `.d.ts`. The
.d.tsis the
public contract. Internal lifecycle and helpers must not leak.
- Losing JSDoc during the rename. Scan before and after — JSDoc
comments on @api members must appear in both the .ts and .d.ts.
- Skipping `git mv`. Makes review miserable and confuses blame.
- Forgetting async return types.
foo()with anasynckeyword
always returns a Promise. Declare it.
- Typing `onclick` as `PointerEvent`.
clickis aMouseEvent
(keyboard-triggered clicks included), so PointerEvent fields like pointerType are undefined for those events. Type onclick as MouseEvent; reserve PointerEvent for onpointer* handlers. Use MouseEvent | TouchEvent only when the code branches on TouchEvent distinctly.
Support Resources
Security
- Security scanPassed, 93 of 100Pattern checks on the skill's files.
- Model reviewNot runA model review is scheduled for the next rescan.
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingskills/experience-lwc-typescript-migrate/SKILL.md
Popularity
- Repository stars
- 1,033
- Repository forks
- 348
- Repository npm downloads, 7 days
- 76,100
- 7-day trend score
- 60
Stars and forks are for the whole GitHub repository, not this skill alone. Downloads are for the repository's npm package. Refreshed 26 Sep 2026, a month ago.
Extension points
hookPre/post lifecycle hooks
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/forcedotcom/sf-skills/experience-lwc-typescript-migrate)