From 398533bb4a37170326328491d238ed4f7236644a Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 4 Feb 2024 13:44:32 -0800 Subject: [PATCH 1/3] Support recursive type aliases for object types --- src/compiler/checker.ts | 143 +++++++++++++++++++++++++++------------- src/compiler/types.ts | 68 ++++++++++--------- 2 files changed, 135 insertions(+), 76 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f15fd70378ea7..f0f223ca8447c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1032,6 +1032,7 @@ import { TupleTypeReference, Type, TypeAliasDeclaration, + TypeAliasInstantiationType, TypeAssertion, TypeChecker, TypeCheckerHost, @@ -6530,9 +6531,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return factory.createKeywordTypeNode(SyntaxKind.AnyKeyword); } - if (!(context.flags & NodeBuilderFlags.NoTypeReduction)) { - type = getReducedType(type); - } + type = context.flags & NodeBuilderFlags.NoTypeReduction ? getResolvedType(type) : getReducedType(type); if (type.flags & TypeFlags.Any) { if (type.aliasSymbol) { @@ -13747,6 +13746,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } + function resolveTypeAliasInstantiationMembers(type: TypeAliasInstantiationType) { + const resolved = resolveStructuredTypeMembers(getResolvedTypeAliasInstantiation(type)); + type.members = resolved.members; + type.properties = resolved.properties; + type.callSignatures = resolved.callSignatures; + type.constructSignatures = resolved.constructSignatures; + type.indexInfos = resolved.indexInfos; + } + type ReplaceableIndexedAccessType = IndexedAccessType & { objectType: TypeParameter; indexType: TypeParameter; }; function replaceIndexedAccess(instantiable: Type, type: ReplaceableIndexedAccessType, replacement: Type) { // map type.indexType to 0 @@ -14090,6 +14098,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { else if ((type as MappedType).objectFlags & ObjectFlags.Mapped) { resolveMappedTypeMembers(type as MappedType); } + else if ((type as MappedType).objectFlags & ObjectFlags.TypeAliasInstantiation) { + resolveTypeAliasInstantiationMembers(type as TypeAliasInstantiationType); + } else { Debug.fail("Unhandled object type " + Debug.formatObjectFlags(type.objectFlags)); } @@ -14824,7 +14835,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { /** * Return the reduced form of the given type. For a union type, it is a union of the normalized constituent types. * For an intersection of types containing one or more mututally exclusive discriminant properties, it is 'never'. - * For all other types, it is simply the type itself. Discriminant properties are considered mutually exclusive when + * For all other types, it is the resolved type itself. Discriminant properties are considered mutually exclusive when * no constituent property has type 'never', but the intersection of the constituent property types is 'never'. */ function getReducedType(type: Type): Type { @@ -14838,7 +14849,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } return (type as IntersectionType).objectFlags & ObjectFlags.IsNeverIntersection ? neverType : type; } - return type; + return getResolvedType(type); } function getReducedUnionType(unionType: UnionType) { @@ -14846,7 +14857,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (reducedTypes === unionType.types) { return unionType; } - const reduced = getUnionType(reducedTypes); + const reduced = getResolvedType(getUnionType(reducedTypes)); if (reduced.flags & TypeFlags.Union) { (reduced as UnionType).resolvedReducedType = reduced; } @@ -15965,7 +15976,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return errorType; } } - if (node.kind === SyntaxKind.TypeReference && isDeferredTypeReferenceNode(node as TypeReferenceNode, length(node.typeArguments) !== typeParameters.length)) { + if (node.kind === SyntaxKind.TypeReference && (getAliasSymbolForTypeNode(node) || 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 @@ -15977,24 +15988,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { 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) { - const typeKind = intrinsicTypeKinds.get(symbol.escapedName as string); - if (typeKind !== undefined && typeArguments && typeArguments.length === 1) { - return typeKind === IntrinsicTypeKind.NoInfer ? getNoInferType(typeArguments[0]) : 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 @@ -16048,10 +16041,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const resolved = resolveAlias(aliasSymbol); if (resolved && resolved.flags & SymbolFlags.TypeAlias) { newAliasSymbol = resolved; - aliasTypeArguments = typeArgumentsFromTypeReferenceNode(node) || (typeParameters ? [] : undefined); + aliasTypeArguments = typeArgumentsFromTypeReferenceNode(node) ?? []; } } } + // If we have an instantiation of a generic type alias for a type reference, an anonymous type, or a + // mapped type, and the instantiation occurs in a context where resolving type arguments might cause a + // circular reference, we create a deferred type alias instantiation. This acts as a proxy for the + // actual instantiation, which is obtained using getResolvedType. + if (getObjectFlags(type) & (ObjectFlags.Reference | ObjectFlags.Anonymous | ObjectFlags.Mapped) && node.kind === SyntaxKind.TypeReference && + isDeferredTypeReferenceNode(node as TypeReferenceNode, length(node.typeArguments) !== typeParameters.length)) { + return createDeferredTypeAliasInstantiation(symbol, node, newAliasSymbol, aliasTypeArguments); + } return getTypeAliasInstantiation(symbol, typeArgumentsFromTypeReferenceNode(node), newAliasSymbol, aliasTypeArguments); } return checkNoTypeArguments(node, symbol) ? type : errorType; @@ -16062,6 +16063,49 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return !!(declaration && getContainingFunction(declaration)); } + function getTypeAliasInstantiation(symbol: Symbol, typeArguments: readonly Type[] | undefined, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]): Type { + const type = getDeclaredTypeOfSymbol(symbol); + if (type === intrinsicMarkerType) { + const typeKind = intrinsicTypeKinds.get(symbol.escapedName as string); + if (typeKind !== undefined && typeArguments && typeArguments.length === 1) { + return typeKind === IntrinsicTypeKind.NoInfer ? getNoInferType(typeArguments[0]) : 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; + } + + function createDeferredTypeAliasInstantiation(typeAlias: Symbol, node: NodeWithTypeArguments, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]): TypeAliasInstantiationType { + const type = getDeclaredTypeOfSymbol(typeAlias); + const result = createObjectType(ObjectFlags.TypeAliasInstantiation, type.symbol) as TypeAliasInstantiationType; + result.typeAlias = typeAlias; + result.node = node; + result.aliasSymbol = aliasSymbol; + result.aliasTypeArguments = aliasTypeArguments; + return result; + } + + function getResolvedType(type: Type): Type { + return type.flags & TypeFlags.Object && (type as ObjectType).objectFlags & ObjectFlags.TypeAliasInstantiation ? + getResolvedTypeAliasInstantiation(type as TypeAliasInstantiationType) : + type; + } + + function getResolvedTypeAliasInstantiation(type: TypeAliasInstantiationType): AnonymousType { + if (!type.resolvedType) { + const typeArguments = typeArgumentsFromTypeReferenceNode(type.node); + const mappedTypeArguments = type.mapper ? instantiateTypes(typeArguments, type.mapper) : typeArguments; + type.resolvedType = getTypeAliasInstantiation(type.typeAlias, mappedTypeArguments, type.aliasSymbol, type.aliasTypeArguments) as ObjectType; + } + return type.resolvedType; + } + function getTypeReferenceName(node: TypeReferenceType): EntityNameOrEntityNameExpression | undefined { switch (node.kind) { case SyntaxKind.TypeReference: @@ -16674,14 +16718,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return isNamedTupleMember(member) || isParameter(member) ? member : undefined; } - // Return true if the given type reference node is directly aliased or if it needs to be deferred - // because it is possibly contained in a circular chain of eagerly resolved types. + // Return true if the given type reference node needs to be deferred because it is possibly contained + // in a circular chain of eagerly resolved types. function isDeferredTypeReferenceNode(node: TypeReferenceNode | ArrayTypeNode | TupleTypeNode, hasDefaultTypeArguments?: boolean) { - return !!getAliasSymbolForTypeNode(node) || isResolvedByTypeAlias(node) && ( - node.kind === SyntaxKind.ArrayType ? mayResolveTypeAlias(node.elementType) : - node.kind === SyntaxKind.TupleType ? some(node.elements, mayResolveTypeAlias) : - hasDefaultTypeArguments || some(node.typeArguments, mayResolveTypeAlias) - ); + return isResolvedByTypeAlias(node) && ( + node.kind === SyntaxKind.ArrayType ? mayResolveTypeAlias(node.elementType) : + node.kind === SyntaxKind.TupleType ? some(node.elements, mayResolveTypeAlias) : + hasDefaultTypeArguments || some(node.typeArguments, mayResolveTypeAlias) + ); } // Return true when the given node is transitively contained in type constructs that eagerly @@ -16746,7 +16790,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (target === emptyGenericType) { links.resolvedType = emptyObjectType; } - else if (!(node.kind === SyntaxKind.TupleType && some(node.elements, e => !!(getTupleElementFlags(e) & ElementFlags.Variadic))) && isDeferredTypeReferenceNode(node)) { + else if (!(node.kind === SyntaxKind.TupleType && some(node.elements, e => !!(getTupleElementFlags(e) & ElementFlags.Variadic))) && (getAliasSymbolForTypeNode(node) || isDeferredTypeReferenceNode(node))) { links.resolvedType = node.kind === SyntaxKind.TupleType && node.elements.length === 0 ? target : createDeferredTypeReference(target, node, /*mapper*/ undefined); } @@ -18391,14 +18435,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } return (type as SubstitutionType).objectFlags & ObjectFlags.IsGenericType; } - return (type.flags & TypeFlags.InstantiableNonPrimitive || isGenericMappedType(type) || isGenericTupleType(type) ? ObjectFlags.IsGenericObjectType : 0) | + return (type.flags & TypeFlags.InstantiableNonPrimitive || isGenericMappedType(getResolvedType(type)) || isGenericTupleType(type) ? ObjectFlags.IsGenericObjectType : 0) | (type.flags & (TypeFlags.InstantiableNonPrimitive | TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping) && !isPatternLiteralType(type) ? ObjectFlags.IsGenericIndexType : 0); } function getSimplifiedType(type: Type, writing: boolean): Type { return type.flags & TypeFlags.IndexedAccess ? getSimplifiedIndexedAccessType(type as IndexedAccessType, writing) : type.flags & TypeFlags.Conditional ? getSimplifiedConditionalType(type as ConditionalType, writing) : - type; + getResolvedType(type); } function distributeIndexOverObjectType(objectType: Type, indexType: Type, writing: boolean) { @@ -19623,6 +19667,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function getObjectTypeInstantiation(type: AnonymousType | DeferredTypeReference, mapper: TypeMapper, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]) { const declaration = type.objectFlags & ObjectFlags.Reference ? (type as TypeReference).node! : type.objectFlags & ObjectFlags.InstantiationExpressionType ? (type as InstantiationExpressionType).node : + type.objectFlags & ObjectFlags.TypeAliasInstantiation ? (type as TypeAliasInstantiationType).node : type.symbol.declarations![0]; const links = getNodeLinks(declaration); const target = type.objectFlags & ObjectFlags.Reference ? links.resolvedType! as DeferredTypeReference : @@ -19639,8 +19684,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { outerTypeParameters = addRange(outerTypeParameters, templateTagParameters); } typeParameters = outerTypeParameters || emptyArray; - const allDeclarations = type.objectFlags & (ObjectFlags.Reference | ObjectFlags.InstantiationExpressionType) ? [declaration] : type.symbol.declarations!; - typeParameters = (target.objectFlags & (ObjectFlags.Reference | ObjectFlags.InstantiationExpressionType) || target.symbol.flags & SymbolFlags.Method || target.symbol.flags & SymbolFlags.TypeLiteral) && !target.aliasTypeArguments ? + const allDeclarations = type.objectFlags & (ObjectFlags.Reference | ObjectFlags.InstantiationExpressionType | ObjectFlags.TypeAliasInstantiation) ? [declaration] : type.symbol.declarations!; + typeParameters = (target.objectFlags & (ObjectFlags.Reference | ObjectFlags.InstantiationExpressionType | ObjectFlags.TypeAliasInstantiation) || target.symbol.flags & SymbolFlags.Method || target.symbol.flags & SymbolFlags.TypeLiteral) && !target.aliasTypeArguments ? filter(typeParameters, tp => some(allDeclarations, d => isTypeParameterPossiblyReferenced(tp, d))) : typeParameters; links.outerTypeParameters = typeParameters; @@ -19843,7 +19888,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function instantiateAnonymousType(type: AnonymousType, mapper: TypeMapper, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[]): AnonymousType { - Debug.assert(type.symbol, "anonymous type must have symbol to be instantiated"); const result = createObjectType(type.objectFlags & ~(ObjectFlags.CouldContainTypeVariablesComputed | ObjectFlags.CouldContainTypeVariables) | ObjectFlags.Instantiated, type.symbol) as AnonymousType; if (type.objectFlags & ObjectFlags.Mapped) { (result as MappedType).declaration = (type as MappedType).declaration; @@ -19854,9 +19898,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { mapper = combineTypeMappers(makeUnaryTypeMapper(origTypeParameter, freshTypeParameter), mapper); freshTypeParameter.mapper = mapper; } - if (type.objectFlags & ObjectFlags.InstantiationExpressionType) { + else if (type.objectFlags & ObjectFlags.InstantiationExpressionType) { (result as InstantiationExpressionType).node = (type as InstantiationExpressionType).node; } + else if (type.objectFlags & ObjectFlags.TypeAliasInstantiation) { + (result as TypeAliasInstantiationType).typeAlias = (type as TypeAliasInstantiationType).typeAlias; + (result as TypeAliasInstantiationType).node = (type as TypeAliasInstantiationType).node; + } result.target = type; result.mapper = mapper; result.aliasSymbol = aliasSymbol || type.aliasSymbol; @@ -19924,7 +19972,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } if (flags & TypeFlags.Object) { const objectFlags = (type as ObjectType).objectFlags; - if (objectFlags & (ObjectFlags.Reference | ObjectFlags.Anonymous | ObjectFlags.Mapped)) { + if (objectFlags & (ObjectFlags.Reference | ObjectFlags.Anonymous | ObjectFlags.Mapped | ObjectFlags.TypeAliasInstantiation)) { if (objectFlags & ObjectFlags.Reference && !(type as TypeReference).node) { const resolvedTypeArguments = (type as TypeReference).resolvedTypeArguments; const newTypeArguments = instantiateTypes(resolvedTypeArguments, mapper); @@ -21170,6 +21218,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const t = isFreshLiteralType(type) ? (type as FreshableType).regularType : isGenericTupleType(type) ? getNormalizedTupleType(type, writing) : getObjectFlags(type) & ObjectFlags.Reference ? (type as TypeReference).node ? createTypeReference((type as TypeReference).target, getTypeArguments(type as TypeReference)) : getSingleBaseForNonAugmentingSubtype(type) || type : + getObjectFlags(type) & ObjectFlags.TypeAliasInstantiation ? getResolvedTypeAliasInstantiation(type as TypeAliasInstantiationType) : type.flags & TypeFlags.UnionOrIntersection ? getNormalizedUnionOrIntersectionType(type as UnionOrIntersectionType, writing) : type.flags & TypeFlags.Substitution ? writing ? (type as SubstitutionType).baseType : getSubstitutionIntersection(type as SubstitutionType) : type.flags & TypeFlags.Simplifiable ? getSimplifiedType(type, writing) : @@ -24018,7 +24067,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { type = getMappedTargetWithSymbol(type); } if (type.flags & TypeFlags.Intersection) { - return some((type as IntersectionType).types, t => isDeeplyNestedType(t, stack, depth, maxDepth)); + return some((type as IntersectionType).types, t => isDeeplyNestedType(getResolvedType(t), stack, depth, maxDepth)); } const identity = getRecursionIdentity(type); let count = 0; @@ -24062,7 +24111,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { type = getMappedTargetWithSymbol(type); } if (type.flags & TypeFlags.Intersection) { - return some((type as IntersectionType).types, t => hasMatchingRecursionIdentity(t, identity)); + return some((type as IntersectionType).types, t => hasMatchingRecursionIdentity(getResolvedType(t), identity)); } return getRecursionIdentity(type) === identity; } @@ -25124,7 +25173,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { type.flags & TypeFlags.Object && !isNonGenericTopLevelType(type) && ( objectFlags & ObjectFlags.Reference && ((type as TypeReference).node || some(getTypeArguments(type as TypeReference), couldContainTypeVariables)) || objectFlags & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) && type.symbol.declarations || - objectFlags & (ObjectFlags.Mapped | ObjectFlags.ReverseMapped | ObjectFlags.ObjectRestType | ObjectFlags.InstantiationExpressionType) + objectFlags & (ObjectFlags.Mapped | ObjectFlags.ReverseMapped | ObjectFlags.ObjectRestType | ObjectFlags.InstantiationExpressionType | ObjectFlags.TypeAliasInstantiation) ) || type.flags & TypeFlags.UnionOrIntersection && !(type.flags & TypeFlags.EnumLiteral) && !isNonGenericTopLevelType(type) && some((type as UnionOrIntersectionType).types, couldContainTypeVariables)); if (type.flags & TypeFlags.ObjectFlagsType) { @@ -25504,6 +25553,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { propagationType = savePropagationType; return; } + source = getResolvedType(source); + target = getResolvedType(target); if (source.aliasSymbol && source.aliasSymbol === target.aliasSymbol) { if (source.aliasTypeArguments) { // Source and target are types originating in the same generic type alias declaration. @@ -28874,11 +28925,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { location = location.parent; } if (isExpressionNode(location) && (!isAssignmentTarget(location) || isWriteAccess(location))) { - const type = removeOptionalTypeMarker( + const type = getResolvedType(removeOptionalTypeMarker( isWriteAccess(location) && location.kind === SyntaxKind.PropertyAccessExpression ? checkPropertyAccessExpression(location as PropertyAccessExpression, /*checkMode*/ undefined, /*writeOnly*/ true) : getTypeOfExpression(location as Expression), - ); + )); if (getExportSymbolOfValueSymbolIfExported(getNodeLinks(location).resolvedSymbol) === symbol) { return type; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 28061c88e94bb..6b6d84c9c3595 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6280,27 +6280,28 @@ export const enum ObjectFlags { Tuple = 1 << 3, // Synthesized generic tuple type Anonymous = 1 << 4, // Anonymous Mapped = 1 << 5, // Mapped - Instantiated = 1 << 6, // Instantiated anonymous or mapped type - ObjectLiteral = 1 << 7, // Originates in an object literal - EvolvingArray = 1 << 8, // Evolving array type - ObjectLiteralPatternWithComputedProperties = 1 << 9, // Object literal pattern with computed properties - ReverseMapped = 1 << 10, // Object contains a property from a reverse-mapped type - JsxAttributes = 1 << 11, // Jsx attributes type - JSLiteral = 1 << 12, // Object type declared in JS - disables errors on read/write of nonexisting members - FreshLiteral = 1 << 13, // Fresh object literal - ArrayLiteral = 1 << 14, // Originates in an array literal + TypeAliasInstantiation = 1 << 6, // Deferred type alias instantiation + Instantiated = 1 << 7, // Instantiated anonymous or mapped type + ObjectLiteral = 1 << 8, // Originates in an object literal + EvolvingArray = 1 << 9, // Evolving array type + ObjectLiteralPatternWithComputedProperties = 1 << 10, // Object literal pattern with computed properties + ReverseMapped = 1 << 11, // Object contains a property from a reverse-mapped type + JsxAttributes = 1 << 12, // Jsx attributes type + JSLiteral = 1 << 13, // Object type declared in JS - disables errors on read/write of nonexisting members + FreshLiteral = 1 << 14, // Fresh object literal + ArrayLiteral = 1 << 15, // Originates in an array literal /** @internal */ - PrimitiveUnion = 1 << 15, // Union of only primitive types + PrimitiveUnion = 1 << 16, // Union of only primitive types /** @internal */ - ContainsWideningType = 1 << 16, // Type is or contains undefined or null widening type + ContainsWideningType = 1 << 17, // Type is or contains undefined or null widening type /** @internal */ - ContainsObjectOrArrayLiteral = 1 << 17, // Type is or contains object literal type + ContainsObjectOrArrayLiteral = 1 << 18, // Type is or contains object literal type /** @internal */ - NonInferrableType = 1 << 18, // Type is or contains anyFunctionType or silentNeverType + NonInferrableType = 1 << 19, // Type is or contains anyFunctionType or silentNeverType /** @internal */ - CouldContainTypeVariablesComputed = 1 << 19, // CouldContainTypeVariables flag has been computed + CouldContainTypeVariablesComputed = 1 << 20, // CouldContainTypeVariables flag has been computed /** @internal */ - CouldContainTypeVariables = 1 << 20, // Type could contain a type variable + CouldContainTypeVariables = 1 << 21, // Type could contain a type variable ClassOrInterface = Class | Interface, /** @internal */ @@ -6314,43 +6315,43 @@ export const enum ObjectFlags { ObjectTypeKindMask = ClassOrInterface | Reference | Tuple | Anonymous | Mapped | ReverseMapped | EvolvingArray, // Flags that require TypeFlags.Object - ContainsSpread = 1 << 21, // Object literal contains spread operation - ObjectRestType = 1 << 22, // Originates in object rest declaration - InstantiationExpressionType = 1 << 23, // Originates in instantiation expression + ContainsSpread = 1 << 22, // Object literal contains spread operation + ObjectRestType = 1 << 23, // Originates in object rest declaration + InstantiationExpressionType = 1 << 24, // Originates in instantiation expression /** @internal */ - IsClassInstanceClone = 1 << 24, // Type is a clone of a class instance type + IsClassInstanceClone = 1 << 25, // Type is a clone of a class instance type // Flags that require TypeFlags.Object and ObjectFlags.Reference /** @internal */ - IdenticalBaseTypeCalculated = 1 << 25, // has had `getSingleBaseForNonAugmentingSubtype` invoked on it already + IdenticalBaseTypeCalculated = 1 << 26, // has had `getSingleBaseForNonAugmentingSubtype` invoked on it already /** @internal */ - IdenticalBaseTypeExists = 1 << 26, // has a defined cachedEquivalentBaseType member + IdenticalBaseTypeExists = 1 << 27, // has a defined cachedEquivalentBaseType member // Flags that require TypeFlags.UnionOrIntersection or TypeFlags.Substitution /** @internal */ - IsGenericTypeComputed = 1 << 21, // IsGenericObjectType flag has been computed + IsGenericTypeComputed = 1 << 22, // IsGenericObjectType flag has been computed /** @internal */ - IsGenericObjectType = 1 << 22, // Union or intersection contains generic object type + IsGenericObjectType = 1 << 23, // Union or intersection contains generic object type /** @internal */ - IsGenericIndexType = 1 << 23, // Union or intersection contains generic index type + IsGenericIndexType = 1 << 24, // Union or intersection contains generic index type /** @internal */ IsGenericType = IsGenericObjectType | IsGenericIndexType, // Flags that require TypeFlags.Union /** @internal */ - ContainsIntersections = 1 << 24, // Union contains intersections + ContainsIntersections = 1 << 25, // Union contains intersections /** @internal */ - IsUnknownLikeUnionComputed = 1 << 25, // IsUnknownLikeUnion flag has been computed + IsUnknownLikeUnionComputed = 1 << 26, // IsUnknownLikeUnion flag has been computed /** @internal */ - IsUnknownLikeUnion = 1 << 26, // Union of null, undefined, and empty object type + IsUnknownLikeUnion = 1 << 27, // Union of null, undefined, and empty object type /** @internal */ // Flags that require TypeFlags.Intersection /** @internal */ - IsNeverIntersectionComputed = 1 << 24, // IsNeverLike flag has been computed + IsNeverIntersectionComputed = 1 << 25, // IsNeverLike flag has been computed /** @internal */ - IsNeverIntersection = 1 << 25, // Intersection reduces to never + IsNeverIntersection = 1 << 26, // Intersection reduces to never /** @internal */ - IsConstrainedTypeVariable = 1 << 26, // T & C, where T's constraint and C are primitives, object, or {} + IsConstrainedTypeVariable = 1 << 27, // T & C, where T's constraint and C are primitives, object, or {} } /** @internal */ @@ -6544,6 +6545,13 @@ export interface MappedType extends AnonymousType { containsError?: boolean; } +/** @internal */ +export interface TypeAliasInstantiationType extends AnonymousType { + typeAlias: Symbol; // Type alias symbol + node: NodeWithTypeArguments; // Type alias instantiation node + resolvedType?: ObjectType; // Resolved type alias instantiation +} + export interface EvolvingArrayType extends ObjectType { elementType: Type; // Element expressions of evolving array type finalArrayType?: Type; // Final array type of evolving array type From ad0a847fd339ea5ad71fb533dc6d6cc0ccd296b0 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 4 Feb 2024 13:45:30 -0800 Subject: [PATCH 2/3] Accept new baselines --- tests/baselines/reference/api/typescript.d.ts | 25 ++++++++++--------- ...lySimplifyingConditionalTypesNoCrash.types | 4 +-- .../reference/omitTypeTestErrors01.types | 2 +- .../baselines/reference/omitTypeTests01.types | 2 +- 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 100081fb8cccf..b383380e0aad4 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -7240,19 +7240,20 @@ declare namespace ts { Tuple = 8, Anonymous = 16, Mapped = 32, - Instantiated = 64, - ObjectLiteral = 128, - EvolvingArray = 256, - ObjectLiteralPatternWithComputedProperties = 512, - ReverseMapped = 1024, - JsxAttributes = 2048, - JSLiteral = 4096, - FreshLiteral = 8192, - ArrayLiteral = 16384, + TypeAliasInstantiation = 64, + Instantiated = 128, + ObjectLiteral = 256, + EvolvingArray = 512, + ObjectLiteralPatternWithComputedProperties = 1024, + ReverseMapped = 2048, + JsxAttributes = 4096, + JSLiteral = 8192, + FreshLiteral = 16384, + ArrayLiteral = 32768, ClassOrInterface = 3, - ContainsSpread = 2097152, - ObjectRestType = 4194304, - InstantiationExpressionType = 8388608, + ContainsSpread = 4194304, + ObjectRestType = 8388608, + InstantiationExpressionType = 16777216, } interface ObjectType extends Type { objectFlags: ObjectFlags; diff --git a/tests/baselines/reference/circularlySimplifyingConditionalTypesNoCrash.types b/tests/baselines/reference/circularlySimplifyingConditionalTypesNoCrash.types index 69f8bc39df037..95e5509ae2648 100644 --- a/tests/baselines/reference/circularlySimplifyingConditionalTypesNoCrash.types +++ b/tests/baselines/reference/circularlySimplifyingConditionalTypesNoCrash.types @@ -56,7 +56,7 @@ declare var connect: Connect; const myStoreConnect: Connect = function( >myStoreConnect : Connect ->function( mapStateToProps?: any, mapDispatchToProps?: any, mergeProps?: any, options: unknown = {},) { return connect( mapStateToProps, mapDispatchToProps, mergeProps, options, );} : (mapStateToProps?: any, mapDispatchToProps?: any, mergeProps?: any, options?: unknown) => InferableComponentEnhancerWithProps> & TOwnProps> +>function( mapStateToProps?: any, mapDispatchToProps?: any, mergeProps?: any, options: unknown = {},) { return connect( mapStateToProps, mapDispatchToProps, mergeProps, options, );} : (mapStateToProps?: any, mapDispatchToProps?: any, mergeProps?: any, options?: unknown) => InferableComponentEnhancerWithProps mapStateToProps?: any, >mapStateToProps : any @@ -73,7 +73,7 @@ const myStoreConnect: Connect = function( ) { return connect( ->connect( mapStateToProps, mapDispatchToProps, mergeProps, options, ) : InferableComponentEnhancerWithProps> & TOwnProps> +>connect( mapStateToProps, mapDispatchToProps, mergeProps, options, ) : InferableComponentEnhancerWithProps >connect : Connect mapStateToProps, diff --git a/tests/baselines/reference/omitTypeTestErrors01.types b/tests/baselines/reference/omitTypeTestErrors01.types index 1299a95ce8b80..8e4a224ef6e2f 100644 --- a/tests/baselines/reference/omitTypeTestErrors01.types +++ b/tests/baselines/reference/omitTypeTestErrors01.types @@ -13,7 +13,7 @@ interface Foo { } export type Bar = Omit; ->Bar : { a: string; b: number; } +>Bar : { b: number; a: string; } export type Baz = Omit; >Baz : { a: string; } diff --git a/tests/baselines/reference/omitTypeTests01.types b/tests/baselines/reference/omitTypeTests01.types index 5bd23bf64ffde..6843b5f529db7 100644 --- a/tests/baselines/reference/omitTypeTests01.types +++ b/tests/baselines/reference/omitTypeTests01.types @@ -13,7 +13,7 @@ interface Foo { } export type Bar = Omit; ->Bar : { a: string; b: number; } +>Bar : { b: number; a: string; } export type Baz = Omit; >Baz : { a: string; } From 81efc8630de5cd67ee03da6e964371b49e2063af Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 4 Feb 2024 14:21:47 -0800 Subject: [PATCH 3/3] Fix formatting --- src/compiler/checker.ts | 10 ++++++---- src/compiler/types.ts | 6 +++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f0f223ca8447c..350627ba7cf82 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16049,8 +16049,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // mapped type, and the instantiation occurs in a context where resolving type arguments might cause a // circular reference, we create a deferred type alias instantiation. This acts as a proxy for the // actual instantiation, which is obtained using getResolvedType. - if (getObjectFlags(type) & (ObjectFlags.Reference | ObjectFlags.Anonymous | ObjectFlags.Mapped) && node.kind === SyntaxKind.TypeReference && - isDeferredTypeReferenceNode(node as TypeReferenceNode, length(node.typeArguments) !== typeParameters.length)) { + if ( + getObjectFlags(type) & (ObjectFlags.Reference | ObjectFlags.Anonymous | ObjectFlags.Mapped) && node.kind === SyntaxKind.TypeReference && + isDeferredTypeReferenceNode(node as TypeReferenceNode, length(node.typeArguments) !== typeParameters.length) + ) { return createDeferredTypeAliasInstantiation(symbol, node, newAliasSymbol, aliasTypeArguments); } return getTypeAliasInstantiation(symbol, typeArgumentsFromTypeReferenceNode(node), newAliasSymbol, aliasTypeArguments); @@ -16723,8 +16725,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function isDeferredTypeReferenceNode(node: TypeReferenceNode | ArrayTypeNode | TupleTypeNode, hasDefaultTypeArguments?: boolean) { return isResolvedByTypeAlias(node) && ( node.kind === SyntaxKind.ArrayType ? mayResolveTypeAlias(node.elementType) : - node.kind === SyntaxKind.TupleType ? some(node.elements, mayResolveTypeAlias) : - hasDefaultTypeArguments || some(node.typeArguments, mayResolveTypeAlias) + node.kind === SyntaxKind.TupleType ? some(node.elements, mayResolveTypeAlias) : + hasDefaultTypeArguments || some(node.typeArguments, mayResolveTypeAlias) ); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 6b6d84c9c3595..fe2095e61eeec 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6547,9 +6547,9 @@ export interface MappedType extends AnonymousType { /** @internal */ export interface TypeAliasInstantiationType extends AnonymousType { - typeAlias: Symbol; // Type alias symbol - node: NodeWithTypeArguments; // Type alias instantiation node - resolvedType?: ObjectType; // Resolved type alias instantiation + typeAlias: Symbol; // Type alias symbol + node: NodeWithTypeArguments; // Type alias instantiation node + resolvedType?: ObjectType; // Resolved type alias instantiation } export interface EvolvingArrayType extends ObjectType {