-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Allow extending from any #14935
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow extending from any #14935
Changes from 6 commits
f1c1969
765ac7c
650cc29
3624417
41eabbc
e09533d
7f6c49e
75f1774
7e3c033
441e54c
90d5c29
f635042
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4617,7 +4617,7 @@ namespace ts { | |
error(type.symbol.valueDeclaration, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_base_expression, symbolToString(type.symbol)); | ||
return type.resolvedBaseConstructorType = unknownType; | ||
} | ||
if (baseConstructorType !== unknownType && baseConstructorType !== nullWideningType && !isConstructorType(baseConstructorType)) { | ||
if (baseConstructorType !== anyType && baseConstructorType !== unknownType && baseConstructorType !== nullWideningType && !isConstructorType(baseConstructorType)) { | ||
error(baseTypeNode.expression, Diagnostics.Type_0_is_not_a_constructor_function_type, typeToString(baseConstructorType)); | ||
return type.resolvedBaseConstructorType = unknownType; | ||
} | ||
|
@@ -4649,7 +4649,7 @@ namespace ts { | |
function resolveBaseTypesOfClass(type: InterfaceType): void { | ||
type.resolvedBaseTypes = type.resolvedBaseTypes || emptyArray; | ||
const baseConstructorType = getApparentType(getBaseConstructorTypeOfClass(type)); | ||
if (!(baseConstructorType.flags & (TypeFlags.Object | TypeFlags.Intersection))) { | ||
if (!(baseConstructorType.flags & (TypeFlags.Object | TypeFlags.Intersection | TypeFlags.Any))) { | ||
return; | ||
} | ||
const baseTypeNode = getBaseTypeNodeOfClass(type); | ||
|
@@ -4662,6 +4662,9 @@ namespace ts { | |
// type arguments in the same manner as a type reference to get the same error reporting experience. | ||
baseType = getTypeFromClassOrInterfaceReference(baseTypeNode, baseConstructorType.symbol); | ||
} | ||
else if (baseConstructorType.flags & TypeFlags.Any) { | ||
baseType = anyType; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you leave a comment saying why it is necessary to set it to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's no good reason; in fact it reduces test churn to return baseConstructorType for tests that extend undefined variables, which I think get I'll change it to baseConstructorType. |
||
} | ||
else { | ||
// The class derives from a "class-like" constructor function, check that we have at least one construct signature | ||
// with a matching number of type parameters and use the return type of the first instantiated signature. Elsewhere | ||
|
@@ -4715,10 +4718,11 @@ namespace ts { | |
return true; | ||
} | ||
|
||
// A valid base type is any non-generic object type or intersection of non-generic | ||
// A valid base type is `any`, any non-generic object type or intersection of non-generic | ||
// object types. | ||
function isValidBaseType(type: Type): boolean { | ||
return type.flags & (TypeFlags.Object | TypeFlags.NonPrimitive) && !isGenericMappedType(type) || | ||
return !!(type.flags & TypeFlags.Any) || | ||
type.flags & (TypeFlags.Object | TypeFlags.NonPrimitive) && !isGenericMappedType(type) || | ||
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should this just be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it should work, although it's kind of a weird pun in that we rely on |
||
type.flags & TypeFlags.Intersection && !forEach((<IntersectionType>type).types, t => !isValidBaseType(t)); | ||
} | ||
|
||
|
@@ -5130,7 +5134,9 @@ namespace ts { | |
addInheritedMembers(members, getPropertiesOfType(instantiatedBaseType)); | ||
callSignatures = concatenate(callSignatures, getSignaturesOfType(instantiatedBaseType, SignatureKind.Call)); | ||
constructSignatures = concatenate(constructSignatures, getSignaturesOfType(instantiatedBaseType, SignatureKind.Construct)); | ||
stringIndexInfo = stringIndexInfo || getIndexInfoOfType(instantiatedBaseType, IndexKind.String); | ||
stringIndexInfo = stringIndexInfo || (instantiatedBaseType === anyType ? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: would it be easier to read this way: if (!stringIndexInfo) {
stringIndexInfo = instantiatedBaseType === anyType ? createIndexInfo(anyType, /*isReadonly*/ false) : getIndexInfoOfType(instantiatedBaseType, IndexKind.String);
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess so. Done. |
||
createIndexInfo(anyType, /*isReadonly*/ false) : | ||
getIndexInfoOfType(instantiatedBaseType, IndexKind.String)); | ||
numberIndexInfo = numberIndexInfo || getIndexInfoOfType(instantiatedBaseType, IndexKind.Number); | ||
} | ||
} | ||
|
@@ -5371,6 +5377,7 @@ namespace ts { | |
// Combinations of function, class, enum and module | ||
let members = emptySymbols; | ||
let constructSignatures: Signature[] = emptyArray; | ||
let stringIndexInfo: IndexInfo = undefined; | ||
if (symbol.exports) { | ||
members = getExportsOfSymbol(symbol); | ||
} | ||
|
@@ -5385,9 +5392,12 @@ namespace ts { | |
members = createSymbolTable(getNamedMembers(members)); | ||
addInheritedMembers(members, getPropertiesOfType(baseConstructorType)); | ||
} | ||
else if (baseConstructorType === anyType) { | ||
stringIndexInfo = createIndexInfo(anyType, /*isReadonly*/ false); | ||
} | ||
} | ||
const numberIndexInfo = symbol.flags & SymbolFlags.Enum ? enumNumberIndexInfo : undefined; | ||
setStructuredTypeMembers(type, members, emptyArray, constructSignatures, undefined, numberIndexInfo); | ||
setStructuredTypeMembers(type, members, emptyArray, constructSignatures, stringIndexInfo, numberIndexInfo); | ||
// We resolve the members before computing the signatures because a signature may use | ||
// typeof with a qualified name expression that circularly references the type we are | ||
// in the process of resolving (see issue #6072). The temporarily empty signature list | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
tests/cases/compiler/extendFromAny.ts(8,9): error TS2339: Property 'length' does not exist on type 'number'. | ||
tests/cases/compiler/extendFromAny.ts(9,10): error TS2339: Property 'length' does not exist on type 'number'. | ||
|
||
|
||
==== tests/cases/compiler/extendFromAny.ts (2 errors) ==== | ||
declare var Base: any; | ||
class C extends Base { | ||
known = 1; | ||
static sknown = 2; | ||
} | ||
|
||
let c = new C(); | ||
c.known.length; // error, 'known' has no 'length' property | ||
~~~~~~ | ||
!!! error TS2339: Property 'length' does not exist on type 'number'. | ||
C.sknown.length; // error, 'sknown' has no 'length' property | ||
~~~~~~ | ||
!!! error TS2339: Property 'length' does not exist on type 'number'. | ||
c.unknown.length; // ok, unknown: any | ||
C.sunknown.length; // ok: sunknown: any | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
//// [extendFromAny.ts] | ||
declare var Base: any; | ||
class C extends Base { | ||
known = 1; | ||
static sknown = 2; | ||
} | ||
|
||
let c = new C(); | ||
c.known.length; // error, 'known' has no 'length' property | ||
C.sknown.length; // error, 'sknown' has no 'length' property | ||
c.unknown.length; // ok, unknown: any | ||
C.sunknown.length; // ok: sunknown: any | ||
|
||
|
||
//// [extendFromAny.js] | ||
var __extends = (this && this.__extends) || (function () { | ||
var extendStatics = Object.setPrototypeOf || | ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || | ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; | ||
return function (d, b) { | ||
extendStatics(d, b); | ||
function __() { this.constructor = d; } | ||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); | ||
}; | ||
})(); | ||
var C = (function (_super) { | ||
__extends(C, _super); | ||
function C() { | ||
var _this = _super !== null && _super.apply(this, arguments) || this; | ||
_this.known = 1; | ||
return _this; | ||
} | ||
return C; | ||
}(Base)); | ||
C.sknown = 2; | ||
var c = new C(); | ||
c.known.length; // error, 'known' has no 'length' property | ||
C.sknown.length; // error, 'sknown' has no 'length' property | ||
c.unknown.length; // ok, unknown: any | ||
C.sunknown.length; // ok: sunknown: any |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
=== /a.ts === | ||
import Foo from "foo"; | ||
>Foo : Symbol(Foo, Decl(a.ts, 0, 6)) | ||
|
||
class A extends Foo { } | ||
>A : Symbol(A, Decl(a.ts, 0, 22)) | ||
>Foo : Symbol(Foo, Decl(a.ts, 0, 6)) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
=== /a.ts === | ||
import Foo from "foo"; | ||
>Foo : any | ||
|
||
class A extends Foo { } | ||
>A : A | ||
>Foo : any | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,9 @@ | ||
tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck40.ts(2,21): error TS2507: Type 'any' is not a constructor function type. | ||
tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck40.ts(2,22): error TS1163: A 'yield' expression is only allowed in a generator body. | ||
|
||
|
||
==== tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck40.ts (2 errors) ==== | ||
==== tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck40.ts (1 errors) ==== | ||
function* g() { | ||
class C extends (yield 0) { } | ||
~~~~~~~~~ | ||
!!! error TS2507: Type 'any' is not a constructor function type. | ||
~~~~~ | ||
!!! error TS1163: A 'yield' expression is only allowed in a generator body. | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,9 @@ | ||
tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck55.ts(2,29): error TS2507: Type 'any' is not a constructor function type. | ||
tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck55.ts(2,30): error TS1163: A 'yield' expression is only allowed in a generator body. | ||
|
||
|
||
==== tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck55.ts (2 errors) ==== | ||
==== tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck55.ts (1 errors) ==== | ||
function* g() { | ||
var x = class C extends (yield) {}; | ||
~~~~~~~ | ||
!!! error TS2507: Type 'any' is not a constructor function type. | ||
~~~~~ | ||
!!! error TS1163: A 'yield' expression is only allowed in a generator body. | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,9 @@ | ||
tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck60.ts(2,21): error TS2507: Type 'any' is not a constructor function type. | ||
tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck60.ts(2,22): error TS1163: A 'yield' expression is only allowed in a generator body. | ||
|
||
|
||
==== tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck60.ts (2 errors) ==== | ||
==== tests/cases/conformance/es6/yieldExpressions/generatorTypeCheck60.ts (1 errors) ==== | ||
function* g() { | ||
class C extends (yield) {}; | ||
~~~~~~~ | ||
!!! error TS2507: Type 'any' is not a constructor function type. | ||
~~~~~ | ||
!!! error TS1163: A 'yield' expression is only allowed in a generator body. | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,8 @@ | ||
tests/cases/compiler/recursiveBaseCheck4.ts(1,7): error TS2506: 'M' is referenced directly or indirectly in its own base expression. | ||
tests/cases/compiler/recursiveBaseCheck4.ts(2,9): error TS2339: Property 'blah' does not exist on type 'M<{}>'. | ||
|
||
|
||
==== tests/cases/compiler/recursiveBaseCheck4.ts (2 errors) ==== | ||
==== tests/cases/compiler/recursiveBaseCheck4.ts (1 errors) ==== | ||
class M<T> extends M<string> { } | ||
~ | ||
!!! error TS2506: 'M' is referenced directly or indirectly in its own base expression. | ||
(new M).blah; | ||
~~~~ | ||
!!! error TS2339: Property 'blah' does not exist on type 'M<{}>'. | ||
(new M).blah; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,8 @@ | ||
tests/cases/compiler/recursiveBaseCheck6.ts(1,7): error TS2506: 'S18' is referenced directly or indirectly in its own base expression. | ||
tests/cases/compiler/recursiveBaseCheck6.ts(2,13): error TS2339: Property 'blah' does not exist on type 'S18<{}>'. | ||
|
||
|
||
==== tests/cases/compiler/recursiveBaseCheck6.ts (2 errors) ==== | ||
==== tests/cases/compiler/recursiveBaseCheck6.ts (1 errors) ==== | ||
class S18<A> extends S18<{ S19: A; }>{ } | ||
~~~ | ||
!!! error TS2506: 'S18' is referenced directly or indirectly in its own base expression. | ||
(new S18()).blah; | ||
~~~~ | ||
!!! error TS2339: Property 'blah' does not exist on type 'S18<{}>'. | ||
(new S18()).blah; |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hard for me to look up right now whether
unknownType
includes it, could you check forTypeFlags.Any
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just check
unknownType
andanyType
both haveTypeFlags.Any
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Includes anyType, autoType, unknownType. Seems like it should work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done