Skip to content

Reset partial memberlist on defered circularity to calculate the correct members #20179

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

Merged
merged 2 commits into from
Dec 22, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 15 additions & 8 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5057,11 +5057,11 @@ namespace ts {
return type.resolvedBaseTypes;
}

function resolveBaseTypesOfClass(type: InterfaceType): void {
type.resolvedBaseTypes = emptyArray;
function resolveBaseTypesOfClass(type: InterfaceType) {
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);
Expand All @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
/* @internal */
namespace ts {
export const emptyArray: never[] = [] as never[];
export const resolvingEmptyArray: never[] = [] as never[];
export const emptyMap: ReadonlyMap<never> = createMap<never>();

export const externalHelpersModuleNameText = "tslib";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//// [circularConstraintYieldsAppropriateError.ts]
// https://github.com/Microsoft/TypeScript/issues/16861
class BaseType<T> {
bar: T
}

class NextType<C extends { someProp: any }, T = C['someProp']> extends BaseType<T> {
baz: string;
}

class Foo extends NextType<Foo> {
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;
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
=== tests/cases/compiler/circularConstraintYieldsAppropriateError.ts ===
// https://github.com/Microsoft/TypeScript/issues/16861
class BaseType<T> {
>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<C extends { someProp: any }, T = C['someProp']> extends BaseType<T> {
>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> {
>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))

Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
=== tests/cases/compiler/circularConstraintYieldsAppropriateError.ts ===
// https://github.com/Microsoft/TypeScript/issues/16861
class BaseType<T> {
>BaseType : BaseType<T>
>T : T

bar: T
>bar : T
>T : T
}

class NextType<C extends { someProp: any }, T = C['someProp']> extends BaseType<T> {
>NextType : NextType<C, T>
>C : C
>someProp : any
>T : T
>C : C
>BaseType : BaseType<T>
>T : T

baz: string;
>baz : string
}

class Foo extends NextType<Foo> {
>Foo : Foo
>NextType : NextType<Foo, { test: true; }>
>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

17 changes: 17 additions & 0 deletions tests/cases/compiler/circularConstraintYieldsAppropriateError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// https://github.com/Microsoft/TypeScript/issues/16861
class BaseType<T> {
bar: T
}

class NextType<C extends { someProp: any }, T = C['someProp']> extends BaseType<T> {
baz: string;
}

class Foo extends NextType<Foo> {
someProp: {
test: true
}
}

const foo = new Foo();
foo.bar.test