LWC NavigationMixin is not recognised by code inspections

Issue #1870 resolved
Aidan Harding created an issue

If you write an LWC using NavigationMixin (https://developer.salesforce.com/docs/component-library/bundle/lightning-navigation/documentation), both the import and the usage are flagged as being unrecognised by IC2.

For example (from the above page):

import { NavigationMixin } from 'lightning/navigation';

export default class Example extends NavigationMixin(LightningElement) {
    navigateToObjectHome() {
        // Navigate to the Account home page
        this[NavigationMixin.Navigate]({
            type: 'standard__objectPage',
            attributes: {
                objectApiName: 'Account',
                actionName: 'home',
            },
        });
    }

Line 1 has a warning that NavigationMixin is an unresolved symbol. And then this[NavigationMixin.Navigate] is also unrecognised.

Comments (17)

  1. Scott Wells repo owner

    Aidan, I just committed a fix for this, but I'm not entirely happy with it. Basically I added the following mixin definition to the corresponding .d.ts file:

        class NavigationMixinType
        {
            GenerateUrl(pageReference: object): Promise<string>;
            Navigate(pageReference: object, replace?: boolean): void;
        }
    
        export function NavigationMixin<SuperclassType extends typeof LightningElement>(superclass: SuperclassType): SuperclassType & NavigationMixinType;
    

    With that in place, the following all properly evaluates without inspection errors:

    import {LightningElement} from 'lwc';
    import {NavigationMixin} from "lightning/navigation";
    
    export default class Example extends NavigationMixin(LightningElement) {
        recordPageUrl;
    
        doSomething() {
            this[NavigationMixin.Navigate]({
                type: 'standard__objectPage',
                attributes: {
                    objectApiName: 'Account',
                    actionName: 'home'
                }
            });
    
            this[NavigationMixin.GenerateUrl]({
                type: 'standard__recordPage',
                attributes: {
                    recordId: 'xxxyyyzzz',
                    actionName: 'view'
                }
            }).then(url => {
                this.recordPageUrl = url;
            });
        }
    }
    

    However, the IDE doesn't provide Navigate or GenerateUrl code completions from the NavigationMixin. stem in the this[NavigationMixin.*] expressions.

    I confess that I'm far from a TypeScript/ES6 expert (and that's actually gross understatement), so perhaps this isn't the right way to define an ES6 mixin in a TypeScript definition file? I also saw several threads on the topic that said this this stuff is still not 100% baked.

    If you're aware of a better way to define this that addresses the code inspection issues while also providing the correct code completions, please feel free to let me know.

  2. Aidan Harding reporter

    Thanks, Scott!

    I’m afraid I can’t suggest any better solutions. I’m a novice with TS, and perhaps a beginner with LWCs. I’ve sent this thread to someone who is more knowledgeable than me, so hopefully, they will be able to comment on any possibility of a neater solution.

  3. Charlie Jonas

    I haven’t really done any lwc dev so I’m not familiar with the NavigationMixin … I also haven’t really used the “mixin” pattern much but there’s decent documentation

    Declaring ambient types is often more confusing and harder than typing the implementation. This github issues explains how to do it for mixins.

    We also need to leverage declaration merging to provide the static GenerateUrl properties (not sure why these are really needed… why not just call the method by name directly?).

    Putting all of that together, I think you’d actually want something like this:

    declare class NavigationMixin {
        static readonly GenerateUrl = 'GenerateUrl';
        static readonly Navigate = 'Navigate';
    }
    
    type Constructor<T = {}> = new (...args: any[]) => T;
    
    declare class NavigationElement {
        public GenerateUrl(pageReference: object): Promise<string>;
        public Navigate(pageReference: object, replace?: boolean): void;
    }
    
    declare function NavigationMixin<T extends Constructor<LightningElement>>(
        superClass: T,
    ): Constructor<NavigationElement> & T;
    

    Here’s a proof of concept in ts-playground

  4. Scott Wells repo owner

    Thanks, Charlie! That actually works perfectly. And it kind of taught me something about TypeScript, though without actually using it on a regular basis, it's still pretty muddy to me. Nonetheless, I appreciate the guidance!

  5. Charlie Jonas

    np! I enjoy writing typings waaaay more than I should 😅

    PS: I’d also type pageReference if possible. Should really improve the dev experience. In typescript, object should be avoid as much as any

  6. Charlie Jonas

    @Scott Wells actually at first glance it should be possible to provide true type-safety here. This is where typescripts algebraic typing and type-inference really is a thing of beauty!

    This should give you an idea of how it might work. I think the challenge here would be translating there docs into accurate domain model types as it’s maybe not totally clear what is allowed.

    Playground Link

  7. Scott Wells repo owner

    Once again, thanks, @Charlie Jonas. I'd actually started doing something similar, but as before it was missing some key aspects. I'm still seeing two issues, though, both of which may be limitations in the current JetBrains TS support:

    1. When I use your TS Playground example, it's not unambiguously resolving, say, type to the correct PageReference variant based on its value: TS_type_ambiguity.png
    2. When I use the this[NavigationMixin.Navigate](...) syntax with the params of that function strongly-typed to PageReference, it doesn't seem to see that as an invocation of Navigate.

    So as I said, these might just be (known?) limitations of the JetBrains TS functionality. I'll try stitching it all together in TS Playground to see if it works as expected/desired there, and if it does but doesn't in the JetBrains IDEs, I'll log a support ticket with them accordingly.

    Thanks (yet) again!

  8. Charlie Jonas

    It should work: Playground Link.

    It’s likely that this requires one of the more recent version of typescript. Maybe check what version your JetBrains project is using.

  9. Charlie Jonas

    The other culprit would be your ts-config.json. Some of the more complex type inference features can only work if you are using the strict settings.

  10. Scott Wells repo owner

    I just copy/pasted that TS Playground snippet into a JetBrains TypeScript scratch window and, while everything validates properly, it's definitely not able to infer unambiguous references to the various PageReference types:

    TS_type_ambiguity.png

    The bundled version of TypeScript is 4.2.2. It looks like the latest is 4.2.4? Would you expect this type of behavioral difference in two patch-level releases? Also, that occurs with "use strict"; at the top of the file which should(?) force strict behavior, correct?

    Sorry for what may be dumb questions. This is now so far out of my area of expertise as to be silly, but I'm enjoying the education!

  11. Charlie Jonas

    Ya being off by 2 patch versions shouldn’t make any difference.

    "use strict"; actually is a javascript thing and doesn’t have any impact on the typescript LSP (AFIK).

    What I was referring to is the tsconfig strict property.

    However, enabling this in a project that wasn’t previously using it is likely to create a bunch of errors! I’m not sure which config property(s) is actually the culprit here.

    In general, if your tsconfig & typescript version matches that of the ts-playground, you should see the same behavior.

  12. Scott Wells repo owner

    In general, if your tsconfig & typescript version matches that of the ts-playground, you should see the same behavior.

    Maybe, maybe not. I know that the JetBrains TS stuff can be configured to the use the TypeScript language server, but I also know that they're doing quite a bit of parsing, type inference, and reference evaluation themselves just as they do with all of their other language plugins (and just like IC does for the Salesforce-specific languages). I just disabled use of the language server and I still see the same behavior, so I'm fairly certain it's their own parser doing this.

    I'll log a bug with them. I'll go ahead and add PageReference as you've started to define it as it does provide much improved code completion and validation vs. just object, but it looks like it's not quite perfect.

    Thanks again!

  13. Scott Wells repo owner

    Actually I take back the last thing I said above. Code completion and validation work well when the client code is in TS, but when it's in JS/ES6, not so much. The good news is that the original mix-in declarations work pretty well, so I'll include them in the next build of IC2; the bad news is that the attempt at providing a strongly-typed PageReference doesn't work any better than just using object. It certainly should...but it doesn't. I'll see if I can distill it into a very simple reproducible test case and log a bug with JetBrains. Thanks!

  14. Scott Wells repo owner

    Delivered in 2.1.7.4. If/when JB provides a fix for the issues with PageReference I'll add that as well, but for now at the very least, NavigationMixin should work properly.

  15. Log in to comment