- attached generate.ts
lwc-standard.d.ts Definitions Suboptimal
The lwc-standard.d.ts file (which I’m assuming is created by you given the copyright) contains module definitions for a bunch of built-in LWC elements, but with functions exported from the module itself.
It would be preferable if they were exported as proper TS classes mimicking the actual API properties and methods documented in the Component Reference.
I’ve made a quick script to generate these from the lwc-standard.json
file (attached). It doesn’t do the libraries (like Navigation) properly as it assumes they’re all components.
The declarations are most noticeable when making a custom Lightning Datatable, where one uses extend
on the base LightningDatatable class. In this specific example, it would be nice if it could inherit the @api
decorated properties so the vanilla props don’t show as unrecognised (in HTML templates).
It can also be useful when using JsDoc comments to aid IDEA’s type resolution:
import {LightningElement} from 'lwc';
import LightningInput from 'lightning/input'
export default class DemoComponent extends LightningElement {
demo() {
/** @type {LightningInput} myInput */
let myInput = this.template.querySelector('lightning-input')
myInput.value = 'foo'
}
}
In this example, myInput.value
is recognised as a field on LightningInput
for highlighting and autocomplete.
Comments (19)
-
reporter -
repo owner Thanks, Xander. Coincidentally I was thinking about this just yesterday when I updated the existing
lwc-standard.d.ts
based on the latestlwc-standard.json
and noticed that the properties were missing. I already have some changes in place to enrich it similar to what you've done. That won't be in this week's build as I'm about to release it, but it's looking good for next week's build. -
repo owner Xander, I've updated my own generator for
lwc-standard.d.ts
to be much closer to what you've provided. There are a few notable differences, though, and I thought I'd enumerate them here to see if they might cause issues or not:- I provide return type information for methods, e.g.,
generateUrl(pageReference: object): Promise<String>;
vs. yours which isgenerateUrl(pageReference: object);
. I doubt that will cause issues, and in fact I would think the additional return type information is only going to be valuable to the IDE's type inference engine. - I use
?
to specify optional formal parameters whereas you usetype|undefined
, e.g.,navigate(pageReference: object, replace?: boolean): void;
vs. yours which isnavigate(pageReference: object, replace: boolean|undefined);
. Any reason to think that would cause an issue? Perhaps some TypeScript(-to-ES6) nuance of which I'm not aware? - You have
lightning/slot
whereas I haveslot
. My understanding is thatslot
doesn't actually exist in thelightning
namespace: https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.create_components_slots
Those are the primary differences. Thoughts?
- I provide return type information for methods, e.g.,
-
reporter - Yeah, this was what I meant when I said it doesn’t do the libraries properly. Your current Navigation ones are better than my generated ones. I use a different version in my custom JS library (i.e. my projects end up with your auto-added one and a vanilla IDEA js library on the local filesystem). Pasted below.
- Functionally, I’m fairly certain they’re the same thing - I mostly just wasn’t sure how to represent the
?
in the AST library I was using. There’s even JsDoc’s{?boolean}
for@param
- You’re probably right as it seems to be a vanilla element they’ve co-opted (LWC being somewhat based on standard web components and all). My script just slaps
lightning/
in front of all the elements in the json. I can’t seem to actually import Slot, though the only reason to do so would probably be the@type {Slot}
notation (the input example doesn’t resolve without the import as I don’t think/
is valid in JsDoc)
const Navigate: unique symbol = Symbol('Navigate'); const GenerateUrl: unique symbol = Symbol('GenerateUrl'); declare module "lightning/navigation" { /** use with @wire, but directly assigns the pageref, not {data,error} */ function CurrentPageReference(config?: any): any; const NavigationMixin: { <T>(Base: T): T & NavClz readonly Navigate: typeof Navigate readonly GenerateUrl: typeof GenerateUrl }; class NavClz { [NavigationMixin.Navigate](pageReference: PageReference, replace?: boolean): void; [NavigationMixin.GenerateUrl](pageReference: PageReference): Promise<string>; } type PageReference = PageReferenceBase|PageReferenceRecordPage interface PageReferenceBase { type: PageRefType; attributes?: object; state: { [key: string]: string; }; } interface PageReferenceRecordPage extends PageReferenceBase { type: 'standard__recordPage', attributes: { actionName: 'clone' | 'edit' |'view' objectApiName?: string recordId: string } } type PageRefType = 'standard__app' | 'standard__component' | 'comm__loginPage' | 'standard__knowledgeArticlePage' | 'comm__namedPage' | 'standard__namedPage' | 'standard__navItemPage' | 'standard__objectPage' | 'standard__recordPage' | 'standard__recordRelationshipPage' | 'standard__webPage' }
Notes:
- I’ve made it use the “proper” Symbol types for the navigate/generateUrl calls, but functionally as long as they’re called via
NavigationMixin.X
it doesn’t matter much - I think I made it a concrete class was something to do with IDEA’s type resolution (but I can’t quite remember)
- The PageReference implementation is incomplete (i.e. doesn’t define the other types) - it seems to work without needing to be imported, and is able to help autocomplete (will attach image)
-
reporter - attached Screenshot_2021-07-02_104425.png
-
reporter Symbol: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol
Jest Stubs don’t seem to define Slot as in lightning namespace either: https://github.com/salesforce/sfdx-lwc-jest/tree/master/src/lightning-stubs
-
repo owner - attached lwc-standard.d.ts
Thanks, Xander. I'm attaching my latest version of
lwc-standard.d.ts
here if you want to take a look. It doesn't include any changes toNavigationMixin
, though I'm familiar with the goal of making it work with a strongly-typedPageReference
and have had a few false starts there. I'll play with what you've provided and see where it goes. Let me know if you have any feedback on the rest of it, though, as I'd love to get this nailed so that it's an accurate strongly-typed representation of the standard component types. -
reporter Looks pretty good, though the question mark syntax is valid for fields too, i.e.
export default class Accordion extends LightningElement { /** The activeSectionName changes the default expanded section. The first section in the accordion is expanded by default. */ @api activeSectionName?: string; ... }
Technically the event handler attributes (seems to be
Aura.Action
) should beEventListener
rather thanFunction
, though I’m not sure it matters too much since the JS controllers won’t really benefit that I can see (the function definition needs the JsDoc@param {Event}
). There are also ~11 variant-specific ones, likeMouseEventHandler
foronclick
(whether LWC uses them or not is another question, I suppose).Another cool thing (if you’re not averse to some regex), would be to use the syntax for a fixed set of values:
export default class Avatar extends LightningElement { /** The variant changes the shape of the avatar. Valid values are empty, circle, and square. This value defaults to square. */ @api variant?: 'empty'|'circle'|'square'; }
There are 17 instances of “valid values are” in the jsdocs
-
reporter Perhaps more relevant for the html autocomplete, but the pattern
(valid values are|accepted variants include|possible values are|options include) (('?[-a-z0-9]+'?, )*('?[-a-z0-9]+'? )?(?:and|or) '?[-a-z0-9]+'?).
has 47 matches! -
repo owner - attached lwc-standard.d.ts
Thanks for the feedback. Here's the latest version, and IC2 now uses the same allowed values information for code completion and validation:
Validation is also extended to Boolean and numeric types in template markup. There's more that could be validated, but this should go a LONG way toward better code completion and validation for standard LWC components.
Let me know if you have any further feedback. I'm going to target getting this out Wednesday or Thursday morning.
-
repo owner - attached lwc-standard.d.ts
Attaching a slightly newer version that includes a previously-missed variant of the enumerated values pattern. I may encounter others as I continue testing/reconciling.
-
reporter Holy balls, that’s awesome!
I think the only extra thing you could do would be to manually adjust some of the
object
andany
types, depending on how granular you want to go.e.g.
options: {value:string, label:string}[]
for combo boxes and a couple others (orArray<{value:string…}>
, same thing) -
repo owner - attached lwc-standard.d.ts
Another take, this time making those fields with a known schema more strongly-typed. It's not 100% perfect, but it's getting much, much closer. At this point unless you see issues with what I did in this last pass, I'll plan to include this in Thursday morning's build and we'll handle anything else as a one-off correction. Thanks so much for the input on this!
-
reporter Looks good!
- lightning/buttonStateful has a few
\'
in the comments - lightning-input.min/max seem to beable to be defined as
string|number
- lightning-input.files can be typed
FileList
(from dom library) - I found some more datatable definitions on my side, see below. NB the interfaces are exported to allow use in JSdoc. My sandbox accepted code that contained
import {thiscantpossiblyexist} from 'lightning/datatable'
` - Agreed anything else can be as-needed
declare module 'lightning/datatable' { import { LightningElement, api } from 'lwc'; export interface RowActionEvent<ROWVALUE = any> extends Event { detail: RowDetail<ROWVALUE>; } export interface RowDetail<ROWVALUE = any> { /** The action that triggered the event */ action: RowAction; /** The row's value from the table's data attribute */ row: ROWVALUE; } export interface RowAction { /** Required. The label that's displayed for the action. */ label: string; /** Required. The name of the action, which identifies the selected action.*/ name: string; /** Specifies whether the action can be selected. If true, the action item is shown as disabled. This value defaults to false. */ disabled?: boolean; /** The name of the icon to be displayed to the right of the action item. */ iconName?: string } export interface DataTableColumn { /** Appends a dropdown menu of actions to a column. You must pass in a list of label-name pairs.*/ actions?: RowAction[]; /** Provides additional customization, such as appending an icon to the output. For more information, see Appending an Icon to Column Data*/ cellAttributes?: CellAttributes; /** Specifies whether a column supports inline editing. The default is false.*/ editable?: boolean; /** Required. The name that binds the columns attributes to the associated data. Each columns attribute must correspond to an item in the data array.*/ fieldName: string; /** Specifies the width of a column in pixels and makes the column non-resizable.*/ fixedWidth?: number; /** The Lightning Design System name of the icon. Names are written in the format standard:opportunity. The icon is appended to the left of the header label.*/ iconName?: string; /** The width of the column when it's initialized, which must be within the min-column-width and max-column-width values, or within 50px and 1000px if they are not provided.*/ initialWidth?: number; /** Required. The text label displayed in the column header.*/ label: string; /** Specifies whether the column can be sorted. The default is false.*/ sortable?: boolean; /** Required. The data type to be used for data formatting. For more information, see Formatting with Data Types.*/ type?: 'action' | 'boolean' | 'button' | 'button-icon' | 'currency' | 'date' | 'date-local' | 'email' | 'location' | 'number' | 'percent' | 'phone' | 'text' | 'url' | string; /** Provides custom formatting with component attributes for the data type. For example, currency-code for the currency type.*/ typeAttributes?: TypeAttributes; } export interface CellAttributes { /** Required. The Lightning Design System name of the icon, for example, utility:down.*/ iconName: string; /** The label for the icon to be displayed on the right of the icon.*/ iconLabel?: string; /** The position of the icon relative to the data. Valid options include left and right. This value defaults to left.*/ iconPosition?: 'left' | 'right'; /** Descriptive text for the icon.*/ iconAlternativeText?: string; alignment?: 'left'|'right'|'center'; } type stringOrFieldRef = string | { fieldName: string } /** See 'Formatting with Data Types' at https://developer.salesforce.com/docs/component-library/bundle/lightning-datatable/documentation for more details */ export interface TypeAttributes { alternativeText?: string; class?: string; currencyCode?: string; currencyDisplayAs?: string; day?: 'numeric' | '2-digit'; disabled?: boolean; era?: 'narrow' | 'short' | 'long'; hour?: 'numeric' | '2-digit'; hour12?: boolean; iconClass?: string; iconName?: string; iconPosition?: string; label?: stringOrFieldRef; latitude?: string; longitude?: string; maximumFractionDigits?: number; maximumSignificantDigits?: number; menuAlignment?: string; minimumFractionDigits?: number; minimumIntegerDigits?: number; minimumSignificantDigits?: number; minute?: 'numeric' | '2-digit'; month?: 'narrow' | 'short' | 'long' | '2-digit'; name?: string; rowActions?: RowAction[]; second?: 'numeric' | '2-digit'; target?: string; timeZone?: string; timeZoneName?: string; title?: string; tooltip?: string; value: stringOrFieldRef; variant?: string; weekday?: 'narrow' | 'short' | 'long'; year?: 'numeric' | '2-digit'; } interface DatatableError { title: string; messages: Array<string>; } type DatatableRowError = DatatableError & {fieldNames?: Array<string>} interface DatatableErrorValues { /** Row level errors */ rows?: {[rowKey:string]: DatatableRowError} /** Table level errors */ table?: DatatableError } /** A table that displays columns of data, formatted according to type. This component requires API version 41.0 and later. */ export default class Datatable extends LightningElement { //<SNIP> @api columns?: Array<DataTableColumn>; /** The array of data to be displayed. */ @api data?: Array<object>; //<SNIP> /** Specifies an object containing information about cell level, row level, and table level errors. When it's set, error messages are displayed on the table accordingly. */ @api errors?: DatatableErrorValues; //<SNIP> /** The action triggered when a row action is clicked. By default, it also closes the row actions menu. Returns the eventDetails object. */ @api onrowaction?: (ev:RowActionEvent)=>void; //<SNIP> } }
- lightning/buttonStateful has a few
-
repo owner - attached lwc-standard.d.ts
Wonderful! Please let me know if this one is the winner. Thanks again!
-
reporter Winner winner, chicken dinner!
-
repo owner Xander, I had planned to put this out in a build this morning, and that build is ready to go, but I've had something unexpected come up that will likely delay it until next Tuesday morning. I'm happy to post the build here if you'd like to toy with it, but I don't want to put out a broad release until I'm settled back in early next week.
-
reporter All good, unless you need a beta tester
-
repo owner - changed status to resolved
Delivered in 2.1.8.3. Thanks again for all the input, education, verification, etc., Xander! There are certainly a few other things to be done (likely more than a few), but this should establish a much better new foundation.
- Log in to comment
Generation script. Requires node modules
ts-morph
&typescript