diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f1e282845d540..c66d85beb93c4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -250,7 +250,7 @@ namespace ts { getTypeOfSymbol, getResolvedSymbol, getIndexTypeOfStructuredType, - getConstraintFromTypeParameter, + getConstraintOfTypeParameter, getFirstIdentifier, ), getAmbientModules, @@ -3715,7 +3715,7 @@ namespace ts { return createTypeParameterDeclaration(name, constraintNode, defaultParameterNode); } - function typeParameterToDeclaration(type: TypeParameter, context: NodeBuilderContext, constraint = getConstraintFromTypeParameter(type)): TypeParameterDeclaration { + function typeParameterToDeclaration(type: TypeParameter, context: NodeBuilderContext, constraint = getConstraintOfTypeParameter(type)): TypeParameterDeclaration { const constraintNode = constraint && typeToTypeNodeHelper(constraint, context); return typeParameterToDeclarationWithConstraint(type, context, constraintNode); } @@ -4363,29 +4363,23 @@ namespace ts { return i; } } - return -1; } function hasType(target: TypeSystemEntity, propertyName: TypeSystemPropertyName): boolean { - if (propertyName === TypeSystemPropertyName.Type) { - return !!getSymbolLinks(target).type; - } - if (propertyName === TypeSystemPropertyName.DeclaredType) { - return !!getSymbolLinks(target).declaredType; - } - if (propertyName === TypeSystemPropertyName.ResolvedBaseConstructorType) { - return !!(target).resolvedBaseConstructorType; + switch (propertyName) { + case TypeSystemPropertyName.Type: + return !!getSymbolLinks(target).type; + case TypeSystemPropertyName.DeclaredType: + return !!getSymbolLinks(target).declaredType; + case TypeSystemPropertyName.ResolvedBaseConstructorType: + return !!(target).resolvedBaseConstructorType; + case TypeSystemPropertyName.ResolvedReturnType: + return !!(target).resolvedReturnType; + case TypeSystemPropertyName.ImmediateBaseConstraint: + return !!(target).immediateBaseConstraint; } - if (propertyName === TypeSystemPropertyName.ResolvedReturnType) { - return !!(target).resolvedReturnType; - } - if (propertyName === TypeSystemPropertyName.ImmediateBaseConstraint) { - const bc = (target).immediateBaseConstraint; - return !!bc && bc !== circularConstraintType; - } - - return Debug.fail("Unhandled TypeSystemPropertyName " + propertyName); + return Debug.assertNever(propertyName); } // Pop an entry from the type resolution stack and return its associated result value. The result value will @@ -6936,21 +6930,12 @@ namespace ts { return undefined; } - function getBaseConstraintOfInstantiableNonPrimitiveUnionOrIntersection(type: Type) { + function getBaseConstraintOfType(type: Type): Type | undefined { if (type.flags & (TypeFlags.InstantiableNonPrimitive | TypeFlags.UnionOrIntersection)) { const constraint = getResolvedBaseConstraint(type); - if (constraint !== noConstraintType && constraint !== circularConstraintType) { - return constraint; - } - } - } - - function getBaseConstraintOfType(type: Type): Type | undefined { - const constraint = getBaseConstraintOfInstantiableNonPrimitiveUnionOrIntersection(type); - if (!constraint && type.flags & TypeFlags.Index) { - return keyofConstraintType; + return constraint !== noConstraintType && constraint !== circularConstraintType ? constraint : undefined; } - return constraint; + return type.flags & TypeFlags.Index ? keyofConstraintType : undefined; } /** @@ -6971,30 +6956,26 @@ namespace ts { * circularly references the type variable. */ function getResolvedBaseConstraint(type: InstantiableType | UnionOrIntersectionType): Type { - let circular: boolean | undefined; - if (!type.resolvedBaseConstraint) { - const constraint = getBaseConstraint(type); - type.resolvedBaseConstraint = circular ? circularConstraintType : getTypeWithThisArgument(constraint || noConstraintType, type); + return type.resolvedBaseConstraint || + (type.resolvedBaseConstraint = getTypeWithThisArgument(getImmediateBaseConstraint(type), type)); + + function getImmediateBaseConstraint(t: Type): Type { + if (!t.immediateBaseConstraint) { + if (!pushTypeResolution(t, TypeSystemPropertyName.ImmediateBaseConstraint)) { + return circularConstraintType; + } + let result = computeBaseConstraint(getSimplifiedType(t)); + if (!popTypeResolution()) { + result = circularConstraintType; + } + t.immediateBaseConstraint = result || noConstraintType; + } + return t.immediateBaseConstraint; } - return type.resolvedBaseConstraint; function getBaseConstraint(t: Type): Type | undefined { - if (t.immediateBaseConstraint) { - return t.immediateBaseConstraint === noConstraintType ? undefined : t.immediateBaseConstraint; - } - if (!pushTypeResolution(t, TypeSystemPropertyName.ImmediateBaseConstraint)) { - circular = true; - t.immediateBaseConstraint = circularConstraintType; - return undefined; - } - const result = computeBaseConstraint(getSimplifiedType(t)); - if (!popTypeResolution()) { - circular = true; - t.immediateBaseConstraint = circularConstraintType; - return undefined; - } - t.immediateBaseConstraint = !result ? noConstraintType : result; - return result; + const c = getImmediateBaseConstraint(t); + return c !== noConstraintType && c !== circularConstraintType ? c : undefined; } function computeBaseConstraint(t: Type): Type | undefined { @@ -7877,6 +7858,7 @@ namespace ts { return inferences && getIntersectionType(inferences); } + /** This is a worker function. Use getConstraintOfTypeParameter which guards against circular constraints. */ function getConstraintFromTypeParameter(typeParameter: TypeParameter): Type | undefined { if (!typeParameter.constraint) { if (typeParameter.target) { @@ -9145,7 +9127,7 @@ namespace ts { return type.simplified = substituteIndexedMappedType(objectType, type); } if (objectType.flags & TypeFlags.TypeParameter) { - const constraint = getConstraintFromTypeParameter(objectType as TypeParameter); + const constraint = getConstraintOfTypeParameter(objectType as TypeParameter); if (constraint && isGenericMappedType(constraint)) { return type.simplified = substituteIndexedMappedType(constraint, type); } @@ -12092,7 +12074,7 @@ namespace ts { } function isUnconstrainedTypeParameter(type: Type) { - return type.flags & TypeFlags.TypeParameter && !getConstraintFromTypeParameter(type); + return type.flags & TypeFlags.TypeParameter && !getConstraintOfTypeParameter(type); } function isTypeReferenceWithGenericArguments(type: Type): boolean { @@ -17620,7 +17602,7 @@ namespace ts { } const thisType = getTypeFromTypeNode(thisParameter.type); - enclosingClass = ((thisType.flags & TypeFlags.TypeParameter) ? getConstraintFromTypeParameter(thisType) : thisType) as InterfaceType; + enclosingClass = ((thisType.flags & TypeFlags.TypeParameter) ? getConstraintOfTypeParameter(thisType) : thisType) as InterfaceType; } // No further restrictions for static properties if (flags & ModifierFlags.Static) { @@ -19261,7 +19243,7 @@ namespace ts { typeArguments.pop(); } while (typeArguments.length < typeParameters.length) { - typeArguments.push(getConstraintFromTypeParameter(typeParameters[typeArguments.length]) || getDefaultTypeArgumentType(isInJavaScriptFile(node))); + typeArguments.push(getConstraintOfTypeParameter(typeParameters[typeArguments.length]) || getDefaultTypeArgumentType(isInJavaScriptFile(node))); } const instantiated = createSignatureInstantiation(candidate, typeArguments); candidates[bestIndex] = instantiated; @@ -25164,7 +25146,7 @@ namespace ts { // If the type parameter node does not have an identical constraint as the resolved // type parameter at this position, we report an error. const sourceConstraint = source.constraint && getTypeFromTypeNode(source.constraint); - const targetConstraint = getConstraintFromTypeParameter(target); + const targetConstraint = getConstraintOfTypeParameter(target); if (sourceConstraint) { // relax check if later interface augmentation has no constraint if (!targetConstraint || !isTypeIdenticalTo(sourceConstraint, targetConstraint)) { diff --git a/src/compiler/symbolWalker.ts b/src/compiler/symbolWalker.ts index cce6739d3596c..1f6930076bef0 100644 --- a/src/compiler/symbolWalker.ts +++ b/src/compiler/symbolWalker.ts @@ -9,7 +9,7 @@ namespace ts { getTypeOfSymbol: (sym: Symbol) => Type, getResolvedSymbol: (node: Node) => Symbol, getIndexTypeOfStructuredType: (type: Type, kind: IndexKind) => Type | undefined, - getConstraintFromTypeParameter: (typeParameter: TypeParameter) => Type | undefined, + getConstraintOfTypeParameter: (typeParameter: TypeParameter) => Type | undefined, getFirstIdentifier: (node: EntityNameOrEntityNameExpression) => Identifier) { return getSymbolWalker; @@ -93,7 +93,7 @@ namespace ts { } function visitTypeParameter(type: TypeParameter): void { - visitType(getConstraintFromTypeParameter(type)); + visitType(getConstraintOfTypeParameter(type)); } function visitUnionOrIntersectionType(type: UnionOrIntersectionType): void { diff --git a/tests/baselines/reference/circularIndexedAccessErrors.types b/tests/baselines/reference/circularIndexedAccessErrors.types index 6b2cf2c51be22..aeaa460ab4a32 100644 --- a/tests/baselines/reference/circularIndexedAccessErrors.types +++ b/tests/baselines/reference/circularIndexedAccessErrors.types @@ -86,7 +86,7 @@ interface Foo { } function foo() { ->foo : () => void +>foo : () => void >T : T >Foo : Foo >T : T diff --git a/tests/baselines/reference/incorrectRecursiveMappedTypeConstraint.types b/tests/baselines/reference/incorrectRecursiveMappedTypeConstraint.types index a388d2b536bd8..bf636eaae8077 100644 --- a/tests/baselines/reference/incorrectRecursiveMappedTypeConstraint.types +++ b/tests/baselines/reference/incorrectRecursiveMappedTypeConstraint.types @@ -1,7 +1,7 @@ === tests/cases/compiler/incorrectRecursiveMappedTypeConstraint.ts === // #17847 function sum(n: number, v: T, k: K) { ->sum : (n: number, v: T, k: K) => void +>sum : (n: number, v: T, k: K) => void >T : T >P : P >T : T diff --git a/tests/baselines/reference/typeParameterDirectlyConstrainedToItself.types b/tests/baselines/reference/typeParameterDirectlyConstrainedToItself.types index 0ef49d9767ae2..5efa4b5d19b63 100644 --- a/tests/baselines/reference/typeParameterDirectlyConstrainedToItself.types +++ b/tests/baselines/reference/typeParameterDirectlyConstrainedToItself.types @@ -24,18 +24,18 @@ interface I2 { } >U : U function f() { } ->f : () => void +>f : () => void >T : T >T : T function f2() { } ->f2 : () => void +>f2 : () => void >T : T >U : U >U : U var a: { ->a : { (): void; (): void; } +>a : { (): void; (): void; } (): void; >T : T @@ -48,14 +48,14 @@ var a: { } var b = () => { } ->b : () => void ->() => { } : () => void +>b : () => void +>() => { } : () => void >T : T >T : T var b2 = () => { } ->b2 : () => void ->() => { } : () => void +>b2 : () => void +>() => { } : () => void >T : T >U : U >U : U diff --git a/tests/baselines/reference/typeParameterHasSelfAsConstraint.types b/tests/baselines/reference/typeParameterHasSelfAsConstraint.types index 2d489515faf86..9d297dc99344d 100644 --- a/tests/baselines/reference/typeParameterHasSelfAsConstraint.types +++ b/tests/baselines/reference/typeParameterHasSelfAsConstraint.types @@ -1,6 +1,6 @@ === tests/cases/compiler/typeParameterHasSelfAsConstraint.ts === function foo(x: T): number { ->foo : (x: T) => number +>foo : (x: T) => number >T : T >T : T >x : T diff --git a/tests/baselines/reference/typeParameterIndirectlyConstrainedToItself.errors.txt b/tests/baselines/reference/typeParameterIndirectlyConstrainedToItself.errors.txt index ac7b58bb846b5..8ca5c51ea2dd4 100644 --- a/tests/baselines/reference/typeParameterIndirectlyConstrainedToItself.errors.txt +++ b/tests/baselines/reference/typeParameterIndirectlyConstrainedToItself.errors.txt @@ -23,9 +23,9 @@ tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterInd tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterIndirectlyConstrainedToItself.ts(16,21): error TS2313: Type parameter 'T' has a circular constraint. tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterIndirectlyConstrainedToItself.ts(16,34): error TS2313: Type parameter 'U' has a circular constraint. tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterIndirectlyConstrainedToItself.ts(16,47): error TS2313: Type parameter 'V' has a circular constraint. -tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterIndirectlyConstrainedToItself.ts(18,19): error TS2313: Type parameter 'U' has a circular constraint. tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterIndirectlyConstrainedToItself.ts(18,32): error TS2313: Type parameter 'T' has a circular constraint. tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterIndirectlyConstrainedToItself.ts(18,45): error TS2313: Type parameter 'V' has a circular constraint. +tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterIndirectlyConstrainedToItself.ts(23,24): error TS2313: Type parameter 'S' has a circular constraint. ==== tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterIndirectlyConstrainedToItself.ts (28 errors) ==== @@ -97,9 +97,15 @@ tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterInd !!! error TS2313: Type parameter 'V' has a circular constraint. class D { } - ~ -!!! error TS2313: Type parameter 'U' has a circular constraint. ~ !!! error TS2313: Type parameter 'T' has a circular constraint. ~ -!!! error TS2313: Type parameter 'V' has a circular constraint. \ No newline at end of file +!!! error TS2313: Type parameter 'V' has a circular constraint. + + // Repro from #25740 + + type Foo = [T] extends [number] ? {} : {}; + function foo>() {} + ~~~~~~ +!!! error TS2313: Type parameter 'S' has a circular constraint. + \ No newline at end of file diff --git a/tests/baselines/reference/typeParameterIndirectlyConstrainedToItself.js b/tests/baselines/reference/typeParameterIndirectlyConstrainedToItself.js index 58d559d7fee15..b6332f8011b00 100644 --- a/tests/baselines/reference/typeParameterIndirectlyConstrainedToItself.js +++ b/tests/baselines/reference/typeParameterIndirectlyConstrainedToItself.js @@ -16,7 +16,13 @@ var a: { var b = () => { } var b2 = () => { } -class D { } +class D { } + +// Repro from #25740 + +type Foo = [T] extends [number] ? {} : {}; +function foo>() {} + //// [typeParameterIndirectlyConstrainedToItself.js] var C = /** @class */ (function () { @@ -39,3 +45,4 @@ var D = /** @class */ (function () { } return D; }()); +function foo() { } diff --git a/tests/baselines/reference/typeParameterIndirectlyConstrainedToItself.symbols b/tests/baselines/reference/typeParameterIndirectlyConstrainedToItself.symbols index f120f7251c6c7..3a8305e4012bf 100644 --- a/tests/baselines/reference/typeParameterIndirectlyConstrainedToItself.symbols +++ b/tests/baselines/reference/typeParameterIndirectlyConstrainedToItself.symbols @@ -90,3 +90,16 @@ class D { } >V : Symbol(V, Decl(typeParameterIndirectlyConstrainedToItself.ts, 17, 33)) >T : Symbol(T, Decl(typeParameterIndirectlyConstrainedToItself.ts, 17, 20)) +// Repro from #25740 + +type Foo = [T] extends [number] ? {} : {}; +>Foo : Symbol(Foo, Decl(typeParameterIndirectlyConstrainedToItself.ts, 17, 50)) +>T : Symbol(T, Decl(typeParameterIndirectlyConstrainedToItself.ts, 21, 9)) +>T : Symbol(T, Decl(typeParameterIndirectlyConstrainedToItself.ts, 21, 9)) + +function foo>() {} +>foo : Symbol(foo, Decl(typeParameterIndirectlyConstrainedToItself.ts, 21, 45)) +>S : Symbol(S, Decl(typeParameterIndirectlyConstrainedToItself.ts, 22, 13)) +>Foo : Symbol(Foo, Decl(typeParameterIndirectlyConstrainedToItself.ts, 17, 50)) +>S : Symbol(S, Decl(typeParameterIndirectlyConstrainedToItself.ts, 22, 13)) + diff --git a/tests/baselines/reference/typeParameterIndirectlyConstrainedToItself.types b/tests/baselines/reference/typeParameterIndirectlyConstrainedToItself.types index ef5773a81a9bc..00ef88dc52bb2 100644 --- a/tests/baselines/reference/typeParameterIndirectlyConstrainedToItself.types +++ b/tests/baselines/reference/typeParameterIndirectlyConstrainedToItself.types @@ -32,14 +32,14 @@ interface I2 { } >T : T function f() { } ->f : () => void +>f : () => void >U : U >T : T >T : T >U : U function f2() { } ->f2 : () => void +>f2 : () => void >T : T >U : U >U : U @@ -48,7 +48,7 @@ function f2() { } >T : T var a: { ->a : { (): void; (): void; } +>a : { (): void; (): void; } (): void; >U : U @@ -66,16 +66,16 @@ var a: { } var b = () => { } ->b : () => void ->() => { } : () => void +>b : () => void +>() => { } : () => void >U : U >T : T >T : T >U : U var b2 = () => { } ->b2 : () => void ->() => { } : () => void +>b2 : () => void +>() => { } : () => void >T : T >U : U >U : U @@ -92,3 +92,16 @@ class D { } >V : V >T : T +// Repro from #25740 + +type Foo = [T] extends [number] ? {} : {}; +>Foo : Foo +>T : T +>T : T + +function foo>() {} +>foo : () => void +>S : S +>Foo : Foo +>S : S + diff --git a/tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterIndirectlyConstrainedToItself.ts b/tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterIndirectlyConstrainedToItself.ts index d5e5ae9208e08..63ed22d486556 100644 --- a/tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterIndirectlyConstrainedToItself.ts +++ b/tests/cases/conformance/types/typeParameters/typeParameterLists/typeParameterIndirectlyConstrainedToItself.ts @@ -15,4 +15,9 @@ var a: { var b = () => { } var b2 = () => { } -class D { } \ No newline at end of file +class D { } + +// Repro from #25740 + +type Foo = [T] extends [number] ? {} : {}; +function foo>() {}