From b615109b612517c0a3e90f4da06cdee20e66ff22 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 2 Mar 2022 13:25:13 -0800 Subject: [PATCH 1/5] Unify signatures in signaturesRelatedTo --- src/compiler/checker.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9dfb4a7e112c2..185af6f291c53 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13049,7 +13049,7 @@ namespace ts { return undefined; } - function getSignatureInstantiation(signature: Signature, typeArguments: Type[] | undefined, isJavascript: boolean, inferredTypeParameters?: readonly TypeParameter[]): Signature { + function getSignatureInstantiation(signature: Signature, typeArguments: readonly Type[] | undefined, isJavascript: boolean, inferredTypeParameters?: readonly TypeParameter[]): Signature { const instantiatedSignature = getSignatureInstantiationWithoutFillingInTypeArguments(signature, fillMissingTypeArguments(typeArguments, signature.typeParameters, getMinTypeArgumentCount(signature.typeParameters), isJavascript)); if (inferredTypeParameters) { const returnSignature = getSingleCallOrConstructSignature(getReturnTypeOfSignature(instantiatedSignature)); @@ -20100,10 +20100,15 @@ namespace ts { sourceObjectFlags & ObjectFlags.Reference && targetObjectFlags & ObjectFlags.Reference && (source as TypeReference).target === (target as TypeReference).target) { // We have instantiations of the same anonymous type (which typically will be the type of a // method). Simply do a pairwise comparison of the signatures in the two signature lists instead - // of the much more expensive N * M comparison matrix we explore below. We erase type parameters - // as they are known to always be the same. + // of the much more expensive N * M comparison matrix we explore below. We instantiate the source + // signature with the type parameters of the target signature to unify the two. for (let i = 0; i < targetSignatures.length; i++) { - const related = signatureRelatedTo(sourceSignatures[i], targetSignatures[i], /*erase*/ true, reportErrors, incompatibleReporter(sourceSignatures[i], targetSignatures[i])); + const sourceSignature = sourceSignatures[i]; + const targetSignature = targetSignatures[i]; + const instantiatedSourceSignature = targetSignature.typeParameters ? + getSignatureInstantiation(sourceSignature, targetSignature.typeParameters, isInJSFile(sourceSignature.declaration)) : + sourceSignature; + const related = signatureRelatedTo(instantiatedSourceSignature, targetSignature, /*erase*/ false, reportErrors, incompatibleReporter(instantiatedSourceSignature, targetSignature)); if (!related) { return Ternary.False; } From 63b1229eb0b2f8d774b529c1d289ea20d82d6f6b Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 2 Mar 2022 13:25:41 -0800 Subject: [PATCH 2/5] Accept new baselines --- tests/baselines/reference/complexRecursiveCollections.types | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/baselines/reference/complexRecursiveCollections.types b/tests/baselines/reference/complexRecursiveCollections.types index d6bf7cf931a27..5fb970f18d971 100644 --- a/tests/baselines/reference/complexRecursiveCollections.types +++ b/tests/baselines/reference/complexRecursiveCollections.types @@ -1137,7 +1137,7 @@ declare module Immutable { >Seq : typeof Seq function isSeq(maybeSeq: any): maybeSeq is Seq.Indexed | Seq.Keyed; ->isSeq : (maybeSeq: any) => maybeSeq is Indexed | Keyed +>isSeq : (maybeSeq: any) => maybeSeq is Keyed | Indexed >maybeSeq : any >Seq : any >Seq : any From b46f19692943c80214fc818ffb7778d1e277c9a8 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 2 Mar 2022 13:25:49 -0800 Subject: [PATCH 3/5] Add regression test --- .../reference/genericSignatureRelations.js | 23 +++++++++++++++ .../genericSignatureRelations.symbols | 28 +++++++++++++++++++ .../reference/genericSignatureRelations.types | 22 +++++++++++++++ .../compiler/genericSignatureRelations.ts | 12 ++++++++ 4 files changed, 85 insertions(+) create mode 100644 tests/baselines/reference/genericSignatureRelations.js create mode 100644 tests/baselines/reference/genericSignatureRelations.symbols create mode 100644 tests/baselines/reference/genericSignatureRelations.types create mode 100644 tests/cases/compiler/genericSignatureRelations.ts diff --git a/tests/baselines/reference/genericSignatureRelations.js b/tests/baselines/reference/genericSignatureRelations.js new file mode 100644 index 0000000000000..c12e9f2ceeb59 --- /dev/null +++ b/tests/baselines/reference/genericSignatureRelations.js @@ -0,0 +1,23 @@ +//// [genericSignatureRelations.ts] +// Repro from #48070 + +type S = () => T extends X ? 1 : '2'; + +type Foo1 = S<'s1'>; +type Foo2 = S<'s2'>; + +type Result1 = Foo1 extends Foo2 ? true : false; +type Result2 = S<'s1'> extends S<'s2'> ? true : false; + + +//// [genericSignatureRelations.js] +"use strict"; +// Repro from #48070 + + +//// [genericSignatureRelations.d.ts] +declare type S = () => T extends X ? 1 : '2'; +declare type Foo1 = S<'s1'>; +declare type Foo2 = S<'s2'>; +declare type Result1 = Foo1 extends Foo2 ? true : false; +declare type Result2 = S<'s1'> extends S<'s2'> ? true : false; diff --git a/tests/baselines/reference/genericSignatureRelations.symbols b/tests/baselines/reference/genericSignatureRelations.symbols new file mode 100644 index 0000000000000..01f533c613554 --- /dev/null +++ b/tests/baselines/reference/genericSignatureRelations.symbols @@ -0,0 +1,28 @@ +=== tests/cases/compiler/genericSignatureRelations.ts === +// Repro from #48070 + +type S = () => T extends X ? 1 : '2'; +>S : Symbol(S, Decl(genericSignatureRelations.ts, 0, 0)) +>X : Symbol(X, Decl(genericSignatureRelations.ts, 2, 7)) +>T : Symbol(T, Decl(genericSignatureRelations.ts, 2, 13)) +>T : Symbol(T, Decl(genericSignatureRelations.ts, 2, 13)) +>X : Symbol(X, Decl(genericSignatureRelations.ts, 2, 7)) + +type Foo1 = S<'s1'>; +>Foo1 : Symbol(Foo1, Decl(genericSignatureRelations.ts, 2, 43)) +>S : Symbol(S, Decl(genericSignatureRelations.ts, 0, 0)) + +type Foo2 = S<'s2'>; +>Foo2 : Symbol(Foo2, Decl(genericSignatureRelations.ts, 4, 20)) +>S : Symbol(S, Decl(genericSignatureRelations.ts, 0, 0)) + +type Result1 = Foo1 extends Foo2 ? true : false; +>Result1 : Symbol(Result1, Decl(genericSignatureRelations.ts, 5, 20)) +>Foo1 : Symbol(Foo1, Decl(genericSignatureRelations.ts, 2, 43)) +>Foo2 : Symbol(Foo2, Decl(genericSignatureRelations.ts, 4, 20)) + +type Result2 = S<'s1'> extends S<'s2'> ? true : false; +>Result2 : Symbol(Result2, Decl(genericSignatureRelations.ts, 7, 48)) +>S : Symbol(S, Decl(genericSignatureRelations.ts, 0, 0)) +>S : Symbol(S, Decl(genericSignatureRelations.ts, 0, 0)) + diff --git a/tests/baselines/reference/genericSignatureRelations.types b/tests/baselines/reference/genericSignatureRelations.types new file mode 100644 index 0000000000000..48cb1b124a06e --- /dev/null +++ b/tests/baselines/reference/genericSignatureRelations.types @@ -0,0 +1,22 @@ +=== tests/cases/compiler/genericSignatureRelations.ts === +// Repro from #48070 + +type S = () => T extends X ? 1 : '2'; +>S : S + +type Foo1 = S<'s1'>; +>Foo1 : Foo1 + +type Foo2 = S<'s2'>; +>Foo2 : Foo2 + +type Result1 = Foo1 extends Foo2 ? true : false; +>Result1 : false +>true : true +>false : false + +type Result2 = S<'s1'> extends S<'s2'> ? true : false; +>Result2 : false +>true : true +>false : false + diff --git a/tests/cases/compiler/genericSignatureRelations.ts b/tests/cases/compiler/genericSignatureRelations.ts new file mode 100644 index 0000000000000..c2e6ef3daaa6f --- /dev/null +++ b/tests/cases/compiler/genericSignatureRelations.ts @@ -0,0 +1,12 @@ +// @strict: true +// @declaration: true + +// Repro from #48070 + +type S = () => T extends X ? 1 : '2'; + +type Foo1 = S<'s1'>; +type Foo2 = S<'s2'>; + +type Result1 = Foo1 extends Foo2 ? true : false; +type Result2 = S<'s1'> extends S<'s2'> ? true : false; From bdfd342aaab56f0dfbf0952e2e61d5ed678ace35 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 18 Oct 2023 07:04:18 -0700 Subject: [PATCH 4/5] Manually re-implement changes in checker.ts --- src/compiler/checker.ts | 1596 +-------------------------------------- 1 file changed, 13 insertions(+), 1583 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bc9fca4101f72..9084995db80cf 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -15058,645 +15058,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } return links.containsArgumentsReference; - function getSignaturesOfSymbol(symbol: Symbol | undefined): Signature[] { - if (!symbol || !symbol.declarations) return emptyArray; - const result: Signature[] = []; - for (let i = 0; i < symbol.declarations.length; i++) { - const decl = symbol.declarations[i]; - if (!isFunctionLike(decl)) continue; - // Don't include signature if node is the implementation of an overloaded function. A node is considered - // an implementation node if it has a body and the previous node is of the same kind and immediately - // precedes the implementation node (i.e. has the same parent and ends where the implementation starts). - if (i > 0 && (decl as FunctionLikeDeclaration).body) { - const previous = symbol.declarations[i - 1]; - if (decl.parent === previous.parent && decl.kind === previous.kind && decl.pos === previous.end) { - continue; - } - } - // If this is a function or method declaration, get the signature from the @type tag for the sake of optional parameters. - // Exclude contextually-typed kinds because we already apply the @type tag to the context, plus applying it here to the initializer would supress checks that the two are compatible. - result.push( - (!isFunctionExpressionOrArrowFunction(decl) && - !isObjectLiteralMethod(decl) && - getSignatureOfTypeTag(decl)) || - getSignatureFromDeclaration(decl) - ); - } - return result; - } - - function resolveExternalModuleTypeByLiteral(name: StringLiteral) { - const moduleSym = resolveExternalModuleName(name, name); - if (moduleSym) { - const resolvedModuleSymbol = resolveExternalModuleSymbol(moduleSym); - if (resolvedModuleSymbol) { - return getTypeOfSymbol(resolvedModuleSymbol); - } - } - - return anyType; - } - - function getThisTypeOfSignature(signature: Signature): Type | undefined { - if (signature.thisParameter) { - return getTypeOfSymbol(signature.thisParameter); - } - } - - function getTypePredicateOfSignature(signature: Signature): TypePredicate | undefined { - if (!signature.resolvedTypePredicate) { - if (signature.target) { - const targetTypePredicate = getTypePredicateOfSignature(signature.target); - signature.resolvedTypePredicate = targetTypePredicate ? instantiateTypePredicate(targetTypePredicate, signature.mapper!) : noTypePredicate; - } - else if (signature.compositeSignatures) { - signature.resolvedTypePredicate = getUnionOrIntersectionTypePredicate(signature.compositeSignatures, signature.compositeKind) || noTypePredicate; - } - else { - const type = signature.declaration && getEffectiveReturnTypeNode(signature.declaration); - let jsdocPredicate: TypePredicate | undefined; - if (!type) { - const jsdocSignature = getSignatureOfTypeTag(signature.declaration!); - if (jsdocSignature && signature !== jsdocSignature) { - jsdocPredicate = getTypePredicateOfSignature(jsdocSignature); - } - } - signature.resolvedTypePredicate = type && isTypePredicateNode(type) ? - createTypePredicateFromTypePredicateNode(type, signature) : - jsdocPredicate || noTypePredicate; - } - Debug.assert(!!signature.resolvedTypePredicate); - } - return signature.resolvedTypePredicate === noTypePredicate ? undefined : signature.resolvedTypePredicate; - } - - function createTypePredicateFromTypePredicateNode(node: TypePredicateNode, signature: Signature): TypePredicate { - const parameterName = node.parameterName; - const type = node.type && getTypeFromTypeNode(node.type); - return parameterName.kind === SyntaxKind.ThisType ? - createTypePredicate(node.assertsModifier ? TypePredicateKind.AssertsThis : TypePredicateKind.This, /*parameterName*/ undefined, /*parameterIndex*/ undefined, type) : - createTypePredicate(node.assertsModifier ? TypePredicateKind.AssertsIdentifier : TypePredicateKind.Identifier, parameterName.escapedText as string, - findIndex(signature.parameters, p => p.escapedName === parameterName.escapedText), type); - } - - function getUnionOrIntersectionType(types: Type[], kind: TypeFlags | undefined, unionReduction?: UnionReduction) { - return kind !== TypeFlags.Intersection ? getUnionType(types, unionReduction) : getIntersectionType(types); - } - - function getReturnTypeOfSignature(signature: Signature): Type { - if (!signature.resolvedReturnType) { - if (!pushTypeResolution(signature, TypeSystemPropertyName.ResolvedReturnType)) { - return errorType; - } - let type = signature.target ? instantiateType(getReturnTypeOfSignature(signature.target), signature.mapper) : - signature.compositeSignatures ? instantiateType(getUnionOrIntersectionType(map(signature.compositeSignatures, getReturnTypeOfSignature), signature.compositeKind, UnionReduction.Subtype), signature.mapper) : - getReturnTypeFromAnnotation(signature.declaration!) || - (nodeIsMissing((signature.declaration as FunctionLikeDeclaration).body) ? anyType : getReturnTypeFromBody(signature.declaration as FunctionLikeDeclaration)); - if (signature.flags & SignatureFlags.IsInnerCallChain) { - type = addOptionalTypeMarker(type); - } - else if (signature.flags & SignatureFlags.IsOuterCallChain) { - type = getOptionalType(type); - } - if (!popTypeResolution()) { - if (signature.declaration) { - const typeNode = getEffectiveReturnTypeNode(signature.declaration); - if (typeNode) { - error(typeNode, Diagnostics.Return_type_annotation_circularly_references_itself); - } - else if (noImplicitAny) { - const declaration = signature.declaration as Declaration; - const name = getNameOfDeclaration(declaration); - if (name) { - error(name, Diagnostics._0_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions, declarationNameToString(name)); - } - else { - error(declaration, Diagnostics.Function_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions); - } - } - } - type = anyType; - } - signature.resolvedReturnType = type; - } - return signature.resolvedReturnType; - } - - function getReturnTypeFromAnnotation(declaration: SignatureDeclaration | JSDocSignature) { - if (declaration.kind === SyntaxKind.Constructor) { - return getDeclaredTypeOfClassOrInterface(getMergedSymbol((declaration.parent as ClassDeclaration).symbol)); - } - if (isJSDocConstructSignature(declaration)) { - return getTypeFromTypeNode((declaration.parameters[0] as ParameterDeclaration).type!); // TODO: GH#18217 - } - const typeNode = getEffectiveReturnTypeNode(declaration); - if (typeNode) { - return getTypeFromTypeNode(typeNode); - } - if (declaration.kind === SyntaxKind.GetAccessor && hasBindableName(declaration)) { - const jsDocType = isInJSFile(declaration) && getTypeForDeclarationFromJSDocComment(declaration); - if (jsDocType) { - return jsDocType; - } - const setter = getDeclarationOfKind(getSymbolOfNode(declaration), SyntaxKind.SetAccessor); - const setterType = getAnnotatedAccessorType(setter); - if (setterType) { - return setterType; - } - } - return getReturnTypeOfTypeTag(declaration); - } - - function isResolvingReturnTypeOfSignature(signature: Signature) { - return !signature.resolvedReturnType && findResolutionCycleStartIndex(signature, TypeSystemPropertyName.ResolvedReturnType) >= 0; - } - - function getRestTypeOfSignature(signature: Signature): Type { - return tryGetRestTypeOfSignature(signature) || anyType; - } - - function tryGetRestTypeOfSignature(signature: Signature): Type | undefined { - if (signatureHasRestParameter(signature)) { - const sigRestType = getTypeOfSymbol(signature.parameters[signature.parameters.length - 1]); - const restType = isTupleType(sigRestType) ? getRestTypeOfTupleType(sigRestType) : sigRestType; - return restType && getIndexTypeOfType(restType, numberType); - } - return undefined; - } - - function getSignatureInstantiation(signature: Signature, typeArguments: readonly Type[] | undefined, isJavascript: boolean, inferredTypeParameters?: readonly TypeParameter[]): Signature { - const instantiatedSignature = getSignatureInstantiationWithoutFillingInTypeArguments(signature, fillMissingTypeArguments(typeArguments, signature.typeParameters, getMinTypeArgumentCount(signature.typeParameters), isJavascript)); - if (inferredTypeParameters) { - const returnSignature = getSingleCallOrConstructSignature(getReturnTypeOfSignature(instantiatedSignature)); - if (returnSignature) { - const newReturnSignature = cloneSignature(returnSignature); - newReturnSignature.typeParameters = inferredTypeParameters; - const newInstantiatedSignature = cloneSignature(instantiatedSignature); - newInstantiatedSignature.resolvedReturnType = getOrCreateTypeFromSignature(newReturnSignature); - return newInstantiatedSignature; - } - } - return instantiatedSignature; - } - - function getSignatureInstantiationWithoutFillingInTypeArguments(signature: Signature, typeArguments: readonly Type[] | undefined): Signature { - const instantiations = signature.instantiations || (signature.instantiations = new Map()); - const id = getTypeListId(typeArguments); - let instantiation = instantiations.get(id); - if (!instantiation) { - instantiations.set(id, instantiation = createSignatureInstantiation(signature, typeArguments)); - } - return instantiation; - } - - function createSignatureInstantiation(signature: Signature, typeArguments: readonly Type[] | undefined): Signature { - return instantiateSignature(signature, createSignatureTypeMapper(signature, typeArguments), /*eraseTypeParameters*/ true); - } - - function createSignatureTypeMapper(signature: Signature, typeArguments: readonly Type[] | undefined): TypeMapper { - return createTypeMapper(signature.typeParameters!, typeArguments); - } - - function getErasedSignature(signature: Signature): Signature { - return signature.typeParameters ? - signature.erasedSignatureCache || (signature.erasedSignatureCache = createErasedSignature(signature)) : - signature; - } - - function createErasedSignature(signature: Signature) { - // Create an instantiation of the signature where all type arguments are the any type. - return instantiateSignature(signature, createTypeEraser(signature.typeParameters!), /*eraseTypeParameters*/ true); - } - - function getCanonicalSignature(signature: Signature): Signature { - return signature.typeParameters ? - signature.canonicalSignatureCache || (signature.canonicalSignatureCache = createCanonicalSignature(signature)) : - signature; - } - - function createCanonicalSignature(signature: Signature) { - // Create an instantiation of the signature where each unconstrained type parameter is replaced with - // its original. When a generic class or interface is instantiated, each generic method in the class or - // interface is instantiated with a fresh set of cloned type parameters (which we need to handle scenarios - // where different generations of the same type parameter are in scope). This leads to a lot of new type - // identities, and potentially a lot of work comparing those identities, so here we create an instantiation - // that uses the original type identities for all unconstrained type parameters. - return getSignatureInstantiation( - signature, - map(signature.typeParameters, tp => tp.target && !getConstraintOfTypeParameter(tp.target) ? tp.target : tp), - isInJSFile(signature.declaration)); - } - - function getBaseSignature(signature: Signature) { - const typeParameters = signature.typeParameters; - if (typeParameters) { - if (signature.baseSignatureCache) { - return signature.baseSignatureCache; - } - const typeEraser = createTypeEraser(typeParameters); - const baseConstraintMapper = createTypeMapper(typeParameters, map(typeParameters, tp => getConstraintOfTypeParameter(tp) || unknownType)); - let baseConstraints: readonly Type[] = map(typeParameters, tp => instantiateType(tp, baseConstraintMapper) || unknownType); - // Run N type params thru the immediate constraint mapper up to N times - // This way any noncircular interdependent type parameters are definitely resolved to their external dependencies - for (let i = 0; i < typeParameters.length - 1; i++) { - baseConstraints = instantiateTypes(baseConstraints, baseConstraintMapper); - } - // and then apply a type eraser to remove any remaining circularly dependent type parameters - baseConstraints = instantiateTypes(baseConstraints, typeEraser); - return signature.baseSignatureCache = instantiateSignature(signature, createTypeMapper(typeParameters, baseConstraints), /*eraseTypeParameters*/ true); - } - return signature; - } - - function getOrCreateTypeFromSignature(signature: Signature): ObjectType { - // There are two ways to declare a construct signature, one is by declaring a class constructor - // using the constructor keyword, and the other is declaring a bare construct signature in an - // object type literal or interface (using the new keyword). Each way of declaring a constructor - // will result in a different declaration kind. - if (!signature.isolatedSignatureType) { - const kind = signature.declaration?.kind; - - // If declaration is undefined, it is likely to be the signature of the default constructor. - const isConstructor = kind === undefined || kind === SyntaxKind.Constructor || kind === SyntaxKind.ConstructSignature || kind === SyntaxKind.ConstructorType; - - const type = createObjectType(ObjectFlags.Anonymous); - type.members = emptySymbols; - type.properties = emptyArray; - type.callSignatures = !isConstructor ? [signature] : emptyArray; - type.constructSignatures = isConstructor ? [signature] : emptyArray; - type.indexInfos = emptyArray; - signature.isolatedSignatureType = type; - } - - return signature.isolatedSignatureType; - } - - function getIndexSymbol(symbol: Symbol): Symbol | undefined { - return symbol.members ? getIndexSymbolFromSymbolTable(symbol.members) : undefined; - } - - function getIndexSymbolFromSymbolTable(symbolTable: SymbolTable): Symbol | undefined { - return symbolTable.get(InternalSymbolName.Index); - } - - function createIndexInfo(keyType: Type, type: Type, isReadonly: boolean, declaration?: IndexSignatureDeclaration): IndexInfo { - return { keyType, type, isReadonly, declaration }; - } - - function getIndexInfosOfSymbol(symbol: Symbol): IndexInfo[] { - const indexSymbol = getIndexSymbol(symbol); - return indexSymbol ? getIndexInfosOfIndexSymbol(indexSymbol) : emptyArray; - } - - function getIndexInfosOfIndexSymbol(indexSymbol: Symbol): IndexInfo[] { - if (indexSymbol.declarations) { - const indexInfos: IndexInfo[] = []; - for (const declaration of (indexSymbol.declarations as IndexSignatureDeclaration[])) { - if (declaration.parameters.length === 1) { - const parameter = declaration.parameters[0]; - if (parameter.type) { - forEachType(getTypeFromTypeNode(parameter.type), keyType => { - if (isValidIndexKeyType(keyType) && !findIndexInfo(indexInfos, keyType)) { - indexInfos.push(createIndexInfo(keyType, declaration.type ? getTypeFromTypeNode(declaration.type) : anyType, - hasEffectiveModifier(declaration, ModifierFlags.Readonly), declaration)); - } - }); - } - } - } - return indexInfos; - } - return emptyArray; - } - - function isValidIndexKeyType(type: Type): boolean { - return !!(type.flags & (TypeFlags.String | TypeFlags.Number | TypeFlags.ESSymbol)) || isPatternLiteralType(type) || - !!(type.flags & TypeFlags.Intersection) && !isGenericType(type) && some((type as IntersectionType).types, isValidIndexKeyType); - } - - function getConstraintDeclaration(type: TypeParameter): TypeNode | undefined { - return mapDefined(filter(type.symbol && type.symbol.declarations, isTypeParameterDeclaration), getEffectiveConstraintOfTypeParameter)[0]; - } - - function getInferredTypeParameterConstraint(typeParameter: TypeParameter, omitTypeReferences?: boolean) { - let inferences: Type[] | undefined; - if (typeParameter.symbol?.declarations) { - for (const declaration of typeParameter.symbol.declarations) { - if (declaration.parent.kind === SyntaxKind.InferType) { - // When an 'infer T' declaration is immediately contained in a type reference node - // (such as 'Foo'), T's constraint is inferred from the constraint of the - // corresponding type parameter in 'Foo'. When multiple 'infer T' declarations are - // present, we form an intersection of the inferred constraint types. - const [childTypeParameter = declaration.parent, grandParent] = walkUpParenthesizedTypesAndGetParentAndChild(declaration.parent.parent); - if (grandParent.kind === SyntaxKind.TypeReference && !omitTypeReferences) { - const typeReference = grandParent as TypeReferenceNode; - const typeParameters = getTypeParametersForTypeReference(typeReference); - if (typeParameters) { - const index = typeReference.typeArguments!.indexOf(childTypeParameter as TypeNode); - if (index < typeParameters.length) { - const declaredConstraint = getConstraintOfTypeParameter(typeParameters[index]); - if (declaredConstraint) { - // Type parameter constraints can reference other type parameters so - // constraints need to be instantiated. If instantiation produces the - // type parameter itself, we discard that inference. For example, in - // type Foo = [T, U]; - // type Bar = T extends Foo ? Foo : T; - // the instantiated constraint for U is X, so we discard that inference. - const mapper = makeDeferredTypeMapper(typeParameters, typeParameters.map((_, index) => () => { - return getEffectiveTypeArgumentAtIndex(typeReference, typeParameters, index); - })); - const constraint = instantiateType(declaredConstraint, mapper); - if (constraint !== typeParameter) { - inferences = append(inferences, constraint); - } - } - } - } - } - // When an 'infer T' declaration is immediately contained in a rest parameter declaration, a rest type - // or a named rest tuple element, we infer an 'unknown[]' constraint. - else if (grandParent.kind === SyntaxKind.Parameter && (grandParent as ParameterDeclaration).dotDotDotToken || - grandParent.kind === SyntaxKind.RestType || - grandParent.kind === SyntaxKind.NamedTupleMember && (grandParent as NamedTupleMember).dotDotDotToken) { - inferences = append(inferences, createArrayType(unknownType)); - } - // When an 'infer T' declaration is immediately contained in a string template type, we infer a 'string' - // constraint. - else if (grandParent.kind === SyntaxKind.TemplateLiteralTypeSpan) { - inferences = append(inferences, stringType); - } - // When an 'infer T' declaration is in the constraint position of a mapped type, we infer a 'keyof any' - // constraint. - else if (grandParent.kind === SyntaxKind.TypeParameter && grandParent.parent.kind === SyntaxKind.MappedType) { - inferences = append(inferences, keyofConstraintType); - } - // When an 'infer T' declaration is the template of a mapped type, and that mapped type is the extends - // clause of a conditional whose check type is also a mapped type, give it a constraint equal to the template - // of the check type's mapped type - else if (grandParent.kind === SyntaxKind.MappedType && (grandParent as MappedTypeNode).type && - skipParentheses((grandParent as MappedTypeNode).type!) === declaration.parent && grandParent.parent.kind === SyntaxKind.ConditionalType && - (grandParent.parent as ConditionalTypeNode).extendsType === grandParent && (grandParent.parent as ConditionalTypeNode).checkType.kind === SyntaxKind.MappedType && - ((grandParent.parent as ConditionalTypeNode).checkType as MappedTypeNode).type) { - const checkMappedType = (grandParent.parent as ConditionalTypeNode).checkType as MappedTypeNode; - const nodeType = getTypeFromTypeNode(checkMappedType.type!); - inferences = append(inferences, instantiateType(nodeType, - makeUnaryTypeMapper(getDeclaredTypeOfTypeParameter(getSymbolOfNode(checkMappedType.typeParameter)), checkMappedType.typeParameter.constraint ? getTypeFromTypeNode(checkMappedType.typeParameter.constraint) : keyofConstraintType) - )); - } - } - } - } - 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) { - const targetConstraint = getConstraintOfTypeParameter(typeParameter.target); - typeParameter.constraint = targetConstraint ? instantiateType(targetConstraint, typeParameter.mapper) : noConstraintType; - } - else { - const constraintDeclaration = getConstraintDeclaration(typeParameter); - if (!constraintDeclaration) { - typeParameter.constraint = getInferredTypeParameterConstraint(typeParameter) || noConstraintType; - } - else { - let type = getTypeFromTypeNode(constraintDeclaration); - if (type.flags & TypeFlags.Any && !isErrorType(type)) { // Allow errorType to propegate to keep downstream errors suppressed - // use keyofConstraintType as the base constraint for mapped type key constraints (unknown isn;t assignable to that, but `any` was), - // use unknown otherwise - type = constraintDeclaration.parent.parent.kind === SyntaxKind.MappedType ? keyofConstraintType : unknownType; - } - typeParameter.constraint = type; - } - } - } - return typeParameter.constraint === noConstraintType ? undefined : typeParameter.constraint; - } - - function getParentSymbolOfTypeParameter(typeParameter: TypeParameter): Symbol | undefined { - const tp = getDeclarationOfKind(typeParameter.symbol, SyntaxKind.TypeParameter)!; - const host = isJSDocTemplateTag(tp.parent) ? getEffectiveContainerForJSDocTemplateTag(tp.parent) : tp.parent; - return host && getSymbolOfNode(host); - } - - function getTypeListId(types: readonly Type[] | undefined) { - let result = ""; - if (types) { - const length = types.length; - let i = 0; - while (i < length) { - const startId = types[i].id; - let count = 1; - while (i + count < length && types[i + count].id === startId + count) { - count++; - } - if (result.length) { - result += ","; - } - result += startId; - if (count > 1) { - result += ":" + count; - } - i += count; - } - } - return result; - } - - function getAliasId(aliasSymbol: Symbol | undefined, aliasTypeArguments: readonly Type[] | undefined) { - return aliasSymbol ? `@${getSymbolId(aliasSymbol)}` + (aliasTypeArguments ? `:${getTypeListId(aliasTypeArguments)}` : "") : ""; - } - - // This function is used to propagate certain flags when creating new object type references and union types. - // It is only necessary to do so if a constituent type might be the undefined type, the null type, the type - // of an object literal or a non-inferrable type. This is because there are operations in the type checker - // that care about the presence of such types at arbitrary depth in a containing type. - function getPropagatingFlagsOfTypes(types: readonly Type[], excludeKinds?: TypeFlags): ObjectFlags { - let result: ObjectFlags = 0; - for (const type of types) { - if (excludeKinds === undefined || !(type.flags & excludeKinds)) { - result |= getObjectFlags(type); - } - } - return result & ObjectFlags.PropagatingFlags; - } - - function createTypeReference(target: GenericType, typeArguments: readonly Type[] | undefined): TypeReference { - const id = getTypeListId(typeArguments); - let type = target.instantiations.get(id); - if (!type) { - type = createObjectType(ObjectFlags.Reference, target.symbol) as TypeReference; - target.instantiations.set(id, type); - type.objectFlags |= typeArguments ? getPropagatingFlagsOfTypes(typeArguments) : 0; - type.target = target; - type.resolvedTypeArguments = typeArguments; - } - return type; - } - - function cloneTypeReference(source: TypeReference): TypeReference { - const type = createType(source.flags) as TypeReference; - type.symbol = source.symbol; - type.objectFlags = source.objectFlags; - type.target = source.target; - type.resolvedTypeArguments = source.resolvedTypeArguments; - return type; - } - - function createDeferredTypeReference(target: GenericType, node: TypeReferenceNode | ArrayTypeNode | TupleTypeNode, mapper?: TypeMapper, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]): DeferredTypeReference { - if (!aliasSymbol) { - aliasSymbol = getAliasSymbolForTypeNode(node); - const localAliasTypeArguments = getTypeArgumentsForAliasSymbol(aliasSymbol); - aliasTypeArguments = mapper ? instantiateTypes(localAliasTypeArguments, mapper) : localAliasTypeArguments; - } - const type = createObjectType(ObjectFlags.Reference, target.symbol) as DeferredTypeReference; - type.target = target; - type.node = node; - type.mapper = mapper; - type.aliasSymbol = aliasSymbol; - type.aliasTypeArguments = aliasTypeArguments; - return type; - } - - function getTypeArguments(type: TypeReference): readonly Type[] { - if (!type.resolvedTypeArguments) { - if (!pushTypeResolution(type, TypeSystemPropertyName.ResolvedTypeArguments)) { - return type.target.localTypeParameters?.map(() => errorType) || emptyArray; - } - const node = type.node; - const typeArguments = !node ? emptyArray : - node.kind === SyntaxKind.TypeReference ? concatenate(type.target.outerTypeParameters, getEffectiveTypeArguments(node, type.target.localTypeParameters!)) : - node.kind === SyntaxKind.ArrayType ? [getTypeFromTypeNode(node.elementType)] : - map(node.elements, getTypeFromTypeNode); - if (popTypeResolution()) { - type.resolvedTypeArguments = type.mapper ? instantiateTypes(typeArguments, type.mapper) : typeArguments; - } - else { - type.resolvedTypeArguments = type.target.localTypeParameters?.map(() => errorType) || emptyArray; - error( - type.node || currentNode, - type.target.symbol ? Diagnostics.Type_arguments_for_0_circularly_reference_themselves : Diagnostics.Tuple_type_arguments_circularly_reference_themselves, - type.target.symbol && symbolToString(type.target.symbol) - ); - } - } - return type.resolvedTypeArguments; - } - - function getTypeReferenceArity(type: TypeReference): number { - return length(type.target.typeParameters); - } - - - /** - * Get type from type-reference that reference to class or interface - */ - function getTypeFromClassOrInterfaceReference(node: NodeWithTypeArguments, symbol: Symbol): Type { - const type = getDeclaredTypeOfSymbol(getMergedSymbol(symbol)) as InterfaceType; - const typeParameters = type.localTypeParameters; - if (typeParameters) { - const numTypeArguments = length(node.typeArguments); - const minTypeArgumentCount = getMinTypeArgumentCount(typeParameters); - const isJs = isInJSFile(node); - const isJsImplicitAny = !noImplicitAny && isJs; - if (!isJsImplicitAny && (numTypeArguments < minTypeArgumentCount || numTypeArguments > typeParameters.length)) { - const missingAugmentsTag = isJs && isExpressionWithTypeArguments(node) && !isJSDocAugmentsTag(node.parent); - const diag = minTypeArgumentCount === typeParameters.length ? - missingAugmentsTag ? - Diagnostics.Expected_0_type_arguments_provide_these_with_an_extends_tag : - Diagnostics.Generic_type_0_requires_1_type_argument_s : - missingAugmentsTag ? - Diagnostics.Expected_0_1_type_arguments_provide_these_with_an_extends_tag : - Diagnostics.Generic_type_0_requires_between_1_and_2_type_arguments; - - const typeStr = typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType); - error(node, diag, typeStr, minTypeArgumentCount, typeParameters.length); - if (!isJs) { - // TODO: Adopt same permissive behavior in TS as in JS to reduce follow-on editing experience failures (requires editing fillMissingTypeArguments) - return errorType; - } - } - if (node.kind === SyntaxKind.TypeReference && isDeferredTypeReferenceNode(node as TypeReferenceNode, length(node.typeArguments) !== typeParameters.length)) { - return createDeferredTypeReference(type as GenericType, node as TypeReferenceNode, /*mapper*/ undefined); - } - // In a type reference, the outer type parameters of the referenced class or interface are automatically - // supplied as type arguments and the type reference only specifies arguments for the local type parameters - // of the class or interface. - const typeArguments = concatenate(type.outerTypeParameters, fillMissingTypeArguments(typeArgumentsFromTypeReferenceNode(node), typeParameters, minTypeArgumentCount, isJs)); - return createTypeReference(type as GenericType, typeArguments); - } - return checkNoTypeArguments(node, symbol) ? type : errorType; - } - - function getTypeAliasInstantiation(symbol: Symbol, typeArguments: readonly Type[] | undefined, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]): Type { - const type = getDeclaredTypeOfSymbol(symbol); - if (type === intrinsicMarkerType && intrinsicTypeKinds.has(symbol.escapedName as string) && typeArguments && typeArguments.length === 1) { - return getStringMappingType(symbol, typeArguments[0]); - } - const links = getSymbolLinks(symbol); - const typeParameters = links.typeParameters!; - const id = getTypeListId(typeArguments) + getAliasId(aliasSymbol, aliasTypeArguments); - let instantiation = links.instantiations!.get(id); - if (!instantiation) { - links.instantiations!.set(id, instantiation = instantiateTypeWithAlias(type, - createTypeMapper(typeParameters, fillMissingTypeArguments(typeArguments, typeParameters, getMinTypeArgumentCount(typeParameters), isInJSFile(symbol.valueDeclaration))), - aliasSymbol, aliasTypeArguments)); - } - return instantiation; - } - - /** - * Get type from reference to type alias. When a type alias is generic, the declared type of the type alias may include - * references to the type parameters of the alias. We replace those with the actual type arguments by instantiating the - * declared type. Instantiations are cached using the type identities of the type arguments as the key. - */ - function getTypeFromTypeAliasReference(node: NodeWithTypeArguments, symbol: Symbol): Type { - if (getCheckFlags(symbol) & CheckFlags.Unresolved) { - const typeArguments = typeArgumentsFromTypeReferenceNode(node); - const id = getAliasId(symbol, typeArguments); - let errorType = errorTypes.get(id); - if (!errorType) { - errorType = createIntrinsicType(TypeFlags.Any, "error"); - errorType.aliasSymbol = symbol; - errorType.aliasTypeArguments = typeArguments; - errorTypes.set(id, errorType); - } - return errorType; - } - const type = getDeclaredTypeOfSymbol(symbol); - const typeParameters = getSymbolLinks(symbol).typeParameters; - if (typeParameters) { - const numTypeArguments = length(node.typeArguments); - const minTypeArgumentCount = getMinTypeArgumentCount(typeParameters); - if (numTypeArguments < minTypeArgumentCount || numTypeArguments > typeParameters.length) { - error(node, - minTypeArgumentCount === typeParameters.length ? - Diagnostics.Generic_type_0_requires_1_type_argument_s : - Diagnostics.Generic_type_0_requires_between_1_and_2_type_arguments, - symbolToString(symbol), - minTypeArgumentCount, - typeParameters.length); - return errorType; - } - // We refrain from associating a local type alias with an instantiation of a top-level type alias - // because the local alias may end up being referenced in an inferred return type where it is not - // accessible--which in turn may lead to a large structural expansion of the type when generating - // a .d.ts file. See #43622 for an example. - const aliasSymbol = getAliasSymbolForTypeNode(node); - const newAliasSymbol = aliasSymbol && (isLocalTypeAlias(symbol) || !isLocalTypeAlias(aliasSymbol)) ? aliasSymbol : undefined; - return getTypeAliasInstantiation(symbol, typeArgumentsFromTypeReferenceNode(node), newAliasSymbol, getTypeArgumentsForAliasSymbol(newAliasSymbol)); - } - return checkNoTypeArguments(node, symbol) ? type : errorType; - } - - function isLocalTypeAlias(symbol: Symbol) { - const declaration = symbol.declarations?.find(isTypeAlias); - return !!(declaration && getContainingFunction(declaration)); - } - - function getTypeReferenceName(node: TypeReferenceType): EntityNameOrEntityNameExpression | undefined { + function traverse(node: Node): boolean { + if (!node) return false; switch (node.kind) { case SyntaxKind.Identifier: return (node as Identifier).escapedText === argumentsSymbol.escapedName && getReferencedValueSymbol(node as Identifier) === argumentsSymbol; @@ -15913,7 +15276,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return undefined; } - function getSignatureInstantiation(signature: Signature, typeArguments: Type[] | undefined, isJavascript: boolean, inferredTypeParameters?: readonly TypeParameter[]): Signature { + function getSignatureInstantiation(signature: Signature, typeArguments: readonly Type[] | undefined, isJavascript: boolean, inferredTypeParameters?: readonly TypeParameter[]): Signature { const instantiatedSignature = getSignatureInstantiationWithoutFillingInTypeArguments(signature, fillMissingTypeArguments(typeArguments, signature.typeParameters, getMinTypeArgumentCount(signature.typeParameters), isJavascript)); if (inferredTypeParameters) { const returnSignature = getSingleCallOrConstructSignature(getReturnTypeOfSignature(instantiatedSignature)); @@ -21914,946 +21277,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { !isErrorType(intrinsicAttributes) && !isErrorType(intrinsicClassAttributes) && (contains(targetTypes, intrinsicAttributes) || contains(targetTypes, intrinsicClassAttributes)) ) { - return Ternary.False; - } - // If the target comes from a partial union prop, allow `undefined` in the target type - const related = isPropertySymbolTypeRelated(sourceProp, targetProp, getTypeOfSourceProperty, reportErrors, intersectionState); - if (!related) { - if (reportErrors) { - reportIncompatibleError(Diagnostics.Types_of_property_0_are_incompatible, symbolToString(targetProp)); - } - return Ternary.False; - } - // When checking for comparability, be more lenient with optional properties. - if (!skipOptional && sourceProp.flags & SymbolFlags.Optional && !(targetProp.flags & SymbolFlags.Optional)) { - // TypeScript 1.0 spec (April 2014): 3.8.3 - // S is a subtype of a type T, and T is a supertype of S if ... - // S' and T are object types and, for each member M in T.. - // M is a property and S' contains a property N where - // if M is a required property, N is also a required property - // (M - property in T) - // (N - property in S) - if (reportErrors) { - reportError(Diagnostics.Property_0_is_optional_in_type_1_but_required_in_type_2, - symbolToString(targetProp), typeToString(source), typeToString(target)); - } - return Ternary.False; - } - return related; - } - - function reportUnmatchedProperty(source: Type, target: Type, unmatchedProperty: Symbol, requireOptionalProperties: boolean) { - let shouldSkipElaboration = false; - // give specific error in case where private names have the same description - if (unmatchedProperty.valueDeclaration - && isNamedDeclaration(unmatchedProperty.valueDeclaration) - && isPrivateIdentifier(unmatchedProperty.valueDeclaration.name) - && source.symbol - && source.symbol.flags & SymbolFlags.Class) { - const privateIdentifierDescription = unmatchedProperty.valueDeclaration.name.escapedText; - const symbolTableKey = getSymbolNameForPrivateIdentifier(source.symbol, privateIdentifierDescription); - if (symbolTableKey && getPropertyOfType(source, symbolTableKey)) { - const sourceName = factory.getDeclarationName(source.symbol.valueDeclaration); - const targetName = factory.getDeclarationName(target.symbol.valueDeclaration); - reportError( - Diagnostics.Property_0_in_type_1_refers_to_a_different_member_that_cannot_be_accessed_from_within_type_2, - diagnosticName(privateIdentifierDescription), - diagnosticName(sourceName.escapedText === "" ? anon : sourceName), - diagnosticName(targetName.escapedText === "" ? anon : targetName)); - return; - } - } - const props = arrayFrom(getUnmatchedProperties(source, target, requireOptionalProperties, /*matchDiscriminantProperties*/ false)); - if (!headMessage || (headMessage.code !== Diagnostics.Class_0_incorrectly_implements_interface_1.code && - headMessage.code !== Diagnostics.Class_0_incorrectly_implements_class_1_Did_you_mean_to_extend_1_and_inherit_its_members_as_a_subclass.code)) { - shouldSkipElaboration = true; // Retain top-level error for interface implementing issues, otherwise omit it - } - if (props.length === 1) { - const propName = symbolToString(unmatchedProperty, /*enclosingDeclaration*/ undefined, SymbolFlags.None, SymbolFormatFlags.AllowAnyNodeKind | SymbolFormatFlags.WriteComputedProps); - reportError(Diagnostics.Property_0_is_missing_in_type_1_but_required_in_type_2, propName, ...getTypeNamesForErrorDisplay(source, target)); - if (length(unmatchedProperty.declarations)) { - associateRelatedInfo(createDiagnosticForNode(unmatchedProperty.declarations![0], Diagnostics._0_is_declared_here, propName)); - } - if (shouldSkipElaboration && errorInfo) { - overrideNextErrorInfo++; - } - } - else if (tryElaborateArrayLikeErrors(source, target, /*reportErrors*/ false)) { - if (props.length > 5) { // arbitrary cutoff for too-long list form - reportError(Diagnostics.Type_0_is_missing_the_following_properties_from_type_1_Colon_2_and_3_more, typeToString(source), typeToString(target), map(props.slice(0, 4), p => symbolToString(p)).join(", "), props.length - 4); - } - else { - reportError(Diagnostics.Type_0_is_missing_the_following_properties_from_type_1_Colon_2, typeToString(source), typeToString(target), map(props, p => symbolToString(p)).join(", ")); - } - if (shouldSkipElaboration && errorInfo) { - overrideNextErrorInfo++; - } - } - // No array like or unmatched property error - just issue top level error (errorInfo = undefined) - } - - function propertiesRelatedTo(source: Type, target: Type, reportErrors: boolean, excludedProperties: Set<__String> | undefined, intersectionState: IntersectionState): Ternary { - if (relation === identityRelation) { - return propertiesIdenticalTo(source, target, excludedProperties); - } - let result = Ternary.True; - if (isTupleType(target)) { - if (isArrayOrTupleType(source)) { - if (!target.target.readonly && (isReadonlyArrayType(source) || isTupleType(source) && source.target.readonly)) { - return Ternary.False; - } - const sourceArity = getTypeReferenceArity(source); - const targetArity = getTypeReferenceArity(target); - const sourceRestFlag = isTupleType(source) ? source.target.combinedFlags & ElementFlags.Rest : ElementFlags.Rest; - const targetRestFlag = target.target.combinedFlags & ElementFlags.Rest; - const sourceMinLength = isTupleType(source) ? source.target.minLength : 0; - const targetMinLength = target.target.minLength; - if (!sourceRestFlag && sourceArity < targetMinLength) { - if (reportErrors) { - reportError(Diagnostics.Source_has_0_element_s_but_target_requires_1, sourceArity, targetMinLength); - } - return Ternary.False; - } - if (!targetRestFlag && targetArity < sourceMinLength) { - if (reportErrors) { - reportError(Diagnostics.Source_has_0_element_s_but_target_allows_only_1, sourceMinLength, targetArity); - } - return Ternary.False; - } - if (!targetRestFlag && (sourceRestFlag || targetArity < sourceArity)) { - if (reportErrors) { - if (sourceMinLength < targetMinLength) { - reportError(Diagnostics.Target_requires_0_element_s_but_source_may_have_fewer, targetMinLength); - } - else { - reportError(Diagnostics.Target_allows_only_0_element_s_but_source_may_have_more, targetArity); - } - } - return Ternary.False; - } - const sourceTypeArguments = getTypeArguments(source); - const targetTypeArguments = getTypeArguments(target); - const startCount = Math.min(isTupleType(source) ? getStartElementCount(source.target, ElementFlags.NonRest) : 0, getStartElementCount(target.target, ElementFlags.NonRest)); - const endCount = Math.min(isTupleType(source) ? getEndElementCount(source.target, ElementFlags.NonRest) : 0, targetRestFlag ? getEndElementCount(target.target, ElementFlags.NonRest) : 0); - let canExcludeDiscriminants = !!excludedProperties; - for (let i = 0; i < targetArity; i++) { - const sourceIndex = i < targetArity - endCount ? i : i + sourceArity - targetArity; - const sourceFlags = isTupleType(source) && (i < startCount || i >= targetArity - endCount) ? source.target.elementFlags[sourceIndex] : ElementFlags.Rest; - const targetFlags = target.target.elementFlags[i]; - if (targetFlags & ElementFlags.Variadic && !(sourceFlags & ElementFlags.Variadic)) { - if (reportErrors) { - reportError(Diagnostics.Source_provides_no_match_for_variadic_element_at_position_0_in_target, i); - } - return Ternary.False; - } - if (sourceFlags & ElementFlags.Variadic && !(targetFlags & ElementFlags.Variable)) { - if (reportErrors) { - reportError(Diagnostics.Variadic_element_at_position_0_in_source_does_not_match_element_at_position_1_in_target, sourceIndex, i); - } - return Ternary.False; - } - if (targetFlags & ElementFlags.Required && !(sourceFlags & ElementFlags.Required)) { - if (reportErrors) { - reportError(Diagnostics.Source_provides_no_match_for_required_element_at_position_0_in_target, i); - } - return Ternary.False; - } - // We can only exclude discriminant properties if we have not yet encountered a variable-length element. - if (canExcludeDiscriminants) { - if (sourceFlags & ElementFlags.Variable || targetFlags & ElementFlags.Variable) { - canExcludeDiscriminants = false; - } - if (canExcludeDiscriminants && excludedProperties?.has(("" + i) as __String)) { - continue; - } - } - const sourceType = !isTupleType(source) ? sourceTypeArguments[0] : - i < startCount || i >= targetArity - endCount ? removeMissingType(sourceTypeArguments[sourceIndex], !!(sourceFlags & targetFlags & ElementFlags.Optional)) : - getElementTypeOfSliceOfTupleType(source, startCount, endCount) || neverType; - const targetType = targetTypeArguments[i]; - const targetCheckType = sourceFlags & ElementFlags.Variadic && targetFlags & ElementFlags.Rest ? createArrayType(targetType) : - removeMissingType(targetType, !!(targetFlags & ElementFlags.Optional)); - const related = isRelatedTo(sourceType, targetCheckType, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState); - if (!related) { - if (reportErrors && (targetArity > 1 || sourceArity > 1)) { - if (i < startCount || i >= targetArity - endCount || sourceArity - startCount - endCount === 1) { - reportIncompatibleError(Diagnostics.Type_at_position_0_in_source_is_not_compatible_with_type_at_position_1_in_target, sourceIndex, i); - } - else { - reportIncompatibleError(Diagnostics.Type_at_positions_0_through_1_in_source_is_not_compatible_with_type_at_position_2_in_target, startCount, sourceArity - endCount - 1, i); - } - } - return Ternary.False; - } - result &= related; - } - return result; - } - if (target.target.combinedFlags & ElementFlags.Variable) { - return Ternary.False; - } - } - const requireOptionalProperties = (relation === subtypeRelation || relation === strictSubtypeRelation) && !isObjectLiteralType(source) && !isEmptyArrayLiteralType(source) && !isTupleType(source); - const unmatchedProperty = getUnmatchedProperty(source, target, requireOptionalProperties, /*matchDiscriminantProperties*/ false); - if (unmatchedProperty) { - if (reportErrors && shouldReportUnmatchedPropertyError(source, target)) { - reportUnmatchedProperty(source, target, unmatchedProperty, requireOptionalProperties); - } - return Ternary.False; - } - if (isObjectLiteralType(target)) { - for (const sourceProp of excludeProperties(getPropertiesOfType(source), excludedProperties)) { - if (!getPropertyOfObjectType(target, sourceProp.escapedName)) { - const sourceType = getTypeOfSymbol(sourceProp); - if (!(sourceType.flags & TypeFlags.Undefined)) { - if (reportErrors) { - reportError(Diagnostics.Property_0_does_not_exist_on_type_1, symbolToString(sourceProp), typeToString(target)); - } - return Ternary.False; - } - } - } - } - // We only call this for union target types when we're attempting to do excess property checking - in those cases, we want to get _all possible props_ - // from the target union, across all members - const properties = getPropertiesOfType(target); - const numericNamesOnly = isTupleType(source) && isTupleType(target); - for (const targetProp of excludeProperties(properties, excludedProperties)) { - const name = targetProp.escapedName; - if (!(targetProp.flags & SymbolFlags.Prototype) && (!numericNamesOnly || isNumericLiteralName(name) || name === "length")) { - const sourceProp = getPropertyOfType(source, name); - if (sourceProp && sourceProp !== targetProp) { - const related = propertyRelatedTo(source, target, sourceProp, targetProp, getNonMissingTypeOfSymbol, reportErrors, intersectionState, relation === comparableRelation); - if (!related) { - return Ternary.False; - } - result &= related; - } - } - } - return result; - } - - function propertiesIdenticalTo(source: Type, target: Type, excludedProperties: Set<__String> | undefined): Ternary { - if (!(source.flags & TypeFlags.Object && target.flags & TypeFlags.Object)) { - return Ternary.False; - } - const sourceProperties = excludeProperties(getPropertiesOfObjectType(source), excludedProperties); - const targetProperties = excludeProperties(getPropertiesOfObjectType(target), excludedProperties); - if (sourceProperties.length !== targetProperties.length) { - return Ternary.False; - } - let result = Ternary.True; - for (const sourceProp of sourceProperties) { - const targetProp = getPropertyOfObjectType(target, sourceProp.escapedName); - if (!targetProp) { - return Ternary.False; - } - const related = compareProperties(sourceProp, targetProp, isRelatedTo); - if (!related) { - return Ternary.False; - } - result &= related; - } - return result; - } - - function signaturesRelatedTo(source: Type, target: Type, kind: SignatureKind, reportErrors: boolean): Ternary { - if (relation === identityRelation) { - return signaturesIdenticalTo(source, target, kind); - } - if (target === anyFunctionType || source === anyFunctionType) { - return Ternary.True; - } - - const sourceIsJSConstructor = source.symbol && isJSConstructor(source.symbol.valueDeclaration); - const targetIsJSConstructor = target.symbol && isJSConstructor(target.symbol.valueDeclaration); - - const sourceSignatures = getSignaturesOfType(source, (sourceIsJSConstructor && kind === SignatureKind.Construct) ? - SignatureKind.Call : kind); - const targetSignatures = getSignaturesOfType(target, (targetIsJSConstructor && kind === SignatureKind.Construct) ? - SignatureKind.Call : kind); - - if (kind === SignatureKind.Construct && sourceSignatures.length && targetSignatures.length) { - const sourceIsAbstract = !!(sourceSignatures[0].flags & SignatureFlags.Abstract); - const targetIsAbstract = !!(targetSignatures[0].flags & SignatureFlags.Abstract); - if (sourceIsAbstract && !targetIsAbstract) { - // An abstract constructor type is not assignable to a non-abstract constructor type - // as it would otherwise be possible to new an abstract class. Note that the assignability - // check we perform for an extends clause excludes construct signatures from the target, - // so this check never proceeds. - if (reportErrors) { - reportError(Diagnostics.Cannot_assign_an_abstract_constructor_type_to_a_non_abstract_constructor_type); - } - return Ternary.False; - } - if (!constructorVisibilitiesAreCompatible(sourceSignatures[0], targetSignatures[0], reportErrors)) { - return Ternary.False; - } - } - - let result = Ternary.True; - const incompatibleReporter = kind === SignatureKind.Construct ? reportIncompatibleConstructSignatureReturn : reportIncompatibleCallSignatureReturn; - const sourceObjectFlags = getObjectFlags(source); - const targetObjectFlags = getObjectFlags(target); - if (sourceObjectFlags & ObjectFlags.Instantiated && targetObjectFlags & ObjectFlags.Instantiated && source.symbol === target.symbol || - sourceObjectFlags & ObjectFlags.Reference && targetObjectFlags & ObjectFlags.Reference && (source as TypeReference).target === (target as TypeReference).target) { - // We have instantiations of the same anonymous type (which typically will be the type of a - // method). Simply do a pairwise comparison of the signatures in the two signature lists instead - // of the much more expensive N * M comparison matrix we explore below. We instantiate the source - // signature with the type parameters of the target signature to unify the two. - for (let i = 0; i < targetSignatures.length; i++) { - const sourceSignature = sourceSignatures[i]; - const targetSignature = targetSignatures[i]; - const instantiatedSourceSignature = targetSignature.typeParameters ? - getSignatureInstantiation(sourceSignature, targetSignature.typeParameters, isInJSFile(sourceSignature.declaration)) : - sourceSignature; - const related = signatureRelatedTo(instantiatedSourceSignature, targetSignature, /*erase*/ false, reportErrors, incompatibleReporter(instantiatedSourceSignature, targetSignature)); - if (!related) { - return Ternary.False; - } - result &= related; - } - } - else if (sourceSignatures.length === 1 && targetSignatures.length === 1) { - // For simple functions (functions with a single signature) we only erase type parameters for - // the comparable relation. Otherwise, if the source signature is generic, we instantiate it - // in the context of the target signature before checking the relationship. Ideally we'd do - // this regardless of the number of signatures, but the potential costs are prohibitive due - // to the quadratic nature of the logic below. - const eraseGenerics = relation === comparableRelation || !!compilerOptions.noStrictGenericChecks; - const sourceSignature = first(sourceSignatures); - const targetSignature = first(targetSignatures); - result = signatureRelatedTo(sourceSignature, targetSignature, eraseGenerics, reportErrors, incompatibleReporter(sourceSignature, targetSignature)); - if (!result && reportErrors && kind === SignatureKind.Construct && (sourceObjectFlags & targetObjectFlags) && - (targetSignature.declaration?.kind === SyntaxKind.Constructor || sourceSignature.declaration?.kind === SyntaxKind.Constructor)) { - const constructSignatureToString = (signature: Signature) => - signatureToString(signature, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrowStyleSignature, kind); - reportError(Diagnostics.Type_0_is_not_assignable_to_type_1, constructSignatureToString(sourceSignature), constructSignatureToString(targetSignature)); - reportError(Diagnostics.Types_of_construct_signatures_are_incompatible); - return result; - } - } - else { - outer: for (const t of targetSignatures) { - const saveErrorInfo = captureErrorCalculationState(); - // Only elaborate errors from the first failure - let shouldElaborateErrors = reportErrors; - for (const s of sourceSignatures) { - const related = signatureRelatedTo(s, t, /*erase*/ true, shouldElaborateErrors, incompatibleReporter(s, t)); - if (related) { - result &= related; - resetErrorInfo(saveErrorInfo); - continue outer; - } - shouldElaborateErrors = false; - } - if (shouldElaborateErrors) { - reportError(Diagnostics.Type_0_provides_no_match_for_the_signature_1, - typeToString(source), - signatureToString(t, /*enclosingDeclaration*/ undefined, /*flags*/ undefined, kind)); - } - return Ternary.False; - } - } - return result; - } - - function shouldReportUnmatchedPropertyError(source: Type, target: Type): boolean { - const typeCallSignatures = getSignaturesOfStructuredType(source, SignatureKind.Call); - const typeConstructSignatures = getSignaturesOfStructuredType(source, SignatureKind.Construct); - const typeProperties = getPropertiesOfObjectType(source); - if ((typeCallSignatures.length || typeConstructSignatures.length) && !typeProperties.length) { - if ((getSignaturesOfType(target, SignatureKind.Call).length && typeCallSignatures.length) || - (getSignaturesOfType(target, SignatureKind.Construct).length && typeConstructSignatures.length)) { - return true; // target has similar signature kinds to source, still focus on the unmatched property - } - return false; - } - return true; - } - - function reportIncompatibleCallSignatureReturn(siga: Signature, sigb: Signature) { - if (siga.parameters.length === 0 && sigb.parameters.length === 0) { - return (source: Type, target: Type) => reportIncompatibleError(Diagnostics.Call_signatures_with_no_arguments_have_incompatible_return_types_0_and_1, typeToString(source), typeToString(target)); - } - return (source: Type, target: Type) => reportIncompatibleError(Diagnostics.Call_signature_return_types_0_and_1_are_incompatible, typeToString(source), typeToString(target)); - } - - function reportIncompatibleConstructSignatureReturn(siga: Signature, sigb: Signature) { - if (siga.parameters.length === 0 && sigb.parameters.length === 0) { - return (source: Type, target: Type) => reportIncompatibleError(Diagnostics.Construct_signatures_with_no_arguments_have_incompatible_return_types_0_and_1, typeToString(source), typeToString(target)); - } - return (source: Type, target: Type) => reportIncompatibleError(Diagnostics.Construct_signature_return_types_0_and_1_are_incompatible, typeToString(source), typeToString(target)); - } - - /** - * See signatureAssignableTo, compareSignaturesIdentical - */ - function signatureRelatedTo(source: Signature, target: Signature, erase: boolean, reportErrors: boolean, incompatibleReporter: (source: Type, target: Type) => void): Ternary { - return compareSignaturesRelated(erase ? getErasedSignature(source) : source, erase ? getErasedSignature(target) : target, - relation === strictSubtypeRelation ? SignatureCheckMode.StrictArity : 0, reportErrors, reportError, incompatibleReporter, isRelatedToWorker, reportUnreliableMapper); - } - - function signaturesIdenticalTo(source: Type, target: Type, kind: SignatureKind): Ternary { - const sourceSignatures = getSignaturesOfType(source, kind); - const targetSignatures = getSignaturesOfType(target, kind); - if (sourceSignatures.length !== targetSignatures.length) { - return Ternary.False; - } - let result = Ternary.True; - for (let i = 0; i < sourceSignatures.length; i++) { - const related = compareSignaturesIdentical(sourceSignatures[i], targetSignatures[i], /*partialMatch*/ false, /*ignoreThisTypes*/ false, /*ignoreReturnTypes*/ false, isRelatedTo); - if (!related) { - return Ternary.False; - } - result &= related; - } - return result; - } - - function membersRelatedToIndexInfo(source: Type, targetInfo: IndexInfo, reportErrors: boolean): Ternary { - let result = Ternary.True; - const keyType = targetInfo.keyType; - const props = source.flags & TypeFlags.Intersection ? getPropertiesOfUnionOrIntersectionType(source as IntersectionType) : getPropertiesOfObjectType(source); - for (const prop of props) { - // Skip over ignored JSX and symbol-named members - if (isIgnoredJsxProperty(source, prop)) { - continue; - } - if (isApplicableIndexType(getLiteralTypeFromProperty(prop, TypeFlags.StringOrNumberLiteralOrUnique), keyType)) { - const propType = getNonMissingTypeOfSymbol(prop); - const type = exactOptionalPropertyTypes || propType.flags & TypeFlags.Undefined || keyType === numberType || !(prop.flags & SymbolFlags.Optional) - ? propType - : getTypeWithFacts(propType, TypeFacts.NEUndefined); - const related = isRelatedTo(type, targetInfo.type, RecursionFlags.Both, reportErrors); - if (!related) { - if (reportErrors) { - reportError(Diagnostics.Property_0_is_incompatible_with_index_signature, symbolToString(prop)); - } - return Ternary.False; - } - result &= related; - } - } - for (const info of getIndexInfosOfType(source)) { - if (isApplicableIndexType(info.keyType, keyType)) { - const related = indexInfoRelatedTo(info, targetInfo, reportErrors); - if (!related) { - return Ternary.False; - } - result &= related; - } - } - return result; - } - - function indexInfoRelatedTo(sourceInfo: IndexInfo, targetInfo: IndexInfo, reportErrors: boolean) { - const related = isRelatedTo(sourceInfo.type, targetInfo.type, RecursionFlags.Both, reportErrors); - if (!related && reportErrors) { - if (sourceInfo.keyType === targetInfo.keyType) { - reportError(Diagnostics._0_index_signatures_are_incompatible, typeToString(sourceInfo.keyType)); - } - else { - reportError(Diagnostics._0_and_1_index_signatures_are_incompatible, typeToString(sourceInfo.keyType), typeToString(targetInfo.keyType)); - } - } - return related; - } - - function indexSignaturesRelatedTo(source: Type, target: Type, sourceIsPrimitive: boolean, reportErrors: boolean, intersectionState: IntersectionState): Ternary { - if (relation === identityRelation) { - return indexSignaturesIdenticalTo(source, target); - } - const indexInfos = getIndexInfosOfType(target); - const targetHasStringIndex = some(indexInfos, info => info.keyType === stringType); - let result = Ternary.True; - for (const targetInfo of indexInfos) { - const related = !sourceIsPrimitive && targetHasStringIndex && targetInfo.type.flags & TypeFlags.Any ? Ternary.True : - isGenericMappedType(source) && targetHasStringIndex ? isRelatedTo(getTemplateTypeFromMappedType(source), targetInfo.type, RecursionFlags.Both, reportErrors) : - typeRelatedToIndexInfo(source, targetInfo, reportErrors, intersectionState); - if (!related) { - return Ternary.False; - } - result &= related; - } - return result; - } - - function typeRelatedToIndexInfo(source: Type, targetInfo: IndexInfo, reportErrors: boolean, intersectionState: IntersectionState): Ternary { - const sourceInfo = getApplicableIndexInfo(source, targetInfo.keyType); - if (sourceInfo) { - return indexInfoRelatedTo(sourceInfo, targetInfo, reportErrors); - } - if (!(intersectionState & IntersectionState.Source) && isObjectTypeWithInferableIndex(source)) { - // Intersection constituents are never considered to have an inferred index signature - return membersRelatedToIndexInfo(source, targetInfo, reportErrors); - } - if (reportErrors) { - reportError(Diagnostics.Index_signature_for_type_0_is_missing_in_type_1, typeToString(targetInfo.keyType), typeToString(source)); - } - return Ternary.False; - } - - function indexSignaturesIdenticalTo(source: Type, target: Type): Ternary { - const sourceInfos = getIndexInfosOfType(source); - const targetInfos = getIndexInfosOfType(target); - if (sourceInfos.length !== targetInfos.length) { - return Ternary.False; - } - for (const targetInfo of targetInfos) { - const sourceInfo = getIndexInfoOfType(source, targetInfo.keyType); - if (!(sourceInfo && isRelatedTo(sourceInfo.type, targetInfo.type, RecursionFlags.Both) && sourceInfo.isReadonly === targetInfo.isReadonly)) { - return Ternary.False; - } - } - return Ternary.True; - } - - function constructorVisibilitiesAreCompatible(sourceSignature: Signature, targetSignature: Signature, reportErrors: boolean) { - if (!sourceSignature.declaration || !targetSignature.declaration) { - return true; - } - - const sourceAccessibility = getSelectedEffectiveModifierFlags(sourceSignature.declaration, ModifierFlags.NonPublicAccessibilityModifier); - const targetAccessibility = getSelectedEffectiveModifierFlags(targetSignature.declaration, ModifierFlags.NonPublicAccessibilityModifier); - - // A public, protected and private signature is assignable to a private signature. - if (targetAccessibility === ModifierFlags.Private) { - return true; - } - - // A public and protected signature is assignable to a protected signature. - if (targetAccessibility === ModifierFlags.Protected && sourceAccessibility !== ModifierFlags.Private) { - return true; - } - - // Only a public signature is assignable to public signature. - if (targetAccessibility !== ModifierFlags.Protected && !sourceAccessibility) { - return true; - } - - if (reportErrors) { - reportError(Diagnostics.Cannot_assign_a_0_constructor_type_to_a_1_constructor_type, visibilityToString(sourceAccessibility), visibilityToString(targetAccessibility)); - } - - return false; - } - } - - function typeCouldHaveTopLevelSingletonTypes(type: Type): boolean { - // Okay, yes, 'boolean' is a union of 'true | false', but that's not useful - // in error reporting scenarios. If you need to use this function but that detail matters, - // feel free to add a flag. - if (type.flags & TypeFlags.Boolean) { - return false; - } - - if (type.flags & TypeFlags.UnionOrIntersection) { - return !!forEach((type as IntersectionType).types, typeCouldHaveTopLevelSingletonTypes); - } - - if (type.flags & TypeFlags.Instantiable) { - const constraint = getConstraintOfType(type); - if (constraint && constraint !== type) { - return typeCouldHaveTopLevelSingletonTypes(constraint); - } - } - - return isUnitType(type) || !!(type.flags & TypeFlags.TemplateLiteral) || !!(type.flags & TypeFlags.StringMapping); - } - - function getExactOptionalUnassignableProperties(source: Type, target: Type) { - if (isTupleType(source) && isTupleType(target)) return emptyArray; - return getPropertiesOfType(target) - .filter(targetProp => isExactOptionalPropertyMismatch(getTypeOfPropertyOfType(source, targetProp.escapedName), getTypeOfSymbol(targetProp))); - } - - function isExactOptionalPropertyMismatch(source: Type | undefined, target: Type | undefined) { - return !!source && !!target && maybeTypeOfKind(source, TypeFlags.Undefined) && !!containsMissingType(target); - } - - function getExactOptionalProperties(type: Type) { - return getPropertiesOfType(type).filter(targetProp => containsMissingType(getTypeOfSymbol(targetProp))); - } - - function getBestMatchingType(source: Type, target: UnionOrIntersectionType, isRelatedTo = compareTypesAssignable) { - return findMatchingDiscriminantType(source, target, isRelatedTo, /*skipPartial*/ true) || - findMatchingTypeReferenceOrTypeAliasReference(source, target) || - findBestTypeForObjectLiteral(source, target) || - findBestTypeForInvokable(source, target) || - findMostOverlappyType(source, target); - } - - function discriminateTypeByDiscriminableItems(target: UnionType, discriminators: [() => Type, __String][], related: (source: Type, target: Type) => boolean | Ternary, defaultValue?: undefined, skipPartial?: boolean): Type | undefined; - function discriminateTypeByDiscriminableItems(target: UnionType, discriminators: [() => Type, __String][], related: (source: Type, target: Type) => boolean | Ternary, defaultValue: Type, skipPartial?: boolean): Type; - function discriminateTypeByDiscriminableItems(target: UnionType, discriminators: [() => Type, __String][], related: (source: Type, target: Type) => boolean | Ternary, defaultValue?: Type, skipPartial?: boolean) { - // undefined=unknown, true=discriminated, false=not discriminated - // The state of each type progresses from left to right. Discriminated types stop at 'true'. - const discriminable = target.types.map(_ => undefined) as (boolean | undefined)[]; - for (const [getDiscriminatingType, propertyName] of discriminators) { - const targetProp = getUnionOrIntersectionProperty(target, propertyName); - if (skipPartial && targetProp && getCheckFlags(targetProp) & CheckFlags.ReadPartial) { - continue; - } - let i = 0; - for (const type of target.types) { - const targetType = getTypeOfPropertyOfType(type, propertyName); - if (targetType && related(getDiscriminatingType(), targetType)) { - discriminable[i] = discriminable[i] === undefined ? true : discriminable[i]; - } - else { - discriminable[i] = false; - } - i++; - } - } - const match = discriminable.indexOf(/*searchElement*/ true); - if (match === -1) { - return defaultValue; - } - // make sure exactly 1 matches before returning it - let nextMatch = discriminable.indexOf(/*searchElement*/ true, match + 1); - while (nextMatch !== -1) { - if (!isTypeIdenticalTo(target.types[match], target.types[nextMatch])) { - return defaultValue; - } - nextMatch = discriminable.indexOf(/*searchElement*/ true, nextMatch + 1); - } - return target.types[match]; - } - - /** - * A type is 'weak' if it is an object type with at least one optional property - * and no required properties, call/construct signatures or index signatures - */ - function isWeakType(type: Type): boolean { - if (type.flags & TypeFlags.Object) { - const resolved = resolveStructuredTypeMembers(type as ObjectType); - return resolved.callSignatures.length === 0 && resolved.constructSignatures.length === 0 && resolved.indexInfos.length === 0 && - resolved.properties.length > 0 && every(resolved.properties, p => !!(p.flags & SymbolFlags.Optional)); - } - if (type.flags & TypeFlags.Intersection) { - return every((type as IntersectionType).types, isWeakType); - } - return false; - } - - function hasCommonProperties(source: Type, target: Type, isComparingJsxAttributes: boolean) { - for (const prop of getPropertiesOfType(source)) { - if (isKnownProperty(target, prop.escapedName, isComparingJsxAttributes)) { - return true; - } - } - return false; - } - - function getVariances(type: GenericType): VarianceFlags[] { - // Arrays and tuples are known to be covariant, no need to spend time computing this. - return type === globalArrayType || type === globalReadonlyArrayType || type.objectFlags & ObjectFlags.Tuple ? - arrayVariances : - getVariancesWorker(type.symbol, type.typeParameters); - } - - function getAliasVariances(symbol: Symbol) { - return getVariancesWorker(symbol, getSymbolLinks(symbol).typeParameters); - } - - // Return an array containing the variance of each type parameter. The variance is effectively - // a digest of the type comparisons that occur for each type argument when instantiations of the - // generic type are structurally compared. We infer the variance information by comparing - // instantiations of the generic type for type arguments with known relations. The function - // returns the emptyArray singleton when invoked recursively for the given generic type. - function getVariancesWorker(symbol: Symbol, typeParameters: readonly TypeParameter[] = emptyArray): VarianceFlags[] { - const links = getSymbolLinks(symbol); - if (!links.variances) { - tracing?.push(tracing.Phase.CheckTypes, "getVariancesWorker", { arity: typeParameters.length, id: getTypeId(getDeclaredTypeOfSymbol(symbol)) }); - links.variances = emptyArray; - const variances = []; - for (const tp of typeParameters) { - const modifiers = getVarianceModifiers(tp); - let variance = modifiers & ModifierFlags.Out ? - modifiers & ModifierFlags.In ? VarianceFlags.Invariant : VarianceFlags.Covariant : - modifiers & ModifierFlags.In ? VarianceFlags.Contravariant : undefined; - if (variance === undefined) { - let unmeasurable = false; - let unreliable = false; - const oldHandler = outofbandVarianceMarkerHandler; - outofbandVarianceMarkerHandler = (onlyUnreliable) => onlyUnreliable ? unreliable = true : unmeasurable = true; - // We first compare instantiations where the type parameter is replaced with - // marker types that have a known subtype relationship. From this we can infer - // invariance, covariance, contravariance or bivariance. - const typeWithSuper = createMarkerType(symbol, tp, markerSuperType); - const typeWithSub = createMarkerType(symbol, tp, markerSubType); - variance = (isTypeAssignableTo(typeWithSub, typeWithSuper) ? VarianceFlags.Covariant : 0) | - (isTypeAssignableTo(typeWithSuper, typeWithSub) ? VarianceFlags.Contravariant : 0); - // If the instantiations appear to be related bivariantly it may be because the - // type parameter is independent (i.e. it isn't witnessed anywhere in the generic - // type). To determine this we compare instantiations where the type parameter is - // replaced with marker types that are known to be unrelated. - if (variance === VarianceFlags.Bivariant && isTypeAssignableTo(createMarkerType(symbol, tp, markerOtherType), typeWithSuper)) { - variance = VarianceFlags.Independent; - } - outofbandVarianceMarkerHandler = oldHandler; - if (unmeasurable || unreliable) { - if (unmeasurable) { - variance |= VarianceFlags.Unmeasurable; - } - if (unreliable) { - variance |= VarianceFlags.Unreliable; - } - } - } - variances.push(variance); - } - links.variances = variances; - tracing?.pop({ variances: variances.map(Debug.formatVariance) }); - } - return links.variances; - } - - function createMarkerType(symbol: Symbol, source: TypeParameter, target: Type) { - const mapper = makeUnaryTypeMapper(source, target); - const type = getDeclaredTypeOfSymbol(symbol); - if (isErrorType(type)) { - return type; - } - const result = symbol.flags & SymbolFlags.TypeAlias ? - getTypeAliasInstantiation(symbol, instantiateTypes(getSymbolLinks(symbol).typeParameters!, mapper)) : - createTypeReference(type as GenericType, instantiateTypes((type as GenericType).typeParameters, mapper)); - markerTypes.add(getTypeId(result)); - return result; - } - - function isMarkerType(type: Type) { - return markerTypes.has(getTypeId(type)); - } - - function getVarianceModifiers(tp: TypeParameter): ModifierFlags { - return (some(tp.symbol?.declarations, d => hasSyntacticModifier(d, ModifierFlags.In)) ? ModifierFlags.In : 0) | - (some(tp.symbol?.declarations, d => hasSyntacticModifier(d, ModifierFlags.Out)) ? ModifierFlags.Out: 0); - } - - // Return true if the given type reference has a 'void' type argument for a covariant type parameter. - // See comment at call in recursiveTypeRelatedTo for when this case matters. - function hasCovariantVoidArgument(typeArguments: readonly Type[], variances: VarianceFlags[]): boolean { - for (let i = 0; i < variances.length; i++) { - if ((variances[i] & VarianceFlags.VarianceMask) === VarianceFlags.Covariant && typeArguments[i].flags & TypeFlags.Void) { - return true; - } - } - return false; - } - - function isUnconstrainedTypeParameter(type: Type) { - return type.flags & TypeFlags.TypeParameter && !getConstraintOfTypeParameter(type as TypeParameter); - } - - function isNonDeferredTypeReference(type: Type): type is TypeReference { - return !!(getObjectFlags(type) & ObjectFlags.Reference) && !(type as TypeReference).node; - } - - function isTypeReferenceWithGenericArguments(type: Type): boolean { - return isNonDeferredTypeReference(type) && some(getTypeArguments(type), t => !!(t.flags & TypeFlags.TypeParameter) || isTypeReferenceWithGenericArguments(t)); - } - - function getGenericTypeReferenceRelationKey(source: TypeReference, target: TypeReference, postFix: string, ignoreConstraints: boolean) { - const typeParameters: Type[] = []; - let constraintMarker = ""; - const sourceId = getTypeReferenceId(source, 0); - const targetId = getTypeReferenceId(target, 0); - return `${constraintMarker}${sourceId},${targetId}${postFix}`; - // getTypeReferenceId(A) returns "111=0-12=1" - // where A.id=111 and number.id=12 - function getTypeReferenceId(type: TypeReference, depth = 0) { - let result = "" + type.target.id; - for (const t of getTypeArguments(type)) { - if (t.flags & TypeFlags.TypeParameter) { - if (ignoreConstraints || isUnconstrainedTypeParameter(t)) { - let index = typeParameters.indexOf(t); - if (index < 0) { - index = typeParameters.length; - typeParameters.push(t); - } - result += "=" + index; - continue; - } - // We mark type references that reference constrained type parameters such that we know to obtain - // and look for a "broadest equivalent key" in the cache. - constraintMarker = "*"; - } - else if (depth < 4 && isTypeReferenceWithGenericArguments(t)) { - result += "<" + getTypeReferenceId(t as TypeReference, depth + 1) + ">"; - continue; - } - result += "-" + t.id; - } - return result; - } - } - - /** - * To improve caching, the relation key for two generic types uses the target's id plus ids of the type parameters. - * For other cases, the types ids are used. - */ - function getRelationKey(source: Type, target: Type, intersectionState: IntersectionState, relation: ESMap, ignoreConstraints: boolean) { - if (relation === identityRelation && source.id > target.id) { - const temp = source; - source = target; - target = temp; - } - const postFix = intersectionState ? ":" + intersectionState : ""; - return isTypeReferenceWithGenericArguments(source) && isTypeReferenceWithGenericArguments(target) ? - getGenericTypeReferenceRelationKey(source as TypeReference, target as TypeReference, postFix, ignoreConstraints) : - `${source.id},${target.id}${postFix}`; - } - - // Invoke the callback for each underlying property symbol of the given symbol and return the first - // value that isn't undefined. - function forEachProperty(prop: Symbol, callback: (p: Symbol) => T): T | undefined { - if (getCheckFlags(prop) & CheckFlags.Synthetic) { - for (const t of (prop as TransientSymbol).containingType!.types) { - const p = getPropertyOfType(t, prop.escapedName); - const result = p && forEachProperty(p, callback); - if (result) { - return result; - } - } - return undefined; - } - return callback(prop); - } - - // Return the declaring class type of a property or undefined if property not declared in class - function getDeclaringClass(prop: Symbol) { - return prop.parent && prop.parent.flags & SymbolFlags.Class ? getDeclaredTypeOfSymbol(getParentOfSymbol(prop)!) as InterfaceType : undefined; - } - - // Return the inherited type of the given property or undefined if property doesn't exist in a base class. - function getTypeOfPropertyInBaseClass(property: Symbol) { - const classType = getDeclaringClass(property); - const baseClassType = classType && getBaseTypes(classType)[0]; - return baseClassType && getTypeOfPropertyOfType(baseClassType, property.escapedName); - } - - // Return true if some underlying source property is declared in a class that derives - // from the given base class. - function isPropertyInClassDerivedFrom(prop: Symbol, baseClass: Type | undefined) { - return forEachProperty(prop, sp => { - const sourceClass = getDeclaringClass(sp); - return sourceClass ? hasBaseType(sourceClass, baseClass) : false; - }); - } - - // Return true if source property is a valid override of protected parts of target property. - function isValidOverrideOf(sourceProp: Symbol, targetProp: Symbol) { - return !forEachProperty(targetProp, tp => getDeclarationModifierFlagsFromSymbol(tp) & ModifierFlags.Protected ? - !isPropertyInClassDerivedFrom(sourceProp, getDeclaringClass(tp)) : false); - } - - // Return true if the given class derives from each of the declaring classes of the protected - // constituents of the given property. - function isClassDerivedFromDeclaringClasses(checkClass: T, prop: Symbol, writing: boolean) { - return forEachProperty(prop, p => getDeclarationModifierFlagsFromSymbol(p, writing) & ModifierFlags.Protected ? - !hasBaseType(checkClass, getDeclaringClass(p)) : false) ? undefined : checkClass; - } - - // Return true if the given type is deeply nested. We consider this to be the case when structural type comparisons - // for maxDepth or more occurrences or instantiations of the type have been recorded on the given stack. It is possible, - // though highly unlikely, for this test to be true in a situation where a chain of instantiations is not infinitely - // expanding. Effectively, we will generate a false positive when two types are structurally equal to at least maxDepth - // levels, but unequal at some level beyond that. - // In addition, this will also detect when an indexed access has been chained off of maxDepth more times (which is - // essentially the dual of the structural comparison), and likewise mark the type as deeply nested, potentially adding - // false positives for finite but deeply expanding indexed accesses (eg, for `Q[P1][P2][P3][P4][P5]`). - // It also detects when a recursive type reference has expanded maxDepth or more times, e.g. if the true branch of - // `type A = null extends T ? [A>] : [T]` - // has expanded into `[A>>>>>]`. In such cases we need - // to terminate the expansion, and we do so here. - function isDeeplyNestedType(type: Type, stack: Type[], depth: number, maxDepth = 3): boolean { - if (depth >= maxDepth) { - const identity = getRecursionIdentity(type); - let count = 0; - let lastTypeId = 0; - for (let i = 0; i < depth; i++) { - const t = stack[i]; - if (getRecursionIdentity(t) === identity) { - // We only count occurrences with a higher type id than the previous occurrence, since higher - // type ids are an indicator of newer instantiations caused by recursion. - if (t.id >= lastTypeId) { - count++; - if (count >= maxDepth) { - return true; - } - } - lastTypeId = t.id; - } - } - } - return false; - } - - // The recursion identity of a type is an object identity that is shared among multiple instantiations of the type. - // We track recursion identities in order to identify deeply nested and possibly infinite type instantiations with - // the same origin. For example, when type parameters are in scope in an object type such as { x: T }, all - // instantiations of that type have the same recursion identity. The default recursion identity is the object - // identity of the type, meaning that every type is unique. Generally, types with constituents that could circularly - // reference the type have a recursion identity that differs from the object identity. - function getRecursionIdentity(type: Type): object { - // Object and array literals are known not to contain recursive references and don't need a recursion identity. - if (type.flags & TypeFlags.Object && !isObjectOrArrayLiteralType(type)) { - if (getObjectFlags(type) && ObjectFlags.Reference && (type as TypeReference).node) { - // Deferred type references are tracked through their associated AST node. This gives us finer - // granularity than using their associated target because each manifest type reference has a - // unique AST node. - return (type as TypeReference).node!; - } - if (type.symbol && !(getObjectFlags(type) & ObjectFlags.Anonymous && type.symbol.flags & SymbolFlags.Class)) { - // We track all object types that have an associated symbol (representing the origin of the type), but - // exclude the static side of classes from this check since it shares its symbol with the instance side. - return type.symbol; - } - if (isTupleType(type)) { - // Tuple types are tracked through their target type - return type.target; - } - } - if (type.flags & TypeFlags.TypeParameter) { - return type.symbol; - } - if (type.flags & TypeFlags.IndexedAccess) { - // Identity is the leftmost object type in a chain of indexed accesses, eg, in A[P][Q] it is A - do { - type = (type as IndexedAccessType).objectType; - } while (type.flags & TypeFlags.IndexedAccess); - return type; - } - if (type.flags & TypeFlags.Conditional) { - // The root object represents the origin of the conditional type - return (type as ConditionalType).root; - } - return type; - } - - function isPropertyIdenticalTo(sourceProp: Symbol, targetProp: Symbol): boolean { - return compareProperties(sourceProp, targetProp, compareTypesIdentical) !== Ternary.False; - } - - function compareProperties(sourceProp: Symbol, targetProp: Symbol, compareTypes: (source: Type, target: Type) => Ternary): Ternary { - // Two members are considered identical when - // - they are public properties with identical names, optionality, and types, - // - they are private or protected properties originating in the same declaration and having identical types - if (sourceProp === targetProp) { - return Ternary.True; - } - const sourcePropAccessibility = getDeclarationModifierFlagsFromSymbol(sourceProp) & ModifierFlags.NonPublicAccessibilityModifier; - const targetPropAccessibility = getDeclarationModifierFlagsFromSymbol(targetProp) & ModifierFlags.NonPublicAccessibilityModifier; - if (sourcePropAccessibility !== targetPropAccessibility) { - return Ternary.False; - } - if (sourcePropAccessibility) { - if (getTargetSymbol(sourceProp) !== getTargetSymbol(targetProp)) { - return Ternary.False; + // do not report top error + return; } } else { @@ -24538,10 +22963,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { ) { // We have instantiations of the same anonymous type (which typically will be the type of a // method). Simply do a pairwise comparison of the signatures in the two signature lists instead - // of the much more expensive N * M comparison matrix we explore below. We erase type parameters - // as they are known to always be the same. + // of the much more expensive N * M comparison matrix we explore below. We instantiate the source + // signature with the type parameters of the target signature to unify the two. for (let i = 0; i < targetSignatures.length; i++) { - const related = signatureRelatedTo(sourceSignatures[i], targetSignatures[i], /*erase*/ true, reportErrors, intersectionState, incompatibleReporter(sourceSignatures[i], targetSignatures[i])); + const sourceSignature = sourceSignatures[i]; + const targetSignature = targetSignatures[i]; + const instantiatedSourceSignature = targetSignature.typeParameters ? + getSignatureInstantiation(sourceSignature, targetSignature.typeParameters, isInJSFile(sourceSignature.declaration)) : + sourceSignature; + const related = signatureRelatedTo(instantiatedSourceSignature, targetSignature, /*erase*/ false, reportErrors, intersectionState, incompatibleReporter(instantiatedSourceSignature, targetSignature)); if (!related) { return Ternary.False; } From a1047daf8b6cef31cb601aa2d7972022005fef0e Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 18 Oct 2023 07:07:26 -0700 Subject: [PATCH 5/5] Accept baseline changes --- .../baselines/reference/genericSignatureRelations.js | 12 +++++++----- .../reference/genericSignatureRelations.symbols | 4 +++- .../reference/genericSignatureRelations.types | 8 +++++--- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/tests/baselines/reference/genericSignatureRelations.js b/tests/baselines/reference/genericSignatureRelations.js index c12e9f2ceeb59..48b363df00765 100644 --- a/tests/baselines/reference/genericSignatureRelations.js +++ b/tests/baselines/reference/genericSignatureRelations.js @@ -1,3 +1,5 @@ +//// [tests/cases/compiler/genericSignatureRelations.ts] //// + //// [genericSignatureRelations.ts] // Repro from #48070 @@ -16,8 +18,8 @@ type Result2 = S<'s1'> extends S<'s2'> ? true : false; //// [genericSignatureRelations.d.ts] -declare type S = () => T extends X ? 1 : '2'; -declare type Foo1 = S<'s1'>; -declare type Foo2 = S<'s2'>; -declare type Result1 = Foo1 extends Foo2 ? true : false; -declare type Result2 = S<'s1'> extends S<'s2'> ? true : false; +type S = () => T extends X ? 1 : '2'; +type Foo1 = S<'s1'>; +type Foo2 = S<'s2'>; +type Result1 = Foo1 extends Foo2 ? true : false; +type Result2 = S<'s1'> extends S<'s2'> ? true : false; diff --git a/tests/baselines/reference/genericSignatureRelations.symbols b/tests/baselines/reference/genericSignatureRelations.symbols index 01f533c613554..13a4c1233afbc 100644 --- a/tests/baselines/reference/genericSignatureRelations.symbols +++ b/tests/baselines/reference/genericSignatureRelations.symbols @@ -1,4 +1,6 @@ -=== tests/cases/compiler/genericSignatureRelations.ts === +//// [tests/cases/compiler/genericSignatureRelations.ts] //// + +=== genericSignatureRelations.ts === // Repro from #48070 type S = () => T extends X ? 1 : '2'; diff --git a/tests/baselines/reference/genericSignatureRelations.types b/tests/baselines/reference/genericSignatureRelations.types index 48cb1b124a06e..b923d1a246610 100644 --- a/tests/baselines/reference/genericSignatureRelations.types +++ b/tests/baselines/reference/genericSignatureRelations.types @@ -1,14 +1,16 @@ -=== tests/cases/compiler/genericSignatureRelations.ts === +//// [tests/cases/compiler/genericSignatureRelations.ts] //// + +=== genericSignatureRelations.ts === // Repro from #48070 type S = () => T extends X ? 1 : '2'; >S : S type Foo1 = S<'s1'>; ->Foo1 : Foo1 +>Foo1 : () => T extends "s1" ? 1 : "2" type Foo2 = S<'s2'>; ->Foo2 : Foo2 +>Foo2 : () => T extends "s2" ? 1 : "2" type Result1 = Foo1 extends Foo2 ? true : false; >Result1 : false