【问题标题】:How to properly annotate a cached mixin如何正确注释缓存的mixin
【发布时间】:2021-01-22 06:58:25
【问题描述】:
export function Mixin(base) {
  return class extends base {
    foo = "bar";
  }
}

class A extends Mixin(SuperClass) {

}

let a = new A;
a. // suggests foo

这很好用,但在我的情况下,SuperClass 通常是同一个构造函数,所以我认为实现缓存是个好主意。

let cache = new Map();
export function Mixin(base) {
  if (!cache.has(base)) {
    cache.set(base, class extends base {
      foo = "bar";
    })
  }
  return cache.get(base);
}

class A extends Mixin(SuperClass) {

}

let a = new A;
a. // no longer suggests foo

因此,为了恢复自动完成功能,我将操作拆分为两个功能。

let cache = new Map();

function Mixin(base) {
  return class extends base {
    foo = "bar";
  }
}

/** 
 * @returns {ReturnType<Mixin>} 
 */
export function Cached(base) {
  if (!cache.has(base)) {
    cache.set(base, Mixin(base));
  }

  return cache.get(base);
}

class A extends Cached(HTMLElement) {}

let a = new A();

a. // suggests foo
a.inn // does not suggest innerHTML

如何注释Cached 的返回值,使其知道它将扩展base

我正在考虑以下内容,只是无法弄清楚正确的语法是什么:

/** 
 * @param {T} base
 * @returns {ReturnType<Mixin> extends T} 
 */
export function Cached(base) {

【问题讨论】:

  • 只是一个疯狂的猜测,没有太多来自 JS,但可能是因为 cache 无论如何都没有输入,所以 return cache.~~ 是未知的,因此没有建议。
  • @RicHard 是的,确实可以是cacheMixin 的返回值,这并不重要,问题是如何给它正确的类型。
  • 我认为你能得到的最接近的是: /*** @type {{foo, 不管}} */ let a = new A; a.whatever // 将完成。
  • @RicHard 必须有一个正确的语法来获取匿名类而不必复制信息,即使必须命名该类仍然是一个有效的解决方案,但即便如此我还是失败了解决问题。
  • 嘿,您最终找到解决方案了吗?

标签: javascript visual-studio-code intellisense mixins jsdoc


【解决方案1】:

cache 具有隐式类型 Map&lt;any, any&gt;,它不会推断任何其他类型,因为 JavaScript 过于宽松,IDE 无法通过解析其余代码(即使您使用的是 ES6 模块并且未导出 cache)。

如果您不想仅仅为了打字而更改实现,最好的选择是使用TypeScriptJSDoc type assertions

这是一个最小的例子:

let cache = new Map();

/**
 * 
 * @param {*} base 
 * @return {typeof mixin}
 */
export function Mixin(base) {
    /**
    * @alias mixin
    */
    const mixin = class extends base {
        foo = "bar";
    }

    if (!cache.has(base)) {
        cache.set(base, mixin);
    }

    return cache.get(base);
}

class A extends Mixin(SuperClass) {

}

let a = new A;

a. // suggests foo

【讨论】:

  • 能否提供一个使用 JSDoc 的示例,如何将自定义类型与 mixin 类关联?
  • 不要认为这行得通。你看,这是棘手的部分..
  • 是的,正在寻找内部成员的引用语法
  • 让我大吃一惊的是,如果第一个示例有效,那么这绝对是可能的。
  • 是的,但是Map 毁了一切。我找到了这个解决方案,但它仍然不完美,因为即使有缓存命中它也会实例化一个类(@alias 在 if 块中不起作用)
【解决方案2】:

所以这让我又一次尝试成功地在我身边自动完成。 但是,我必须将 cache 包装到一个范围内,如下所示:

class Foo {
    foo = 'bar';
    fooooooo = 'baaaaaaar';
}

class ClassMaker {
    static _cache = new Map();

    /**
     *
     * @param base
     * @return {Foo}
     */
    static getFromBase(base) {
        if (!ClassMaker._cache.has(base)) {
            ClassMaker._cache[base] = class extends base {
            };
            ClassMaker._cache[base].__proto__ = Foo.__proto__;
        }

        return ClassMaker._cache[base];
    }
}

class Baz {
    baz = 'bar';
}

class Unrelated {
    foooooNotCompletedForReference = 'asd';
}

let a = ClassMaker.getFromBase(Baz);

事情事情会自动完成。另请注意,它不只是显示飞来飞去的每个符号,即 Unrelated 类不会填充到自动完成帮助器中。

但是请注意,JavaScript 不是类型化的,如果您想使用这些功能,最好使用 TypeScript 或 Dart。 JavaScript 中的所有这些自动建议只能通过使用其他工具分析书面代码来尽最大努力。它们会有缺陷,而且无论如何都无法保证。

【讨论】:

  • 感谢您的努力,在修复了几个语法错误后,我注意到您的示例还受到我在意外解决方案中的额外发现的影响,我提出了该解决方案作为对问题的编辑。
  • 您可能更快地修复了错误。我现在不使用 JS,但我仍然试图让它至少在我的 Opera 中编译 ;)
猜你喜欢
  • 2021-12-02
  • 2021-10-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-04
  • 2011-04-23
  • 2017-01-24
  • 1970-01-01
相关资源
最近更新 更多