【问题标题】:Different Typescript inference management between vscode and monaco-editorvscode和monaco-editor之间的不同Typescript推理管理
【发布时间】:2020-03-09 02:08:56
【问题描述】:

我刚刚做了一个复杂的函数,它接受 3 个参数:名称、类型和方法。 此函数将方法存储在商店中。它从第二个参数推断出第三个参数的返回类型。

addMethod.d.ts

interface SimplifiedTypeMap {
  string: string;
  number: number;
  boolean: boolean;
}

type GlobalMethodAdd = <T extends keyof SimplifiedTypeMap>(
  name: string,
  types: T[],
  method: () => SimplifiedTypeMap[T]
) => void;

interface MethodStore {
  [name: string]: {
    types: (keyof SimplifiedTypeMap)[];
    method: () => SimplifiedTypeMap[keyof SimplifiedTypeMap];
  };
}

感谢打字稿引擎,最后一个参数(方法)的返回类型是从第二个参数(类型)中的项目推断出来的,int 强制函数的用户编写具有特定返回类型的方法

addMethod.ts

import { random } from "lodash-es";

export const methodStorage: MethodStore = {};

const addMethod: GlobalMethodAdd = (name, types, method) => {
  methodStorage[name] = { types, method };
};

addMethod("test", ["string", "number"], () =>
  random(1, true) > 0.5 ? "abcd" : 1234
);

当我在 Visual-Studio Code 或 Codesandbox 上使用 addMethod 函数时,第三个参数的返回类型是众所周知的,但在 monaco-editor 上却不是:

视觉工作室代码

代码沙盒编辑器

(失败)摩纳哥编辑器

Here is my example in codesandbox

================================================ ============================

编辑 我发现是 noLib 编译器选项的使用导致了这种情况发生。

reactMonaco.languages.typescript.typescriptDefaults.setCompilerOptions({
  target: reactMonaco.languages.typescript.ScriptTarget.ES5,
  noLib: true,
  allowNonTsExtensions: true,
  checkJs: true,
});

有没有办法在保持推理正常工作的同时避免 es5 libs 自动完成?

【问题讨论】:

  • 评论 noLib 对我有用。

标签: typescript visual-studio-code intellisense type-inference monaco-editor


【解决方案1】:

TL;DR

在 Monaco Editor 实施适当的解决方案之前,只需使用 monaco.languages.typescript.typescriptDefaults.addExtraLib(YOUR_CUSTOM_LIBRARY,"defaultLib:lib.es6.d.ts") 并且完全不要使用 setCompilerOptions。您可以通过组合https://github.com/microsoft/TypeScript/tree/master/lib 中所需的部分来创建自己的自定义库。

加长版

我也在使用 monaco 编辑器并且遇到了同样的问题。浏览了一下monaco源代码后,我想我找到了一个不需要修改monaco编辑器源代码的解决方案(workaround?)。

首先,让我们看一下当前的行为。 Monaco Editor 使用所谓的LanguageServiceHost 来实现TypeScript 相关功能,另见Using the Language Service API

它[语言宿主]抽象了语言服务与外部世界之间的所有交互。语言服务宿主将输入文件的管理、监控和维护工作交给宿主。

Monaco 编辑器的 TypeScript 语言宿主是 microsoft/monaco-typescript 存储库的一部分,可以在 tsWorker.ts 中找到。

现在,如果我们查看源代码,我们发现只有 two library files 是 Monaco Editor 的 currently supported

lib.d.tscompiler option target 设置为ES5 时使用,否则使用lib.es6.d.ts。更多调试表明,无论我们为lib 选项设置什么,这两个文件之一都用作库。正如您提到的,我们可以使用 noLib 选项,但这会导致其他副作用,例如类型推断不再正常工作。


所以现在我的想法是,大多数时候,您不想要图书馆,而是想要一个不同的图书馆。 DOM 特定部分(我认为您不想要)在lib.dom.d.ts 中,它包含在lib.d.ts 中。 通常,最好添加至少lib.es5.d.ts,其中包含诸如ObjectFunctionNumber 等JavaScript 基础类型;或者可能是lib.es2015.d.ts,其中包含Symbol、新的数组方法等等。所以问题就变成了:我们如何让 Monaco Editor 使用自定义的——可能是空的——库?

monaco-typescript 中,库源代码硬编码在文件lib.ts 中。因此,如果我们使用诸如 webpack 之类的捆绑程序,我们可以只用我们自己的版本替换该文件,该版本使用我们想要使用的库导出常量 lib_dtslib_es6_dts。这个选项可能并不总是可行的,而且修改库的源代码也不是一个好主意。

但是,如果我们再看看tsWorker.tshow it retrieves the content for a given file name,就会出现另一种解决方案(解决方法?):如果我们使用特定名称的add an extra lib,将使用额外的库而不是内置库.

  • 对于lib.d.ts,使用文件名defaultLib:lib.d.ts
  • 对于lib.es6.d.ts,使用文件名defaultLib:lib.es6.d.ts

总而言之,这是一个可以在Monaco Editor playground 上运行的代码示例。请注意,这会将目标设置为ES5(如您的示例所示),但我们也根本无法修改编译器选项,而在添加额外库时只需使用lib.es6.d.ts

const code = `
interface SimplifiedTypeMap {
    string: string;
    number: number;
    boolean: boolean;
}

type GlobalMethodAdd = <T extends keyof SimplifiedTypeMap>(
    name: string,
    types: T[],
    method: () => SimplifiedTypeMap[T]
) => void;

interface MethodStore {
    [name: string]: {
        types: (keyof SimplifiedTypeMap)[];
        method: () => SimplifiedTypeMap[keyof SimplifiedTypeMap];
    };
}

export const methodStorage: MethodStore = {};

const addMethod: GlobalMethodAdd = (name, types, method) => {
    methodStorage[name] = { types, method };
};

// Try hovering over addMethod, type inferrence works correctly
addMethod("test", ["string", "number"], () =>
    1 > 0.5 ? "abcd" : 1234
);

// No DOM library loaded, so this gives an error
new HTMLAnchorElement();    
`;

async function setup() {
    const response = await fetch("https://raw.githubusercontent.com/microsoft/TypeScript/master/lib/lib.es5.d.ts");
    const libes5 = await response.text();

    monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
        allowNonTsExtensions: true,
        target: monaco.languages.typescript.ScriptTarget.ES5,
    });

    // Use "lib.custom.d.ts" for ES5, and "defaultLib:lib.es6.d.ts" for ESNext (the default)
    monaco.languages.typescript.typescriptDefaults.addExtraLib(
        libes5,
        "defaultLib:lib.d.ts"
    );

    monaco.editor.create(document.getElementById("container"), {
        value: code,
        language: "typescript"
    });
}

setup();

另外,这是一个modified version of your Code Sandbox example

【讨论】:

  • 非常感谢。你刚刚给了我我需要的东西!
猜你喜欢
  • 1970-01-01
  • 2022-01-05
  • 2020-11-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多