lwc icon indicating copy to clipboard operation
lwc copied to clipboard

refactor(decorators): update decorator types @W-16373548@

Open wjhsf opened this issue 1 year ago • 5 comments

Details

~This PR updates the types for @api, @track, and @wire decorators to only work on components that extend LightningElement. This is technically a breaking change, but should be of minimal impact because the decorators do not work on non-LWC components.~ Decided to not move forward with this change. Many users extend classes that are typed as any, which is valid but does not satisfy the LightningElement constraint.

This PR also drops support for TypeScript's experimental decorators, requiring projects to use the modern, standard decorators (this is TypeScript's default since v5). This is also technically a breaking change, but because the LWC decorators are compiled into non-standard non-decorator code, the only requirement for pure LWC projects should be removing "experimentalDecorators": true from their tsconfig.json compiler options. This will be an impactful breaking change for LWC projects that use non-LWC experimental decorators on non-LWC classes. I don't know how many users that may be, but it feels like it's probably a small number?

Additionally, this PR makes changes to the @wire decorator to improve type validation. The wire function now validates that the config provided matches the config expected by the adapter, and it validates that the type of the decorated property matches the value emitted by the adapter. In the current version of LWC (v7.2.0), the example below passes type validation. But with the changes in this PR, the latter two uses of @wire would result in type errors.

type Config = { id: number }
type Book = { title: string, author: string }
declare const getBook: WireAdapterConstructor<Config, Book>

class Component extends LightningElement {
  @wire(getBook, { id: 123 }) valid?: Book
  @wire(getBook, { id: 123 }) invalidPropType?: Author
  @wire(getBook, { id: true }) invalidConfigType: Book
}

For bonus fun, reactive config props are accurately resolved! In the following example, '$bookId' is correctly resolved to number, and would pass validation, while '$authorName' is resolved to string, and would fail.

class Component extends LightningElement {
  bookId = 123
  authorName = 'Nolan Lawson'
  @wire(getBook, {id: '$bookId'} as const) valid?: Book
  @wire(getBook, {id: '$authorName'} as const) invalid?: Book
}

Not immediately relevant to this PR, but worth calling out: TypeScript determines the type of the decorator from the type of the property, not the other way around. This means that for @wire(getBook, config) book, the type of book cannot be inferred from getBook; book must always be explicitly typed.

Does this pull request introduce a breaking change?

  • 💔 Yes, it does introduce a breaking change. But only to the types!

Does this pull request introduce an observable change?

  • 🔬 Yes, it does include an observable change.

GUS work item

W-16373548

wjhsf avatar Jul 31 '24 17:07 wjhsf

/nucleus start

wjhsf avatar Aug 01 '24 17:08 wjhsf

/nucleus start

wjhsf avatar Aug 07 '24 19:08 wjhsf

/nucleus start

wjhsf avatar Aug 07 '24 19:08 wjhsf

/nucleus test

wjhsf avatar Aug 07 '24 19:08 wjhsf

/nucleus start

nolanlawson avatar Aug 07 '24 21:08 nolanlawson

These are nice improvements. These look specific to the wire, or does this also include better typing for method() calls imperatively?

nwcm avatar Sep 01 '24 23:09 nwcm

These are nice improvements. These look specific to the wire, or does this also include better typing for method() calls imperatively?

These changes are specific to @wire. Can you elaborate on what you mean by method() calls imperatively?

wjhsf avatar Sep 03 '24 14:09 wjhsf