Typescript: 无法在类上定义静态“长度”函数

创建于 2014-08-12  ·  22评论  ·  资料来源: microsoft/TypeScript

接受以下打字稿程序:

class C {
    static length ()  { return "twelve"; }
}

但是,在生成的代码中:

var C = (function () {
    function C() {
    }
    C.length = function () {
        return "twelve";
    };
    return C;
})();

在这里,尝试分配给函数的length属性。 这不起作用(至少在Firefox和Chrome中),从而导致随后的C.length()调用崩溃。 也许应该只禁止调用静态类函数length

从Codeplex问题#1260迁移。

Bug Fixed good first issue help wanted

最有用的评论

同时,ES6 / 2015本机支持已进入Chrome和Firefox。 如果我们观察到它们是如何自然地处理问题的,那么它与我和@ nicolo-ribaudo提出的建议很接近(参见#9778)。

说明本机行为的代码段:

class Foo {
    constructor() {}
}
class Bar {
    static length() {}
    static name() {}
    static caller() {}
}
Foo.name = "FooChanged";
Bar.name = "Baz";

console.log(Foo.name) // Logs "Foo". Foo.name remains unwritable
console.log(Bar.name) // Logs "Baz".  Bar.name became writable

关于@tinganho之前写的有关namelengthcaller的只读性的内容,我们可以观察到本机实现,该属性一次可以在构造函数上写我们已将其中的一个定义为静态类成员(因此,请查看或修改我最近在MDN上有关获取类名的关于Function.name危险假设的警告)。

因此,使用@ nicolo-ribaudo建议的Object.defineProperty可以准确地模拟本机行为。 需要考虑的一件事是,一些较旧但与ES5兼容的浏览器版本将属性设置为configurable: false 。 因此,尝试使它们writable: true对它们将失败(例如,参见Function.name的可配置

综上所述,我对这个问题的看法是:

  • 我们不能将Object.defineProperty()用作编译目标es3 。 但是,如果没有Object.defineProperty ,则在ES5环境中运行ES3代码会遇到只读问题。 因此,我们需要针对目标es3进行编译错误
  • 使用Object.defineProperty()writable: true编译目标对象es5并发出警告/错误,指出使用这些属性名称可能会在浏览器中引起错误x of version <= y建议如果必须支持这些浏览器,则最安全的选择是选择另一个名称。
  • 如果您绝对不希望人们使用这些属性,请为这两个目标生成一个编译错误。

与编译输出无关,我认为任何形式的附加消息都比今天的情况(TS 1.8.10)要好得多,在这种情况下,我们完全没有任何关于编译器输出潜在问题的提示。

编辑:添加了

所有22条评论

我最近遇到这个问题。 :(

也不允许其他一些操作,例如以下代码应该出错(如果我们执行此功能):

class C {
    static name = 'something';
    static arguments = 'args';
    static caller = 'caller';
}

console.log(C.name); // 'C'
console.log(C.arguments); // null
console.log(C.caller); // null

然而,并非所有这些都是标准。

我认为属性namecallerlength是不可行的。 它们是只读属性,不能被覆盖。 给他们的所有分配都将被忽略。

@tinganho同意

好的,但是由于人们不时发现这种错误,所以我认为TS应该通知他们有关做错事的信息。 因为诸如namelength类的属性很自然地牢记在心,所以人们会尝试一次又一次地重新定义它们。

可能是因为“长度”,“名称”已定义。

我将尝试进行此工作。 除了已经提到的内容之外,还有更多不应允许的属性名称吗?

到目前为止,我看到了:
长度,名称,参数,调用方

当有人尝试使用禁止名称之一设置静态属性时,究竟会发生什么? 编译器错误? 向用户显示警告?

我对此并不陌生,因此,如果我错过了一些东西,那么将不胜感激。 :+1:

为什么不编译

class C {
    static name = 'something';
    static arguments = 'args';
    static caller = 'caller';
}

console.log(C.name); // 'C'
console.log(C.arguments); // null
console.log(C.caller); // null

var C = (function () {
    function C() {
    }

    C.__statics = {
        name: 'something',
        arguments: 'args',
        caller: 'caller'
    };

    return C;
})();
console.log(C.__statics.name); // 'something'
console.log(C.__statics.arguments); // 'args'
console.log(C.__statics.caller); // 'caller'

问题也出现在stackoverflow上: http :

@zerkms,请不要在两个地方发表相同的评论; 令人困惑。

有时您不能随意在结构类型系统中重写名称。

考虑这样的一些代码

interface HasName {
  name: string;
}
class Foo {
  static name = 'fooClass';
}
let bar: HasName = { name: string };
let q = Math.random() > 0.5 ? Foo : bar;
console.log(q.name);

@RyanCavanaugh确实是一个很好的例子(我不是在TS上开发的,但是天哪-只要将这段代码视为有效,它的类型系统就很奇怪)。

如果不可能无风险地转运它们,也许我们应该禁止它们?

如果不可能无风险地转运它们,也许我们应该禁止它们?

绝对不更改可转换,只是使其成为编译错误:rose:

[...]也许我们应该禁止它们?

一些有进取心的开发人员应向我们发送公关! :眨眼:

为什么不使用Object.defineProperty呢?

例如

class C {
    static length() { return "twelve"; }
}

会被转换成类似

var C = (function () {
    function C() {
    }
    Object.defineProperty(C, "length", {
        value: function () { return "twelve"; },
        writable: true
    });
    return C;
}());

@ nicolo-ribaudo确实有效。 我的看法:但是宁愿看到一个错误而不是这样的错误。 TypeScript通常靠优美的错误而不是_fixing JavaScript_:rose:

TypeScript通常倾向于优美的错误,而不是修复JavaScript

TS的全部想法不是通过填补它不可能也永远无法做到的空白来修复JS。

TS的全部想法不是通过填补它不可能也永远无法做到的空白来修复JS。

是的。 但是通过了解JavaScript的工作原理。 以nullundefined为例。 TypeScript选择理解两者(而不是像Dart那样将其整合到https://www.dartlang.org/docs/synonyms/中)。 如果您使用了错误的JavaScript模式,则TypeScript会给出错误(而不是在转换中进行修复):rose:

我的观点是我自己的,除了我之外,没有其他人认可:玫瑰:

同时,ES6 / 2015本机支持已进入Chrome和Firefox。 如果我们观察到它们是如何自然地处理问题的,那么它与我和@ nicolo-ribaudo提出的建议很接近(参见#9778)。

说明本机行为的代码段:

class Foo {
    constructor() {}
}
class Bar {
    static length() {}
    static name() {}
    static caller() {}
}
Foo.name = "FooChanged";
Bar.name = "Baz";

console.log(Foo.name) // Logs "Foo". Foo.name remains unwritable
console.log(Bar.name) // Logs "Baz".  Bar.name became writable

关于@tinganho之前写的有关namelengthcaller的只读性的内容,我们可以观察到本机实现,该属性一次可以在构造函数上写我们已将其中的一个定义为静态类成员(因此,请查看或修改我最近在MDN上有关获取类名的关于Function.name危险假设的警告)。

因此,使用@ nicolo-ribaudo建议的Object.defineProperty可以准确地模拟本机行为。 需要考虑的一件事是,一些较旧但与ES5兼容的浏览器版本将属性设置为configurable: false 。 因此,尝试使它们writable: true对它们将失败(例如,参见Function.name的可配置

综上所述,我对这个问题的看法是:

  • 我们不能将Object.defineProperty()用作编译目标es3 。 但是,如果没有Object.defineProperty ,则在ES5环境中运行ES3代码会遇到只读问题。 因此,我们需要针对目标es3进行编译错误
  • 使用Object.defineProperty()writable: true编译目标对象es5并发出警告/错误,指出使用这些属性名称可能会在浏览器中引起错误x of version <= y建议如果必须支持这些浏览器,则最安全的选择是选择另一个名称。
  • 如果您绝对不希望人们使用这些属性,请为这两个目标生成一个编译错误。

与编译输出无关,我认为任何形式的附加消息都比今天的情况(TS 1.8.10)要好得多,在这种情况下,我们完全没有任何关于编译器输出潜在问题的提示。

编辑:添加了

@RyanCavanaugh :能否请您回顾一下里程碑TS 2.0.1的此问题。 解决方案似乎很简单,但是取决于您选择发射器输出的选项,可能需要更改(请参阅我之前的评论)。 TS 2.0发行版可能最好包含此修复程序。

PR上的标签表明,资源有限的TypeScript团队正在向社区开放此问题。 如果您希望解决此问题,那么更广泛的社区中的某个人将不得不解决它,直到TypeScript核心团队认为他们有足够的积压空间来发布它(这可能会很长时间)。

@kitsonk感谢您解释PR标签。 我想我明白。 我的建议是重新评估它是否仍然是解决该问题的正确方法。 称为_name_或_length_的静态类属性不太可能发生,并且TS会为此生成错误输出。 因此,我什至不称此问题为“边缘案例”。

我认为静态属性是语言的核心,即使还没有拉取请求,该线程中也已提出了解决问题的方法。

尽管如此,我将评估我是否可以提供PR,但我必须承认我对TS来源尚不了解,并且恐怕TS 2.0会在此之前发布。

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

fwanicka picture fwanicka  ·  3评论

jbondc picture jbondc  ·  3评论

Antony-Jones picture Antony-Jones  ·  3评论

remojansen picture remojansen  ·  3评论

wmaurer picture wmaurer  ·  3评论