跳到主要内容
知仓学习社ZHICANG

experience-lwc-typescript-migrate

Use when converting an existing JavaScript Lightning Web Component (.js, .html, .css) to TypeScript with full type annotations and a matching `.d.ts…

不碰外部(只输出文字)无严重或高危命中forcedotcom/sf-skills

它会碰到什么

扫了多少4 个文本文件,14 KB
它会碰到什么不碰外部(只输出文字)
命中总数0 处
命中统计严重 0 · 高 0 · 中 0 · 低 0

这一栏是扫描器报的事实,不是结论。命中多不等于有毒(安全工具、规则库、示例脚本本来就会包含危险写法),命中少也不等于干净。它和你手上的凭据、文件、网络有什么关系,需要你自己看。

技能内容

<!-- adk-managed-skill -->

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.ts for an existing LWC so other components (or an

external TypeScript host) can import it safely.

  • User is adding type annotations to an already-renamed .ts LWC 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.
  • git is available (the rename must preserve history via git 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:

componentName/
├── componentName.js
├── componentName.html
├── componentName.css
└── (possibly) __tests__/, __utam__/, existing .d.ts

Understand:

  • What extends LightningElement? What is the class name?
  • Which fields and methods carry the @api decorator?
  • 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

git mv componentName/componentName.js componentName/componentName.ts

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:

  1. @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.

  1. Complex shapes become interface or type aliases — not inline

shapes repeated everywhere.

  1. Optional members use ? only when the value is genuinely allowed

to be undefined. Do not sprinkle ? defensively.

  1. Private/internal state — still type it, but don't export the

types. Use private for members that must never be touched by

consumers.

  1. Event handlers — prefer precise DOM event types:
  • MouseEvent for onclick (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.

  • PointerEvent for onpointerdown / onpointerup / onpointermove

and other pointer* handlers where pointer-specific fields are

actually meaningful.

  • CustomEvent<{ detail: ... }> for LWC custom events.
  • Event is the last resort; document why when using it.
  1. Async methods always return Promise<T> — never bare T.
  2. 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 @api JSDoc 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 --noEmit or 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:

"<skill_dir>/scripts/find-consumers.sh" "<project-root>" "<componentName>"

For each match, confirm the consumer's expected types still align with

the new .d.ts public surface.

Step 6 — Expected final bundle shape

componentName/
├── componentName.ts          # Main TypeScript implementation
├── componentName.html        # Template (unchanged)
├── componentName.css         # Styles (unchanged)
└── componentName.d.ts        # Type definitions (new)

Verification Checklist

Before conversion:

  • [ ] Component is valid JS and all tests pass.
  • [ ] You've identified every @api member and its intended type.

After conversion:

  • [ ] git mv was used so history is preserved.
  • [ ] Every variable and parameter in the .ts has a concrete type

(no implicit any).

  • [ ] Complex object shapes live in interface / type aliases, not

inline repeats.

  • [ ] Optional ? is only on genuinely optional fields.
  • [ ] .d.ts exists, declares c/componentName, extends

LightningElement, includes only @api members.

  • [ ] Every @api JSDoc is preserved verbatim in the .d.ts.
  • [ ] tsc passes with zero errors; no @ts-ignore or any used 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.ts is 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 an async keyword

always returns a Promise. Declare it.

  • Typing onclick as PointerEvent. click is a MouseEvent

(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

想直接用这个技能?

本站把开放许可(MIT / Apache 等)的技能按仓库打包整理到网盘,点一下转存到你自己的网盘,不用一个个从 GitHub 拉。许可未声明的技能只给原始仓库链接,不打包。

同名技能的其他版本

有 2 个不同仓库或目录里都有叫 experience-lwc-typescript-migrate 的技能。它们内容并不相同,别混用:

  • forcedotcom/sf-skills — Use when converting an existing JavaScript Lightning Web Component (.js, .html, .css) to T