diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 73c93f48cbfa0..b3888acb5837c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -70,6 +70,7 @@ namespace ts { const emptySymbols = createSymbolTable(); const identityMapper: (type: Type) => Type = identity; + const fillMissingTypeArgumentState = createMap(); const compilerOptions = host.getCompilerOptions(); const languageVersion = getEmitScriptTarget(compilerOptions); @@ -7771,14 +7772,24 @@ namespace ts { const numTypeArguments = length(typeArguments); if (isJavaScriptImplicitAny || (numTypeArguments >= minTypeArgumentCount && numTypeArguments <= numTypeParameters)) { const result = typeArguments ? typeArguments.slice() : []; - + const stateKey = getTypeListId(typeArguments) + ">" + getTypeListId(typeParameters); // Map an unsatisfied type parameter with a default type. // If a type parameter does not have a default type, or if the default type // is a forward reference, the empty object type is used. const baseDefaultType = getDefaultTypeArgumentType(isJavaScriptImplicitAny); const circularityMapper = createTypeMapper(typeParameters!, map(typeParameters!, () => baseDefaultType)); - for (let i = numTypeArguments; i < numTypeParameters; i++) { - result[i] = instantiateType(getConstraintFromTypeParameter(typeParameters![i]) || baseDefaultType, circularityMapper); + if (fillMissingTypeArgumentState.has(stateKey)) { + // If we are already in the process of filling the type argument list's defaults, we cannot recur into the constraint again and must the the base + for (let i = numTypeArguments; i < numTypeParameters; i++) { + result[i] = baseDefaultType; + } + } + else { + fillMissingTypeArgumentState.set(stateKey, true); + for (let i = numTypeArguments; i < numTypeParameters; i++) { + result[i] = instantiateType(getConstraintFromTypeParameter(typeParameters![i]) || baseDefaultType, circularityMapper); + } + fillMissingTypeArgumentState.delete(stateKey); } for (let i = numTypeArguments; i < numTypeParameters; i++) { const mapper = createTypeMapper(typeParameters!, result); diff --git a/tests/baselines/reference/selfRecrusiveTypeParameterWithDefault.js b/tests/baselines/reference/selfRecrusiveTypeParameterWithDefault.js new file mode 100644 index 0000000000000..83ca074653015 --- /dev/null +++ b/tests/baselines/reference/selfRecrusiveTypeParameterWithDefault.js @@ -0,0 +1,7 @@ +//// [selfRecrusiveTypeParameterWithDefault.ts] +interface JoiObject {} + +interface AbstractSchema extends JoiObject { x; } + + +//// [selfRecrusiveTypeParameterWithDefault.js] diff --git a/tests/baselines/reference/selfRecrusiveTypeParameterWithDefault.symbols b/tests/baselines/reference/selfRecrusiveTypeParameterWithDefault.symbols new file mode 100644 index 0000000000000..b9231899d02b8 --- /dev/null +++ b/tests/baselines/reference/selfRecrusiveTypeParameterWithDefault.symbols @@ -0,0 +1,12 @@ +=== tests/cases/compiler/selfRecrusiveTypeParameterWithDefault.ts === +interface JoiObject {} +>JoiObject : Symbol(JoiObject, Decl(selfRecrusiveTypeParameterWithDefault.ts, 0, 0)) + +interface AbstractSchema extends JoiObject { x; } +>AbstractSchema : Symbol(AbstractSchema, Decl(selfRecrusiveTypeParameterWithDefault.ts, 0, 22)) +>Schema : Symbol(Schema, Decl(selfRecrusiveTypeParameterWithDefault.ts, 2, 25)) +>AbstractSchema : Symbol(AbstractSchema, Decl(selfRecrusiveTypeParameterWithDefault.ts, 0, 22)) +>Value : Symbol(Value, Decl(selfRecrusiveTypeParameterWithDefault.ts, 2, 61)) +>JoiObject : Symbol(JoiObject, Decl(selfRecrusiveTypeParameterWithDefault.ts, 0, 0)) +>x : Symbol(AbstractSchema.x, Decl(selfRecrusiveTypeParameterWithDefault.ts, 2, 94)) + diff --git a/tests/baselines/reference/selfRecrusiveTypeParameterWithDefault.types b/tests/baselines/reference/selfRecrusiveTypeParameterWithDefault.types new file mode 100644 index 0000000000000..f8f31d3d905b3 --- /dev/null +++ b/tests/baselines/reference/selfRecrusiveTypeParameterWithDefault.types @@ -0,0 +1,6 @@ +=== tests/cases/compiler/selfRecrusiveTypeParameterWithDefault.ts === +interface JoiObject {} + +interface AbstractSchema extends JoiObject { x; } +>x : any + diff --git a/tests/cases/compiler/selfRecrusiveTypeParameterWithDefault.ts b/tests/cases/compiler/selfRecrusiveTypeParameterWithDefault.ts new file mode 100644 index 0000000000000..a43a0c27783b6 --- /dev/null +++ b/tests/cases/compiler/selfRecrusiveTypeParameterWithDefault.ts @@ -0,0 +1,3 @@ +interface JoiObject {} + +interface AbstractSchema extends JoiObject { x; }