Skip to content

Commit

Permalink
Merge pull request #12065 from about-code/master
Browse files Browse the repository at this point in the history
Fixing #442: Impossible to define static 'length' function on class
  • Loading branch information
sandersn committed Jan 17, 2017
2 parents 65ef51d + 9b217e3 commit 899d512
Show file tree
Hide file tree
Showing 10 changed files with 1,020 additions and 52 deletions.
66 changes: 50 additions & 16 deletions src/compiler/checker.ts
Expand Up @@ -2171,24 +2171,25 @@ namespace ts {
return type.flags & TypeFlags.StringLiteral ? `"${escapeString((<LiteralType>type).text)}"` : (<LiteralType>type).text;
}

function getSymbolDisplayBuilder(): SymbolDisplayBuilder {

function getNameOfSymbol(symbol: Symbol): string {
if (symbol.declarations && symbol.declarations.length) {
const declaration = symbol.declarations[0];
if (declaration.name) {
return declarationNameToString(declaration.name);
}
switch (declaration.kind) {
case SyntaxKind.ClassExpression:
return "(Anonymous class)";
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
return "(Anonymous function)";
}
function getNameOfSymbol(symbol: Symbol): string {
if (symbol.declarations && symbol.declarations.length) {
const declaration = symbol.declarations[0];
if (declaration.name) {
return declarationNameToString(declaration.name);
}
switch (declaration.kind) {
case SyntaxKind.ClassExpression:
return "(Anonymous class)";
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
return "(Anonymous function)";
}
return symbol.name;
}
return symbol.name;
}

function getSymbolDisplayBuilder(): SymbolDisplayBuilder {

/**
* Writes only the name of the symbol out to the writer. Uses the original source text
Expand Down Expand Up @@ -15680,7 +15681,7 @@ namespace ts {
}
}
else {
const isStatic = forEach(member.modifiers, m => m.kind === SyntaxKind.StaticKeyword);
const isStatic = getModifierFlags(member) & ModifierFlags.Static;
const names = isStatic ? staticNames : instanceNames;

const memberName = member.name && getPropertyNameForPropertyNameNode(member.name);
Expand Down Expand Up @@ -15718,6 +15719,38 @@ namespace ts {
}
}

/**
* Static members being set on a constructor function may conflict with built-in properties
* of Function. Esp. in ECMAScript 5 there are non-configurable and non-writable
* built-in properties. This check issues a transpile error when a class has a static
* member with the same name as a non-writable built-in property.
*
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.3.3
* @see http://www.ecma-international.org/ecma-262/5.1/#sec-15.3.5
* @see http://www.ecma-international.org/ecma-262/6.0/#sec-properties-of-the-function-constructor
* @see http://www.ecma-international.org/ecma-262/6.0/#sec-function-instances
*/
function checkClassForStaticPropertyNameConflicts(node: ClassLikeDeclaration) {
for (const member of node.members) {
const memberNameNode = member.name;
const isStatic = getModifierFlags(member) & ModifierFlags.Static;
if (isStatic && memberNameNode) {
const memberName = getPropertyNameForPropertyNameNode(memberNameNode);
switch (memberName) {
case "name":
case "length":
case "caller":
case "arguments":
case "prototype":
const message = Diagnostics.Static_property_0_conflicts_with_built_in_property_Function_0_of_constructor_function_1;
const className = getNameOfSymbol(getSymbolOfNode(node));
error(memberNameNode, message, memberName, className);
break;
}
}
}
}

function checkObjectTypeForDuplicateDeclarations(node: TypeLiteralNode | InterfaceDeclaration) {
const names = createMap<boolean>();
for (const member of node.members) {
Expand Down Expand Up @@ -18291,6 +18324,7 @@ namespace ts {
const staticType = <ObjectType>getTypeOfSymbol(symbol);
checkTypeParameterListsIdentical(node, symbol);
checkClassForDuplicateDeclarations(node);
checkClassForStaticPropertyNameConflicts(node);

const baseTypeNode = getClassExtendsHeritageClauseElement(node);
if (baseTypeNode) {
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Expand Up @@ -2015,6 +2015,10 @@
"category": "Error",
"code": 2698
},
"Static property '{0}' conflicts with built-in property 'Function.{0}' of constructor function '{1}'.": {
"category": "Error",
"code": 2699
},
"Rest types may only be created from object types.": {
"category": "Error",
"code": 2700
Expand Down
10 changes: 10 additions & 0 deletions tests/baselines/reference/propertyNamedPrototype.errors.txt
@@ -0,0 +1,10 @@
tests/cases/conformance/classes/propertyMemberDeclarations/propertyNamedPrototype.ts(3,12): error TS2699: Static property 'prototype' conflicts with built-in property 'Function.prototype' of constructor function 'C'.


==== tests/cases/conformance/classes/propertyMemberDeclarations/propertyNamedPrototype.ts (1 errors) ====
class C {
prototype: number; // ok
static prototype: C; // error
~~~~~~~~~
!!! error TS2699: Static property 'prototype' conflicts with built-in property 'Function.prototype' of constructor function 'C'.
}
@@ -1,43 +1,43 @@
tests/cases/compiler/staticMemberOfClassAndPublicMemberOfAnotherClassAssignment.ts(12,1): error TS2322: Type 'C' is not assignable to type 'A'.
Property 'name' is missing in type 'C'.
Property 'prop' is missing in type 'C'.
tests/cases/compiler/staticMemberOfClassAndPublicMemberOfAnotherClassAssignment.ts(13,1): error TS2322: Type 'typeof B' is not assignable to type 'A'.
Property 'name' is missing in type 'typeof B'.
Property 'prop' is missing in type 'typeof B'.
tests/cases/compiler/staticMemberOfClassAndPublicMemberOfAnotherClassAssignment.ts(16,5): error TS2322: Type 'C' is not assignable to type 'B'.
Property 'name' is missing in type 'C'.
Property 'prop' is missing in type 'C'.
tests/cases/compiler/staticMemberOfClassAndPublicMemberOfAnotherClassAssignment.ts(17,1): error TS2322: Type 'typeof B' is not assignable to type 'B'.
Property 'name' is missing in type 'typeof B'.
Property 'prop' is missing in type 'typeof B'.


==== tests/cases/compiler/staticMemberOfClassAndPublicMemberOfAnotherClassAssignment.ts (4 errors) ====
interface A {
name();
prop();
}
class B {
public name() { }
public prop() { }
}
class C {
public static name() { }
public static prop() { }
}

var a: A = new B();
a = new C(); // error name is missing
a = new C(); // error prop is missing
~
!!! error TS2322: Type 'C' is not assignable to type 'A'.
!!! error TS2322: Property 'name' is missing in type 'C'.
a = B; // error name is missing
!!! error TS2322: Property 'prop' is missing in type 'C'.
a = B; // error prop is missing
~
!!! error TS2322: Type 'typeof B' is not assignable to type 'A'.
!!! error TS2322: Property 'name' is missing in type 'typeof B'.
!!! error TS2322: Property 'prop' is missing in type 'typeof B'.
a = C;

var b: B = new C(); // error name is missing
var b: B = new C(); // error prop is missing
~
!!! error TS2322: Type 'C' is not assignable to type 'B'.
!!! error TS2322: Property 'name' is missing in type 'C'.
b = B; // error name is missing
!!! error TS2322: Property 'prop' is missing in type 'C'.
b = B; // error prop is missing
~
!!! error TS2322: Type 'typeof B' is not assignable to type 'B'.
!!! error TS2322: Property 'name' is missing in type 'typeof B'.
!!! error TS2322: Property 'prop' is missing in type 'typeof B'.
b = C;
b = a;

Expand Down
@@ -1,21 +1,21 @@
//// [staticMemberOfClassAndPublicMemberOfAnotherClassAssignment.ts]
interface A {
name();
prop();
}
class B {
public name() { }
public prop() { }
}
class C {
public static name() { }
public static prop() { }
}

var a: A = new B();
a = new C(); // error name is missing
a = B; // error name is missing
a = new C(); // error prop is missing
a = B; // error prop is missing
a = C;

var b: B = new C(); // error name is missing
b = B; // error name is missing
var b: B = new C(); // error prop is missing
b = B; // error prop is missing
b = C;
b = a;

Expand All @@ -29,21 +29,21 @@ c = a;
var B = (function () {
function B() {
}
B.prototype.name = function () { };
B.prototype.prop = function () { };
return B;
}());
var C = (function () {
function C() {
}
C.name = function () { };
C.prop = function () { };
return C;
}());
var a = new B();
a = new C(); // error name is missing
a = B; // error name is missing
a = new C(); // error prop is missing
a = B; // error prop is missing
a = C;
var b = new C(); // error name is missing
b = B; // error name is missing
var b = new C(); // error prop is missing
b = B; // error prop is missing
b = C;
b = a;
var c = new B();
Expand Down

0 comments on commit 899d512

Please sign in to comment.