Skip to content
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