microsoft / microsoft/TypeScript

Suggestion: Reopen static and instance side of classes

未关闭
#2,957 16 条评论 30 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

In Discussion Suggestion
主要语言
Go
星标
111k
派生
14.3k
平均合并
2 天 4 小时
30 天内合并 PR
132

描述

Summary

To support both the semantics of subclassing built-ins in ES6 and still allow authors to augment built-ins, we need a mechanism to reopen the static and instance sides of a class.

Current state

Today we can re-open interfaces, allowing authors to augment built-ins (for example, to support polyfills):

// in lib.d.ts
interface Array<T> { /*...*/ }
interface ArrayConstructor { /*...*/ }
declare var Array: ArrayConstructor;

// in polyfill.ts
interface Array<T> {
  includes(value: T): Boolean;
}
interface ArrayConstructor {
  of<T>(...items: T[]): Array<T>;
}

Array.prototype.includes = function (value: any) { return this.indexOf(value) != -1; }
Array.of = function<T> (...items: T[]) { return items; }

We can also re-open the static side of a class, in a limited fashion:

// initial declaration
class MyClass {
}

// re-open
module MyClass {
  export var staticProperty = 1;
}

There are several issues with these approaches:

  • You cannot use type defined by the var/interface pattern in the extends clause of a class in TypeScript, meaning that "classes" defined using this pattern cannot be subclassed in ES6, which is an issue for built-ins.
  • While you can re-open the static side of a class using module, you can only use non-keyword identifiers for property names. So you could not, for example, add a [Symbol.species] property to the class, or use decorators on these members.
  • There is no way to re-open the instance side of a class.

Proposal

I propose we add a new syntactic modifier for the class declaration that would indicate we are re-opening an existing class. For this example I am using the keyword partial, although the semantics here differ significantly than the same-named capability in C#:

// in lib.d.ts
declare class Array<T> {
}

// in polyfill.ts
partial class Array<T> {
  static of<T>(...items: T[]) { return items; }
  includes(value: T): boolean { return this.indexOf(value) != -1; }
}

// emit (ES5)
Array.of = function() { 
  var items = [];
  for (var _i = 0; i < arguments.length; i++) 
    items[i] = arguments[i];
  return items;
}
Array.prototype.includes = function(value) {
  return this.indexOf(value) != -1;
}

Rules

  • A partial class declaration must be preceded by a non-partial class declaration in the same lexical scope. These should be the same rules that apply when merging a module with a class or function today.
  • A partial class declaration must have the same module visibility as the preceding non-partial class declaration.
  • A partial class declaration must have the same generic type parameters (including constraints) as the non-partial class declaration.
  • A partial class declaration cannot have an extends clause, but may have an implements clause.
  • A partial class declaration cannot have a constructor member.
  • A partial class declaration cannot have members with the same name as existing members on a class.
    • Exception: ambient partial class declaration members can merge with other ambient partial class declaration members if they are compatible overloads, similar to interfaces.
  • Non-static property declarations on a partial class declaration cannot have initializers.
  • A partial class declaration can have a class decorator. User code that executes in-between the initial class declaration and the partial declaration will be able to observe the class before decorators on the partial class are applied.
    • NOTE: We could choose to disallow class decorators on a partial class.

Out of scope

  • This proposal does not cover the case where built-in "classes" can often also be called as functions. This case is covered in #2959.
  • This proposal does not cover the case where authors may have already extended the interface of a built in. This case is covered in #2961.

Previous discussions

This has also been discussed previously:

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

调研方向

首先阅读 proposal 示例,以及 #9、#563 和所链接的 CodePlex 工作项中引用的先前讨论。payload 没有列出任何 repository 文件、测试或入口点;要完成这项工作,需要实现并验证所提议的 partial-class 规则、static 与 instance 合并、decorator 以及 ES5 输出。

由索引模型根据 Issue 内容生成。

评估

技术栈
javascript, typescript
领域
compilers
Issue 类型
功能
难度
5/5
预计耗时
一周以上
活跃度
停滞
描述清晰度
基本清楚
新手友好度
25/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。