question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Implicit function scope

See original GitHub issue

This is slightly related to #953 - the difference is I would like some code to not be visible at all. I am aware of addExtraLib and use that to inject some custom global functions and classes for IntelliSense purposes.

The use case I have is that I want a user to write a function’s body only (the function declaration is not part of the model). This works fine until, for instance, the user writes something like:

let varName = 0;

If varName is already declared somewhere with the addExtraLib call, this will give a “Cannot redeclare block-scoped variable ‘varName’.(2451)” error. (This won’t be the case during the actual execution, but there’s no way to tell monaco that!) It’s also impossible to have a return statement, as that will cause an “A ‘return’ statement can only be used within a function body. (1108)” error.

I would like the code to be validated as though it is in its own function scope, as is the case within a function, so this error is no longer reported.

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:7 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
spahnkecommented, Sep 22, 2020

If varName is already declared somewhere with the addExtraLib call, this will give a “Cannot redeclare block-scoped variable ‘varName’.(2451)” error.

You can work around that pretty easily by only including necessary types in your extra lib instead of the actual code you wrap the user’s code in. Consider the following function with parameters and a predefined global object foo with one method bar() that returns a number:

function implicitFunction(a: number, b: number) {
    // user code goes here
    return a + b + foo.bar();
}

Then you want your users to just write

return a + b + foo.bar();

into the editor and get completion for everything, as far as I understand. The return statements normally gives you an error which you can suppress by the method mentioned earlier. So you only need to supply a, b and foo in your extra lib and can use a .d.ts file for that:

interface Foo {
    bar(): number;
}

// global object
declare const foo: Foo;

// parameters - declare as var if you want the user to be able to change these, otherwise use const
declare var a: number;
declare var b: number;

This is how we use the editor for this exact use case. Provide the necessary types by a .d.ts file in the extra lib and suppress the error for the return statement. Since you only provide the necessary types there is no chance of name clashes and you probably don’t want the user to be able to overwrite the global objects anyway. This is why you declare the global object as const so the user gets an error if they overwrite/redeclare it, and use var for parameters if you want the user to be able to overwrite those.

Does this help you?

Full playground example adopted from https://microsoft.github.io/monaco-editor/playground.html#extending-language-services-configure-javascript-defaults:

// validation settings
monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({
    noSemanticValidation: false,
    noSyntaxValidation: false,
    noSuggestionDiagnostics: false,
    diagnosticCodesToIgnore: [1108],
});

// compiler options
monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
    target: monaco.languages.typescript.ScriptTarget.ES6,
    allowNonTsExtensions: true
});

// extra libraries
const libSource = `
interface Foo {
    bar(): number;
}

// global object
declare const foo: Foo;

// parameters
declare var a: number;
declare var b: number;
`
const libUri = 'ts:filename/facts.d.ts';
monaco.languages.typescript.javascriptDefaults.addExtraLib(libSource, libUri);
// When resolving definitions and references, the editor will try to use created models.
// Creating a model for the library allows "peek definition/references" commands to work with the library.
monaco.editor.createModel(libSource, 'typescript', monaco.Uri.parse(libUri));

const jsCode = `return a + b + foo.bar();`

monaco.editor.create(document.getElementById('container'), {
    value: jsCode,
    language: 'javascript'
});
0reactions
Wiiseguycommented, Sep 22, 2020

You’re right that it sounds a bit strange.

To explain my use case a bit more, users of our application can create all kinds of identifiers through a web UI that translate in code to classes/constants/functions.

A user had one of those classes named result and would later add a function with a let result = 0; statement in its body (as one does). Of course, this is perfectly valid if the code was in an actual function, you can just redeclare variables if you want. But as the extraLib and the user code resides in the same block-scope, this would hint to the user that they couldn’t use result. The code executes fine when tested, so there was a discrepancy.

You could say that they shouldn’t use the name ‘result’ as a class, but it’s how they happened to compose their model. 🤷‍♂️

Anyway, thanks again for the tips!

Read more comments on GitHub >

github_iconTop Results From Across the Web

Implicit Scope and Implicit Resolution in Scala - geekAbyte
The Implicitly Function​​ looks for an implicit value of type Person from within the current scope or the associated types of the Person...
Read more >
Implicits
The implicit scope of a type T consists of all companion modules of classes that are associated with the implicit parameter's type. Here,...
Read more >
Dynamic scoping is an effect, implicit parameters are a coeffect
The big innovation is the implicit function is that it resolves all dynamic references in the function (not just lexically, but for all...
Read more >
Chapter 5. Using implicits to write expressive code - Scala in ...
The implicit scope of a type is defined as all companion modules that are associated with that type. This means that if the...
Read more >
How to prefer implicit in method scope over class scope?
There is an implicit defined at the class / object level and I want to 'override' it with an implicit defined in the...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found