From 20dc1a52dd61854feaef542fd8e320f7b7b8f2af Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Mon, 20 Nov 2017 18:00:51 -0800 Subject: [PATCH 1/2] Reset partial memberlist on defered circularity to calculate the correct members --- src/compiler/checker.ts | 23 +++++--- src/compiler/utilities.ts | 1 + ...ircularConstraintYieldsAppropriateError.js | 52 +++++++++++++++++++ ...arConstraintYieldsAppropriateError.symbols | 48 +++++++++++++++++ ...ularConstraintYieldsAppropriateError.types | 50 ++++++++++++++++++ ...ircularConstraintYieldsAppropriateError.ts | 17 ++++++ 6 files changed, 183 insertions(+), 8 deletions(-) create mode 100644 tests/baselines/reference/circularConstraintYieldsAppropriateError.js create mode 100644 tests/baselines/reference/circularConstraintYieldsAppropriateError.symbols create mode 100644 tests/baselines/reference/circularConstraintYieldsAppropriateError.types create mode 100644 tests/cases/compiler/circularConstraintYieldsAppropriateError.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f507b0faaddb0..813c5338f9086 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5057,11 +5057,11 @@ namespace ts { return type.resolvedBaseTypes; } - function resolveBaseTypesOfClass(type: InterfaceType): void { - type.resolvedBaseTypes = emptyArray; + function resolveBaseTypesOfClass(type: InterfaceType): BaseType[] { + type.resolvedBaseTypes = resolvingEmptyArray; const baseConstructorType = getApparentType(getBaseConstructorTypeOfClass(type)); if (!(baseConstructorType.flags & (TypeFlags.Object | TypeFlags.Intersection | TypeFlags.Any))) { - return; + return type.resolvedBaseTypes = emptyArray; } const baseTypeNode = getBaseTypeNodeOfClass(type); const typeArgs = typeArgumentsFromTypeReferenceNode(baseTypeNode); @@ -5084,24 +5084,31 @@ namespace ts { const constructors = getInstantiatedConstructorsForTypeArguments(baseConstructorType, baseTypeNode.typeArguments, baseTypeNode); if (!constructors.length) { error(baseTypeNode.expression, Diagnostics.No_base_constructor_has_the_specified_number_of_type_arguments); - return; + return type.resolvedBaseTypes = emptyArray; } baseType = getReturnTypeOfSignature(constructors[0]); } if (baseType === unknownType) { - return; + return type.resolvedBaseTypes = emptyArray; } if (!isValidBaseType(baseType)) { error(baseTypeNode.expression, Diagnostics.Base_constructor_return_type_0_is_not_a_class_or_interface_type, typeToString(baseType)); - return; + return type.resolvedBaseTypes = emptyArray; } if (type === baseType || hasBaseType(baseType, type)) { error(type.symbol.valueDeclaration, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType)); - return; + return type.resolvedBaseTypes = emptyArray; + } + if (type.resolvedBaseTypes === resolvingEmptyArray) { + // Circular reference, likely through instantiation of default parameters + // (otherwise there'd be an error from hasBaseType) - this is fine, but `.members` should be reset + // as `getIndexedAccessType` via `instantiateType` via `getTypeFromClassOrInterfaceReference` forces a + // partial instantiation of the members without the base types fully resolved + (type as Type as ResolvedType).members = undefined; } - type.resolvedBaseTypes = [baseType]; + return type.resolvedBaseTypes = [baseType]; } function areAllOuterTypeParametersApplied(type: Type): boolean { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 46835bd94d591..247f613527a76 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3,6 +3,7 @@ /* @internal */ namespace ts { export const emptyArray: never[] = [] as never[]; + export const resolvingEmptyArray: never[] = [] as never[]; export const emptyMap: ReadonlyMap = createMap(); export const externalHelpersModuleNameText = "tslib"; diff --git a/tests/baselines/reference/circularConstraintYieldsAppropriateError.js b/tests/baselines/reference/circularConstraintYieldsAppropriateError.js new file mode 100644 index 0000000000000..a8583219dba85 --- /dev/null +++ b/tests/baselines/reference/circularConstraintYieldsAppropriateError.js @@ -0,0 +1,52 @@ +//// [circularConstraintYieldsAppropriateError.ts] +// https://github.com/Microsoft/TypeScript/issues/16861 +class BaseType { + bar: T +} + +class NextType extends BaseType { + baz: string; +} + +class Foo extends NextType { + someProp: { + test: true + } +} + +const foo = new Foo(); +foo.bar.test + +//// [circularConstraintYieldsAppropriateError.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 __()); + }; +})(); +// https://github.com/Microsoft/TypeScript/issues/16861 +var BaseType = /** @class */ (function () { + function BaseType() { + } + return BaseType; +}()); +var NextType = /** @class */ (function (_super) { + __extends(NextType, _super); + function NextType() { + return _super !== null && _super.apply(this, arguments) || this; + } + return NextType; +}(BaseType)); +var Foo = /** @class */ (function (_super) { + __extends(Foo, _super); + function Foo() { + return _super !== null && _super.apply(this, arguments) || this; + } + return Foo; +}(NextType)); +var foo = new Foo(); +foo.bar.test; diff --git a/tests/baselines/reference/circularConstraintYieldsAppropriateError.symbols b/tests/baselines/reference/circularConstraintYieldsAppropriateError.symbols new file mode 100644 index 0000000000000..7f485dd282cc2 --- /dev/null +++ b/tests/baselines/reference/circularConstraintYieldsAppropriateError.symbols @@ -0,0 +1,48 @@ +=== tests/cases/compiler/circularConstraintYieldsAppropriateError.ts === +// https://github.com/Microsoft/TypeScript/issues/16861 +class BaseType { +>BaseType : Symbol(BaseType, Decl(circularConstraintYieldsAppropriateError.ts, 0, 0)) +>T : Symbol(T, Decl(circularConstraintYieldsAppropriateError.ts, 1, 15)) + + bar: T +>bar : Symbol(BaseType.bar, Decl(circularConstraintYieldsAppropriateError.ts, 1, 19)) +>T : Symbol(T, Decl(circularConstraintYieldsAppropriateError.ts, 1, 15)) +} + +class NextType extends BaseType { +>NextType : Symbol(NextType, Decl(circularConstraintYieldsAppropriateError.ts, 3, 1)) +>C : Symbol(C, Decl(circularConstraintYieldsAppropriateError.ts, 5, 15)) +>someProp : Symbol(someProp, Decl(circularConstraintYieldsAppropriateError.ts, 5, 26)) +>T : Symbol(T, Decl(circularConstraintYieldsAppropriateError.ts, 5, 43)) +>C : Symbol(C, Decl(circularConstraintYieldsAppropriateError.ts, 5, 15)) +>BaseType : Symbol(BaseType, Decl(circularConstraintYieldsAppropriateError.ts, 0, 0)) +>T : Symbol(T, Decl(circularConstraintYieldsAppropriateError.ts, 5, 43)) + + baz: string; +>baz : Symbol(NextType.baz, Decl(circularConstraintYieldsAppropriateError.ts, 5, 84)) +} + +class Foo extends NextType { +>Foo : Symbol(Foo, Decl(circularConstraintYieldsAppropriateError.ts, 7, 1)) +>NextType : Symbol(NextType, Decl(circularConstraintYieldsAppropriateError.ts, 3, 1)) +>Foo : Symbol(Foo, Decl(circularConstraintYieldsAppropriateError.ts, 7, 1)) + + someProp: { +>someProp : Symbol(Foo.someProp, Decl(circularConstraintYieldsAppropriateError.ts, 9, 33)) + + test: true +>test : Symbol(test, Decl(circularConstraintYieldsAppropriateError.ts, 10, 15)) + } +} + +const foo = new Foo(); +>foo : Symbol(foo, Decl(circularConstraintYieldsAppropriateError.ts, 15, 5)) +>Foo : Symbol(Foo, Decl(circularConstraintYieldsAppropriateError.ts, 7, 1)) + +foo.bar.test +>foo.bar.test : Symbol(test, Decl(circularConstraintYieldsAppropriateError.ts, 10, 15)) +>foo.bar : Symbol(BaseType.bar, Decl(circularConstraintYieldsAppropriateError.ts, 1, 19)) +>foo : Symbol(foo, Decl(circularConstraintYieldsAppropriateError.ts, 15, 5)) +>bar : Symbol(BaseType.bar, Decl(circularConstraintYieldsAppropriateError.ts, 1, 19)) +>test : Symbol(test, Decl(circularConstraintYieldsAppropriateError.ts, 10, 15)) + diff --git a/tests/baselines/reference/circularConstraintYieldsAppropriateError.types b/tests/baselines/reference/circularConstraintYieldsAppropriateError.types new file mode 100644 index 0000000000000..7aa57d21b1a03 --- /dev/null +++ b/tests/baselines/reference/circularConstraintYieldsAppropriateError.types @@ -0,0 +1,50 @@ +=== tests/cases/compiler/circularConstraintYieldsAppropriateError.ts === +// https://github.com/Microsoft/TypeScript/issues/16861 +class BaseType { +>BaseType : BaseType +>T : T + + bar: T +>bar : T +>T : T +} + +class NextType extends BaseType { +>NextType : NextType +>C : C +>someProp : any +>T : T +>C : C +>BaseType : BaseType +>T : T + + baz: string; +>baz : string +} + +class Foo extends NextType { +>Foo : Foo +>NextType : NextType +>Foo : Foo + + someProp: { +>someProp : { test: true; } + + test: true +>test : true +>true : true + } +} + +const foo = new Foo(); +>foo : Foo +>new Foo() : Foo +>Foo : typeof Foo + +foo.bar.test +>foo.bar.test : true +>foo.bar : { test: true; } +>foo : Foo +>bar : { test: true; } +>test : true + diff --git a/tests/cases/compiler/circularConstraintYieldsAppropriateError.ts b/tests/cases/compiler/circularConstraintYieldsAppropriateError.ts new file mode 100644 index 0000000000000..848df9642c5a9 --- /dev/null +++ b/tests/cases/compiler/circularConstraintYieldsAppropriateError.ts @@ -0,0 +1,17 @@ +// https://github.com/Microsoft/TypeScript/issues/16861 +class BaseType { + bar: T +} + +class NextType extends BaseType { + baz: string; +} + +class Foo extends NextType { + someProp: { + test: true + } +} + +const foo = new Foo(); +foo.bar.test \ No newline at end of file From 8a77620cb469fa682136bf137264e72ae3c1d681 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 21 Nov 2017 12:11:57 -0800 Subject: [PATCH 2/2] Remove return type --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 813c5338f9086..679b257127be5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5057,7 +5057,7 @@ namespace ts { return type.resolvedBaseTypes; } - function resolveBaseTypesOfClass(type: InterfaceType): BaseType[] { + function resolveBaseTypesOfClass(type: InterfaceType) { type.resolvedBaseTypes = resolvingEmptyArray; const baseConstructorType = getApparentType(getBaseConstructorTypeOfClass(type)); if (!(baseConstructorType.flags & (TypeFlags.Object | TypeFlags.Intersection | TypeFlags.Any))) {