diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a93ca5297bb55..d08091584cfd3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5009,7 +5009,20 @@ namespace ts { if (type.flags & TypeFlags.TypeParameter || objectFlags & ObjectFlags.ClassOrInterface) { if (type.flags & TypeFlags.TypeParameter && contains(context.inferTypeParameters, type)) { context.approximateLength += (symbolName(type.symbol).length + 6); - return factory.createInferTypeNode(typeParameterToDeclarationWithConstraint(type as TypeParameter, context, /*constraintNode*/ undefined)); + let constraintNode: TypeNode | undefined; + const constraint = getConstraintOfTypeParameter(type as TypeParameter); + if (constraint) { + // If the infer type has a constraint that is not the same as the constraint + // we would have normally inferred based on context, we emit the constraint + // using `infer T extends ?`. We omit inferred constraints from type references + // as they may be elided. + const inferredConstraint = getInferredTypeParameterConstraint(type as TypeParameter, /*omitTypeReferences*/ true); + if (!(inferredConstraint && isTypeIdenticalTo(constraint, inferredConstraint))) { + context.approximateLength += 9; + constraintNode = constraint && typeToTypeNodeHelper(constraint, context); + } + } + return factory.createInferTypeNode(typeParameterToDeclarationWithConstraint(type as TypeParameter, context, constraintNode)); } if (context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams && type.flags & TypeFlags.TypeParameter && @@ -13257,7 +13270,7 @@ namespace ts { return mapDefined(filter(type.symbol && type.symbol.declarations, isTypeParameterDeclaration), getEffectiveConstraintOfTypeParameter)[0]; } - function getInferredTypeParameterConstraint(typeParameter: TypeParameter) { + function getInferredTypeParameterConstraint(typeParameter: TypeParameter, omitTypeReferences?: boolean) { let inferences: Type[] | undefined; if (typeParameter.symbol?.declarations) { for (const declaration of typeParameter.symbol.declarations) { @@ -13267,7 +13280,7 @@ namespace ts { // 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) { + if (grandParent.kind === SyntaxKind.TypeReference && !omitTypeReferences) { const typeReference = grandParent as TypeReferenceNode; const typeParameters = getTypeParametersForTypeReference(typeReference); if (typeParameters) { @@ -35614,6 +35627,22 @@ namespace ts { grammarErrorOnNode(node, Diagnostics.infer_declarations_are_only_permitted_in_the_extends_clause_of_a_conditional_type); } checkSourceElement(node.typeParameter); + const symbol = getSymbolOfNode(node.typeParameter); + if (symbol.declarations && symbol.declarations.length > 1) { + const links = getSymbolLinks(symbol); + if (!links.typeParametersChecked) { + links.typeParametersChecked = true; + const typeParameter = getDeclaredTypeOfTypeParameter(symbol); + const declarations: TypeParameterDeclaration[] = getDeclarationsOfKind(symbol, SyntaxKind.TypeParameter); + if (!areTypeParametersIdentical(declarations, [typeParameter], decl => [decl])) { + // Report an error on every conflicting declaration. + const name = symbolToString(symbol); + for (const declaration of declarations) { + error(declaration.name, Diagnostics.All_declarations_of_0_must_have_identical_constraints, name); + } + } + } + } registerForUnusedIdentifiersCheck(node); } @@ -39124,7 +39153,7 @@ namespace ts { } const type = getDeclaredTypeOfSymbol(symbol) as InterfaceType; - if (!areTypeParametersIdentical(declarations, type.localTypeParameters!)) { + if (!areTypeParametersIdentical(declarations, type.localTypeParameters!, getEffectiveTypeParameterDeclarations)) { // Report an error on every conflicting declaration. const name = symbolToString(symbol); for (const declaration of declarations) { @@ -39134,13 +39163,13 @@ namespace ts { } } - function areTypeParametersIdentical(declarations: readonly (ClassDeclaration | InterfaceDeclaration)[], targetParameters: TypeParameter[]) { + function areTypeParametersIdentical(declarations: readonly T[], targetParameters: TypeParameter[], getTypeParameterDeclarations: (node: T) => readonly TypeParameterDeclaration[]) { const maxTypeArgumentCount = length(targetParameters); const minTypeArgumentCount = getMinTypeArgumentCount(targetParameters); for (const declaration of declarations) { // If this declaration has too few or too many type parameters, we report an error - const sourceParameters = getEffectiveTypeParameterDeclarations(declaration); + const sourceParameters = getTypeParameterDeclarations(declaration); const numTypeParameters = sourceParameters.length; if (numTypeParameters < minTypeArgumentCount || numTypeParameters > maxTypeArgumentCount) { return false; diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 9ef4e2c6c1646..3bc61d247ace5 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3433,6 +3433,10 @@ "category": "Error", "code": 2837 }, + "All declarations of '{0}' must have identical constraints.": { + "category": "Error", + "code": 2838 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index e06b0e6b8b1dc..a0fbb74b33be1 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -909,6 +909,9 @@ namespace ts { let currentParenthesizerRule: ((node: Node) => Node) | undefined; const { enter: enterComment, exit: exitComment } = performance.createTimerIf(extendedDiagnostics, "commentTime", "beforeComment", "afterComment"); const parenthesizer = factory.parenthesizer; + const typeArgumentParenthesizerRuleSelector: OrdinalParentheizerRuleSelector = { + select: index => index === 0 ? parenthesizer.parenthesizeLeadingTypeArgument : undefined + }; const emitBinaryExpression = createEmitBinaryExpression(); reset(); @@ -2241,7 +2244,7 @@ namespace ts { } function emitArrayType(node: ArrayTypeNode) { - emit(node.elementType, parenthesizer.parenthesizeElementTypeOfArrayType); + emit(node.elementType, parenthesizer.parenthesizeNonArrayTypeOfPostfixType); writePunctuation("["); writePunctuation("]"); } @@ -2254,7 +2257,7 @@ namespace ts { function emitTupleType(node: TupleTypeNode) { emitTokenWithComment(SyntaxKind.OpenBracketToken, node.pos, writePunctuation, node); const flags = getEmitFlags(node) & EmitFlags.SingleLine ? ListFormat.SingleLineTupleTypeElements : ListFormat.MultiLineTupleTypeElements; - emitList(node, node.elements, flags | ListFormat.NoSpaceIfEmpty); + emitList(node, node.elements, flags | ListFormat.NoSpaceIfEmpty, parenthesizer.parenthesizeElementTypeOfTupleType); emitTokenWithComment(SyntaxKind.CloseBracketToken, node.elements.end, writePunctuation, node); } @@ -2268,24 +2271,24 @@ namespace ts { } function emitOptionalType(node: OptionalTypeNode) { - emit(node.type, parenthesizer.parenthesizeElementTypeOfArrayType); + emit(node.type, parenthesizer.parenthesizeTypeOfOptionalType); writePunctuation("?"); } function emitUnionType(node: UnionTypeNode) { - emitList(node, node.types, ListFormat.UnionTypeConstituents, parenthesizer.parenthesizeMemberOfElementType); + emitList(node, node.types, ListFormat.UnionTypeConstituents, parenthesizer.parenthesizeConstituentTypeOfUnionType); } function emitIntersectionType(node: IntersectionTypeNode) { - emitList(node, node.types, ListFormat.IntersectionTypeConstituents, parenthesizer.parenthesizeMemberOfElementType); + emitList(node, node.types, ListFormat.IntersectionTypeConstituents, parenthesizer.parenthesizeConstituentTypeOfIntersectionType); } function emitConditionalType(node: ConditionalTypeNode) { - emit(node.checkType, parenthesizer.parenthesizeMemberOfConditionalType); + emit(node.checkType, parenthesizer.parenthesizeCheckTypeOfConditionalType); writeSpace(); writeKeyword("extends"); writeSpace(); - emit(node.extendsType, parenthesizer.parenthesizeMemberOfConditionalType); + emit(node.extendsType, parenthesizer.parenthesizeExtendsTypeOfConditionalType); writeSpace(); writePunctuation("?"); writeSpace(); @@ -2315,11 +2318,15 @@ namespace ts { function emitTypeOperator(node: TypeOperatorNode) { writeTokenText(node.operator, writeKeyword); writeSpace(); - emit(node.type, parenthesizer.parenthesizeMemberOfElementType); + + const parenthesizerRule = node.operator === SyntaxKind.ReadonlyKeyword ? + parenthesizer.parenthesizeOperandOfReadonlyTypeOperator : + parenthesizer.parenthesizeOperandOfTypeOperator; + emit(node.type, parenthesizerRule); } function emitIndexedAccessType(node: IndexedAccessTypeNode) { - emit(node.objectType, parenthesizer.parenthesizeMemberOfElementType); + emit(node.objectType, parenthesizer.parenthesizeNonArrayTypeOfPostfixType); writePunctuation("["); emit(node.indexType); writePunctuation("]"); @@ -4256,7 +4263,7 @@ namespace ts { } function emitTypeArguments(parentNode: Node, typeArguments: NodeArray | undefined) { - emitList(parentNode, typeArguments, ListFormat.TypeArguments, parenthesizer.parenthesizeMemberOfElementType); + emitList(parentNode, typeArguments, ListFormat.TypeArguments, typeArgumentParenthesizerRuleSelector); } function emitTypeParameters(parentNode: SignatureDeclaration | InterfaceDeclaration | TypeAliasDeclaration | ClassDeclaration | ClassExpression, typeParameters: NodeArray | undefined) { @@ -4324,15 +4331,15 @@ namespace ts { } } - function emitList(parentNode: Node | undefined, children: NodeArray | undefined, format: ListFormat, parenthesizerRule?: (node: Node) => Node, start?: number, count?: number) { + function emitList(parentNode: Node | undefined, children: NodeArray | undefined, format: ListFormat, parenthesizerRule?: ParenthesizerRuleOrSelector, start?: number, count?: number) { emitNodeList(emit, parentNode, children, format, parenthesizerRule, start, count); } - function emitExpressionList(parentNode: Node | undefined, children: NodeArray | undefined, format: ListFormat, parenthesizerRule?: (node: Expression) => Expression, start?: number, count?: number) { + function emitExpressionList(parentNode: Node | undefined, children: NodeArray | undefined, format: ListFormat, parenthesizerRule?: ParenthesizerRuleOrSelector, start?: number, count?: number) { emitNodeList(emitExpression, parentNode, children, format, parenthesizerRule, start, count); } - function emitNodeList(emit: (node: Node, parenthesizerRule?: ((node: Node) => Node) | undefined) => void, parentNode: Node | undefined, children: NodeArray | undefined, format: ListFormat, parenthesizerRule: ((node: Node) => Node) | undefined, start = 0, count = children ? children.length - start : 0) { + function emitNodeList(emit: (node: Node, parenthesizerRule?: ((node: Node) => Node) | undefined) => void, parentNode: Node | undefined, children: NodeArray | undefined, format: ListFormat, parenthesizerRule: ParenthesizerRuleOrSelector | undefined, start = 0, count = children ? children.length - start : 0) { const isUndefined = children === undefined; if (isUndefined && format & ListFormat.OptionalIfUndefined) { return; @@ -4388,6 +4395,8 @@ namespace ts { increaseIndent(); } + const emitListItem = getEmitListItem(emit, parenthesizerRule); + // Emit each child. let previousSibling: Node | undefined; let previousSourceFileTextKind: ReturnType; @@ -4443,12 +4452,7 @@ namespace ts { } nextListElementPos = child.pos; - if (emit.length === 1) { - emit(child); - } - else { - emit(child, parenthesizerRule); - } + emitListItem(child, emit, parenthesizerRule, i); if (shouldDecreaseIndentAfterEmit) { decreaseIndent(); @@ -5890,4 +5894,30 @@ namespace ts { CountMask = 0x0FFFFFFF, // Temp variable counter _i = 0x10000000, // Use/preference flag for '_i' } + + interface OrdinalParentheizerRuleSelector { + select(index: number): ((node: T) => T) | undefined; + } + + type ParenthesizerRule = (node: T) => T; + + type ParenthesizerRuleOrSelector = OrdinalParentheizerRuleSelector | ParenthesizerRule; + + function emitListItemNoParenthesizer(node: Node, emit: (node: Node, parenthesizerRule?: ((node: Node) => Node) | undefined) => void, _parenthesizerRule: ParenthesizerRuleOrSelector | undefined, _index: number) { + emit(node); + } + + function emitListItemWithParenthesizerRuleSelector(node: Node, emit: (node: Node, parenthesizerRule?: ((node: Node) => Node) | undefined) => void, parenthesizerRuleSelector: OrdinalParentheizerRuleSelector, index: number) { + emit(node, parenthesizerRuleSelector.select(index)); + } + + function emitListItemWithParenthesizerRule(node: Node, emit: (node: Node, parenthesizerRule?: ((node: Node) => Node) | undefined) => void, parenthesizerRule: ParenthesizerRule | undefined, _index: number) { + emit(node, parenthesizerRule); + } + + function getEmitListItem | undefined>(emit: (node: Node, parenthesizerRule?: ((node: Node) => Node) | undefined) => void, parenthesizerRule: R): (node: Node, emit: (node: Node, parenthesizerRule?: ((node: Node) => Node) | undefined) => void, parenthesizerRule: R, index: number) => void { + return emit.length === 1 ? emitListItemNoParenthesizer : + typeof parenthesizerRule === "object" ? emitListItemWithParenthesizerRuleSelector : + emitListItemWithParenthesizerRule; + } } diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index 412fc581085d7..cbeb8df5e5b25 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -34,6 +34,8 @@ namespace ts { const getJSDocPrimaryTypeCreateFunction = memoizeOne((kind: T["kind"]) => () => createJSDocPrimaryTypeWorker(kind)); const getJSDocUnaryTypeCreateFunction = memoizeOne((kind: T["kind"]) => (type: T["type"]) => createJSDocUnaryTypeWorker(kind, type)); const getJSDocUnaryTypeUpdateFunction = memoizeOne((kind: T["kind"]) => (node: T, type: T["type"]) => updateJSDocUnaryTypeWorker(kind, node, type)); + const getJSDocPrePostfixUnaryTypeCreateFunction = memoizeOne((kind: T["kind"]) => (type: T["type"], postfix?: boolean) => createJSDocPrePostfixUnaryTypeWorker(kind, type, postfix)); + const getJSDocPrePostfixUnaryTypeUpdateFunction = memoizeOne((kind: T["kind"]) => (node: T, type: T["type"]) => updateJSDocPrePostfixUnaryTypeWorker(kind, node, type)); const getJSDocSimpleTagCreateFunction = memoizeOne((kind: T["kind"]) => (tagName: Identifier | undefined, comment?: NodeArray) => createJSDocSimpleTagWorker(kind, tagName, comment)); const getJSDocSimpleTagUpdateFunction = memoizeOne((kind: T["kind"]) => (node: T, tagName: Identifier | undefined, comment?: NodeArray) => updateJSDocSimpleTagWorker(kind, node, tagName, comment)); const getJSDocTypeLikeTagCreateFunction = memoizeOne((kind: T["kind"]) => (tagName: Identifier | undefined, typeExpression?: JSDocTypeExpression, comment?: NodeArray) => createJSDocTypeLikeTagWorker(kind, tagName, typeExpression, comment)); @@ -42,6 +44,8 @@ namespace ts { const factory: NodeFactory = { get parenthesizer() { return parenthesizerRules(); }, get converters() { return converters(); }, + baseFactory, + flags, createNodeArray, createNumericLiteral, createBigIntLiteral, @@ -317,10 +321,10 @@ namespace ts { // lazily load factory members for JSDoc types with similar structure get createJSDocAllType() { return getJSDocPrimaryTypeCreateFunction(SyntaxKind.JSDocAllType); }, get createJSDocUnknownType() { return getJSDocPrimaryTypeCreateFunction(SyntaxKind.JSDocUnknownType); }, - get createJSDocNonNullableType() { return getJSDocUnaryTypeCreateFunction(SyntaxKind.JSDocNonNullableType); }, - get updateJSDocNonNullableType() { return getJSDocUnaryTypeUpdateFunction(SyntaxKind.JSDocNonNullableType); }, - get createJSDocNullableType() { return getJSDocUnaryTypeCreateFunction(SyntaxKind.JSDocNullableType); }, - get updateJSDocNullableType() { return getJSDocUnaryTypeUpdateFunction(SyntaxKind.JSDocNullableType); }, + get createJSDocNonNullableType() { return getJSDocPrePostfixUnaryTypeCreateFunction(SyntaxKind.JSDocNonNullableType); }, + get updateJSDocNonNullableType() { return getJSDocPrePostfixUnaryTypeUpdateFunction(SyntaxKind.JSDocNonNullableType); }, + get createJSDocNullableType() { return getJSDocPrePostfixUnaryTypeCreateFunction(SyntaxKind.JSDocNullableType); }, + get updateJSDocNullableType() { return getJSDocPrePostfixUnaryTypeUpdateFunction(SyntaxKind.JSDocNullableType); }, get createJSDocOptionalType() { return getJSDocUnaryTypeCreateFunction(SyntaxKind.JSDocOptionalType); }, get updateJSDocOptionalType() { return getJSDocUnaryTypeUpdateFunction(SyntaxKind.JSDocOptionalType); }, get createJSDocVariadicType() { return getJSDocUnaryTypeCreateFunction(SyntaxKind.JSDocVariadicType); }, @@ -1912,7 +1916,7 @@ namespace ts { // @api function createArrayTypeNode(elementType: TypeNode) { const node = createBaseNode(SyntaxKind.ArrayType); - node.elementType = parenthesizerRules().parenthesizeElementTypeOfArrayType(elementType); + node.elementType = parenthesizerRules().parenthesizeNonArrayTypeOfPostfixType(elementType); node.transformFlags = TransformFlags.ContainsTypeScript; return node; } @@ -1927,7 +1931,7 @@ namespace ts { // @api function createTupleTypeNode(elements: readonly (TypeNode | NamedTupleMember)[]) { const node = createBaseNode(SyntaxKind.TupleType); - node.elements = createNodeArray(elements); + node.elements = createNodeArray(parenthesizerRules().parenthesizeElementTypesOfTupleType(elements)); node.transformFlags = TransformFlags.ContainsTypeScript; return node; } @@ -1963,7 +1967,7 @@ namespace ts { // @api function createOptionalTypeNode(type: TypeNode) { const node = createBaseNode(SyntaxKind.OptionalType); - node.type = parenthesizerRules().parenthesizeElementTypeOfArrayType(type); + node.type = parenthesizerRules().parenthesizeTypeOfOptionalType(type); node.transformFlags = TransformFlags.ContainsTypeScript; return node; } @@ -1990,44 +1994,44 @@ namespace ts { : node; } - function createUnionOrIntersectionTypeNode(kind: SyntaxKind.UnionType | SyntaxKind.IntersectionType, types: readonly TypeNode[]) { + function createUnionOrIntersectionTypeNode(kind: SyntaxKind.UnionType | SyntaxKind.IntersectionType, types: readonly TypeNode[], parenthesize: (nodes: readonly TypeNode[]) => readonly TypeNode[]) { const node = createBaseNode(kind); - node.types = parenthesizerRules().parenthesizeConstituentTypesOfUnionOrIntersectionType(types); + node.types = factory.createNodeArray(parenthesize(types)); node.transformFlags = TransformFlags.ContainsTypeScript; return node; } - function updateUnionOrIntersectionTypeNode(node: T, types: NodeArray): T { + function updateUnionOrIntersectionTypeNode(node: T, types: NodeArray, parenthesize: (nodes: readonly TypeNode[]) => readonly TypeNode[]): T { return node.types !== types - ? update(createUnionOrIntersectionTypeNode(node.kind, types) as T, node) + ? update(createUnionOrIntersectionTypeNode(node.kind, types, parenthesize) as T, node) : node; } // @api function createUnionTypeNode(types: readonly TypeNode[]): UnionTypeNode { - return createUnionOrIntersectionTypeNode(SyntaxKind.UnionType, types) as UnionTypeNode; + return createUnionOrIntersectionTypeNode(SyntaxKind.UnionType, types, parenthesizerRules().parenthesizeConstituentTypesOfUnionType) as UnionTypeNode; } // @api function updateUnionTypeNode(node: UnionTypeNode, types: NodeArray) { - return updateUnionOrIntersectionTypeNode(node, types); + return updateUnionOrIntersectionTypeNode(node, types, parenthesizerRules().parenthesizeConstituentTypesOfUnionType); } // @api function createIntersectionTypeNode(types: readonly TypeNode[]): IntersectionTypeNode { - return createUnionOrIntersectionTypeNode(SyntaxKind.IntersectionType, types) as IntersectionTypeNode; + return createUnionOrIntersectionTypeNode(SyntaxKind.IntersectionType, types, parenthesizerRules().parenthesizeConstituentTypesOfIntersectionType) as IntersectionTypeNode; } // @api function updateIntersectionTypeNode(node: IntersectionTypeNode, types: NodeArray) { - return updateUnionOrIntersectionTypeNode(node, types); + return updateUnionOrIntersectionTypeNode(node, types, parenthesizerRules().parenthesizeConstituentTypesOfIntersectionType); } // @api function createConditionalTypeNode(checkType: TypeNode, extendsType: TypeNode, trueType: TypeNode, falseType: TypeNode) { const node = createBaseNode(SyntaxKind.ConditionalType); - node.checkType = parenthesizerRules().parenthesizeMemberOfConditionalType(checkType); - node.extendsType = parenthesizerRules().parenthesizeMemberOfConditionalType(extendsType); + node.checkType = parenthesizerRules().parenthesizeCheckTypeOfConditionalType(checkType); + node.extendsType = parenthesizerRules().parenthesizeExtendsTypeOfConditionalType(extendsType); node.trueType = trueType; node.falseType = falseType; node.transformFlags = TransformFlags.ContainsTypeScript; @@ -2154,7 +2158,9 @@ namespace ts { function createTypeOperatorNode(operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword, type: TypeNode): TypeOperatorNode { const node = createBaseNode(SyntaxKind.TypeOperator); node.operator = operator; - node.type = parenthesizerRules().parenthesizeMemberOfElementType(type); + node.type = operator === SyntaxKind.ReadonlyKeyword ? + parenthesizerRules().parenthesizeOperandOfReadonlyTypeOperator(type) : + parenthesizerRules().parenthesizeOperandOfTypeOperator(type); node.transformFlags = TransformFlags.ContainsTypeScript; return node; } @@ -2169,7 +2175,7 @@ namespace ts { // @api function createIndexedAccessTypeNode(objectType: TypeNode, indexType: TypeNode) { const node = createBaseNode(SyntaxKind.IndexedAccessType); - node.objectType = parenthesizerRules().parenthesizeMemberOfElementType(objectType); + node.objectType = parenthesizerRules().parenthesizeNonArrayTypeOfPostfixType(objectType); node.indexType = indexType; node.transformFlags = TransformFlags.ContainsTypeScript; return node; @@ -4360,12 +4366,21 @@ namespace ts { } // @api - // createJSDocNonNullableType // createJSDocNullableType + // createJSDocNonNullableType + function createJSDocPrePostfixUnaryTypeWorker(kind: T["kind"], type: T["type"], postfix = false): T { + const node = createJSDocUnaryTypeWorker( + kind, + postfix ? type && parenthesizerRules().parenthesizeNonArrayTypeOfPostfixType(type) : type + ) as Mutable; + node.postfix = postfix; + return node; + } + + // @api // createJSDocOptionalType // createJSDocVariadicType // createJSDocNamepathType - function createJSDocUnaryTypeWorker(kind: T["kind"], type: T["type"]): T { const node = createBaseNode(kind); node.type = type; @@ -4375,6 +4390,13 @@ namespace ts { // @api // updateJSDocNonNullableType // updateJSDocNullableType + function updateJSDocPrePostfixUnaryTypeWorker(kind: T["kind"], node: T, type: T["type"]): T { + return node.type !== type + ? update(createJSDocPrePostfixUnaryTypeWorker(kind, type, node.postfix), node) + : node; + } + + // @api // updateJSDocOptionalType // updateJSDocVariadicType // updateJSDocNamepathType diff --git a/src/compiler/factory/parenthesizerRules.ts b/src/compiler/factory/parenthesizerRules.ts index 1e2cb936582fa..c824c13fd1629 100644 --- a/src/compiler/factory/parenthesizerRules.ts +++ b/src/compiler/factory/parenthesizerRules.ts @@ -25,11 +25,20 @@ namespace ts { parenthesizeExpressionForDisallowedComma, parenthesizeExpressionOfExpressionStatement, parenthesizeConciseBodyOfArrowFunction, - parenthesizeMemberOfConditionalType, - parenthesizeMemberOfElementType, - parenthesizeElementTypeOfArrayType, - parenthesizeConstituentTypesOfUnionOrIntersectionType, + parenthesizeCheckTypeOfConditionalType, + parenthesizeExtendsTypeOfConditionalType, + parenthesizeConstituentTypesOfUnionType, + parenthesizeConstituentTypeOfUnionType, + parenthesizeConstituentTypesOfIntersectionType, + parenthesizeConstituentTypeOfIntersectionType, + parenthesizeOperandOfTypeOperator, + parenthesizeOperandOfReadonlyTypeOperator, + parenthesizeNonArrayTypeOfPostfixType, + parenthesizeElementTypesOfTupleType, + parenthesizeElementTypeOfTupleType, + parenthesizeTypeOfOptionalType, parenthesizeTypeArguments, + parenthesizeLeadingTypeArgument, }; function getParenthesizeLeftSideOfBinaryForOperator(operatorKind: BinaryOperator) { @@ -388,38 +397,199 @@ namespace ts { return body; } - function parenthesizeMemberOfConditionalType(member: TypeNode): TypeNode { - return member.kind === SyntaxKind.ConditionalType ? factory.createParenthesizedType(member) : member; + // Type[Extends] : + // FunctionOrConstructorType + // ConditionalType[?Extends] + + // ConditionalType[Extends] : + // UnionType[?Extends] + // [~Extends] UnionType[~Extends] `extends` Type[+Extends] `?` Type[~Extends] `:` Type[~Extends] + // + // - The check type (the `UnionType`, above) does not allow function, constructor, or conditional types (they must be parenthesized) + // - The extends type (the first `Type`, above) does not allow conditional types (they must be parenthesized). Function and constructor types are fine. + // - The true and false branch types (the second and third `Type` non-terminals, above) allow any type + function parenthesizeCheckTypeOfConditionalType(checkType: TypeNode): TypeNode { + switch (checkType.kind) { + case SyntaxKind.FunctionType: + case SyntaxKind.ConstructorType: + case SyntaxKind.ConditionalType: + return factory.createParenthesizedType(checkType); + } + return checkType; + } + + function parenthesizeExtendsTypeOfConditionalType(extendsType: TypeNode): TypeNode { + switch (extendsType.kind) { + case SyntaxKind.ConditionalType: + return factory.createParenthesizedType(extendsType); + } + return extendsType; } - function parenthesizeMemberOfElementType(member: TypeNode): TypeNode { - switch (member.kind) { + // UnionType[Extends] : + // `|`? IntersectionType[?Extends] + // UnionType[?Extends] `|` IntersectionType[?Extends] + // + // - A union type constituent has the same precedence as the check type of a conditional type + function parenthesizeConstituentTypeOfUnionType(type: TypeNode) { + switch (type.kind) { + case SyntaxKind.UnionType: // Not strictly necessary, but a union containing a union should have been flattened + case SyntaxKind.IntersectionType: // Not strictly necessary, but makes generated output more readable and avoids breaks in DT tests + return factory.createParenthesizedType(type); + } + return parenthesizeCheckTypeOfConditionalType(type); + } + + function parenthesizeConstituentTypesOfUnionType(members: readonly TypeNode[]): NodeArray { + return factory.createNodeArray(sameMap(members, parenthesizeConstituentTypeOfUnionType)); + } + + // IntersectionType[Extends] : + // `&`? TypeOperator[?Extends] + // IntersectionType[?Extends] `&` TypeOperator[?Extends] + // + // - An intersection type constituent does not allow function, constructor, conditional, or union types (they must be parenthesized) + function parenthesizeConstituentTypeOfIntersectionType(type: TypeNode) { + switch (type.kind) { case SyntaxKind.UnionType: + case SyntaxKind.IntersectionType: // Not strictly necessary, but an intersection containing an intersection should have been flattened + return factory.createParenthesizedType(type); + } + return parenthesizeConstituentTypeOfUnionType(type); + } + + function parenthesizeConstituentTypesOfIntersectionType(members: readonly TypeNode[]): NodeArray { + return factory.createNodeArray(sameMap(members, parenthesizeConstituentTypeOfIntersectionType)); + } + + // TypeOperator[Extends] : + // PostfixType + // InferType[?Extends] + // `keyof` TypeOperator[?Extends] + // `unique` TypeOperator[?Extends] + // `readonly` TypeOperator[?Extends] + // + function parenthesizeOperandOfTypeOperator(type: TypeNode) { + switch (type.kind) { case SyntaxKind.IntersectionType: - case SyntaxKind.FunctionType: - case SyntaxKind.ConstructorType: - return factory.createParenthesizedType(member); + return factory.createParenthesizedType(type); } - return parenthesizeMemberOfConditionalType(member); + return parenthesizeConstituentTypeOfIntersectionType(type); } - function parenthesizeElementTypeOfArrayType(member: TypeNode): TypeNode { - switch (member.kind) { - case SyntaxKind.TypeQuery: + function parenthesizeOperandOfReadonlyTypeOperator(type: TypeNode) { + switch (type.kind) { case SyntaxKind.TypeOperator: + return factory.createParenthesizedType(type); + } + return parenthesizeOperandOfTypeOperator(type); + } + + // PostfixType : + // NonArrayType + // NonArrayType [no LineTerminator here] `!` // JSDoc + // NonArrayType [no LineTerminator here] `?` // JSDoc + // IndexedAccessType + // ArrayType + // + // IndexedAccessType : + // NonArrayType `[` Type[~Extends] `]` + // + // ArrayType : + // NonArrayType `[` `]` + // + function parenthesizeNonArrayTypeOfPostfixType(type: TypeNode) { + switch (type.kind) { case SyntaxKind.InferType: - return factory.createParenthesizedType(member); + case SyntaxKind.TypeOperator: + case SyntaxKind.TypeQuery: // Not strictly necessary, but makes generated output more readable and avoids breaks in DT tests + return factory.createParenthesizedType(type); } - return parenthesizeMemberOfElementType(member); + return parenthesizeOperandOfTypeOperator(type); } - function parenthesizeConstituentTypesOfUnionOrIntersectionType(members: readonly TypeNode[]): NodeArray { - return factory.createNodeArray(sameMap(members, parenthesizeMemberOfElementType)); + // TupleType : + // `[` Elision? `]` + // `[` NamedTupleElementTypes `]` + // `[` NamedTupleElementTypes `,` Elision? `]` + // `[` TupleElementTypes `]` + // `[` TupleElementTypes `,` Elision? `]` + // + // NamedTupleElementTypes : + // Elision? NamedTupleMember + // NamedTupleElementTypes `,` Elision? NamedTupleMember + // + // NamedTupleMember : + // Identifier `?`? `:` Type[~Extends] + // `...` Identifier `:` Type[~Extends] + // + // TupleElementTypes : + // Elision? TupleElementType + // TupleElementTypes `,` Elision? TupleElementType + // + // TupleElementType : + // Type[~Extends] // NOTE: Needs cover grammar to disallow JSDoc postfix-optional + // OptionalType + // RestType + // + // OptionalType : + // Type[~Extends] `?` // NOTE: Needs cover grammar to disallow JSDoc postfix-optional + // + // RestType : + // `...` Type[~Extends] + // + function parenthesizeElementTypesOfTupleType(types: readonly (TypeNode | NamedTupleMember)[]): NodeArray { + return factory.createNodeArray(sameMap(types, parenthesizeElementTypeOfTupleType)); + } + + function parenthesizeElementTypeOfTupleType(type: TypeNode | NamedTupleMember): TypeNode { + if (hasJSDocPostfixQuestion(type)) return factory.createParenthesizedType(type); + return type; + } + + function hasJSDocPostfixQuestion(type: TypeNode | NamedTupleMember): boolean { + if (isJSDocNullableType(type)) return type.postfix; + if (isNamedTupleMember(type)) return hasJSDocPostfixQuestion(type.type); + if (isFunctionTypeNode(type) || isConstructorTypeNode(type) || isTypeOperatorNode(type)) return hasJSDocPostfixQuestion(type.type); + if (isConditionalTypeNode(type)) return hasJSDocPostfixQuestion(type.falseType); + if (isUnionTypeNode(type)) return hasJSDocPostfixQuestion(last(type.types)); + if (isIntersectionTypeNode(type)) return hasJSDocPostfixQuestion(last(type.types)); + if (isInferTypeNode(type)) return !!type.typeParameter.constraint && hasJSDocPostfixQuestion(type.typeParameter.constraint); + return false; + } + + function parenthesizeTypeOfOptionalType(type: TypeNode): TypeNode { + if (hasJSDocPostfixQuestion(type)) return factory.createParenthesizedType(type); + return parenthesizeNonArrayTypeOfPostfixType(type); + } + // function parenthesizeMemberOfElementType(member: TypeNode): TypeNode { + // switch (member.kind) { + // case SyntaxKind.UnionType: + // case SyntaxKind.IntersectionType: + // case SyntaxKind.FunctionType: + // case SyntaxKind.ConstructorType: + // return factory.createParenthesizedType(member); + // } + // return parenthesizeMemberOfConditionalType(member); + // } + + // function parenthesizeElementTypeOfArrayType(member: TypeNode): TypeNode { + // switch (member.kind) { + // case SyntaxKind.TypeQuery: + // case SyntaxKind.TypeOperator: + // case SyntaxKind.InferType: + // return factory.createParenthesizedType(member); + // } + // return parenthesizeMemberOfElementType(member); + // } + + function parenthesizeLeadingTypeArgument(node: TypeNode) { + return isFunctionOrConstructorTypeNode(node) && node.typeParameters ? factory.createParenthesizedType(node) : node; } function parenthesizeOrdinalTypeArgument(node: TypeNode, i: number) { - return i === 0 && isFunctionOrConstructorTypeNode(node) && node.typeParameters ? factory.createParenthesizedType(node) : node; + return i === 0 ? parenthesizeLeadingTypeArgument(node) : node; } function parenthesizeTypeArguments(typeArguments: NodeArray | undefined): NodeArray | undefined { @@ -446,10 +616,19 @@ namespace ts { parenthesizeExpressionForDisallowedComma: identity, parenthesizeExpressionOfExpressionStatement: identity, parenthesizeConciseBodyOfArrowFunction: identity, - parenthesizeMemberOfConditionalType: identity, - parenthesizeMemberOfElementType: identity, - parenthesizeElementTypeOfArrayType: identity, - parenthesizeConstituentTypesOfUnionOrIntersectionType: nodes => cast(nodes, isNodeArray), + parenthesizeCheckTypeOfConditionalType: identity, + parenthesizeExtendsTypeOfConditionalType: identity, + parenthesizeConstituentTypesOfUnionType: nodes => cast(nodes, isNodeArray), + parenthesizeConstituentTypeOfUnionType: identity, + parenthesizeConstituentTypesOfIntersectionType: nodes => cast(nodes, isNodeArray), + parenthesizeConstituentTypeOfIntersectionType: identity, + parenthesizeOperandOfTypeOperator: identity, + parenthesizeOperandOfReadonlyTypeOperator: identity, + parenthesizeNonArrayTypeOfPostfixType: identity, + parenthesizeElementTypesOfTupleType: nodes => cast(nodes, isNodeArray), + parenthesizeElementTypeOfTupleType: identity, + parenthesizeTypeOfOptionalType: identity, parenthesizeTypeArguments: nodes => nodes && cast(nodes, isNodeArray), + parenthesizeLeadingTypeArgument: identity, }; } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 059083df5ca18..7642612e27a44 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1391,6 +1391,14 @@ namespace ts { return doInsideOfContext(NodeFlags.DisallowInContext, func); } + function allowConditionalTypesAnd(func: () => T): T { + return doOutsideOfContext(NodeFlags.DisallowConditionalTypesContext, func); + } + + function disallowConditionalTypesAnd(func: () => T): T { + return doInsideOfContext(NodeFlags.DisallowConditionalTypesContext, func); + } + function doInYieldContext(func: () => T): T { return doInsideOfContext(NodeFlags.YieldContext, func); } @@ -1427,6 +1435,10 @@ namespace ts { return inContext(NodeFlags.DisallowInContext); } + function inDisallowConditionalTypesContext() { + return inContext(NodeFlags.DisallowConditionalTypesContext); + } + function inDecoratorContext() { return inContext(NodeFlags.DecoratorContext); } @@ -3073,7 +3085,7 @@ namespace ts { function parseJSDocNonNullableType(): TypeNode { const pos = getNodePos(); nextToken(); - return finishNode(factory.createJSDocNonNullableType(parseNonArrayType()), pos); + return finishNode(factory.createJSDocNonNullableType(parseNonArrayType(), /*postfix*/ false), pos); } function parseJSDocUnknownOrNullableType(): JSDocUnknownType | JSDocNullableType { @@ -3100,7 +3112,7 @@ namespace ts { return finishNode(factory.createJSDocUnknownType(), pos); } else { - return finishNode(factory.createJSDocNullableType(parseType()), pos); + return finishNode(factory.createJSDocNullableType(parseType(), /*postfix*/ false), pos); } } @@ -3323,7 +3335,7 @@ namespace ts { function parseReturnType(returnToken: SyntaxKind.ColonToken | SyntaxKind.EqualsGreaterThanToken, isType: boolean): TypeNode | undefined; function parseReturnType(returnToken: SyntaxKind.ColonToken | SyntaxKind.EqualsGreaterThanToken, isType: boolean) { if (shouldParseReturnType(returnToken, isType)) { - return parseTypeOrTypePredicate(); + return allowConditionalTypesAnd(parseTypeOrTypePredicate); } } @@ -3948,7 +3960,7 @@ namespace ts { switch (token()) { case SyntaxKind.ExclamationToken: nextToken(); - type = finishNode(factory.createJSDocNonNullableType(type), pos); + type = finishNode(factory.createJSDocNonNullableType(type, /*postfix*/ true), pos); break; case SyntaxKind.QuestionToken: // If next token is start of a type we have a conditional type @@ -3956,7 +3968,7 @@ namespace ts { return type; } nextToken(); - type = finishNode(factory.createJSDocNullableType(type), pos); + type = finishNode(factory.createJSDocNullableType(type, /*postfix*/ true), pos); break; case SyntaxKind.OpenBracketToken: parseExpected(SyntaxKind.OpenBracketToken); @@ -3983,17 +3995,21 @@ namespace ts { return finishNode(factory.createTypeOperatorNode(operator, parseTypeOperatorOrHigher()), pos); } - function parseTypeParameterOfInferType() { + function tryParseConstraintOfInferType() { + if (parseOptional(SyntaxKind.ExtendsKeyword)) { + const constraint = disallowConditionalTypesAnd(parseType); + if (inDisallowConditionalTypesContext() || token() !== SyntaxKind.QuestionToken) { + return constraint; + } + } + } + + function parseTypeParameterOfInferType(): TypeParameterDeclaration { const pos = getNodePos(); - return finishNode( - factory.createTypeParameterDeclaration( - /*modifiers*/ undefined, - parseIdentifier(), - /*constraint*/ undefined, - /*defaultType*/ undefined - ), - pos - ); + const name = parseIdentifier(); + const constraint = tryParse(tryParseConstraintOfInferType); + const node = factory.createTypeParameterDeclaration(/*modifiers*/ undefined, name, constraint); + return finishNode(node, pos); } function parseInferType(): InferTypeNode { @@ -4012,7 +4028,7 @@ namespace ts { case SyntaxKind.InferKeyword: return parseInferType(); } - return parsePostfixTypeOrHigher(); + return allowConditionalTypesAnd(parsePostfixTypeOrHigher); } function parseFunctionOrConstructorTypeToError( @@ -4161,24 +4177,22 @@ namespace ts { } function parseType(): TypeNode { - // The rules about 'yield' only apply to actual code/expression contexts. They don't - // apply to 'type' contexts. So we disable these parameters here before moving on. - return doOutsideOfContext(NodeFlags.TypeExcludesFlags, parseTypeWorker); - } + if (contextFlags & NodeFlags.TypeExcludesFlags) { + return doOutsideOfContext(NodeFlags.TypeExcludesFlags, parseType); + } - function parseTypeWorker(noConditionalTypes?: boolean): TypeNode { if (isStartOfFunctionTypeOrConstructorType()) { return parseFunctionOrConstructorType(); } const pos = getNodePos(); const type = parseUnionTypeOrHigher(); - if (!noConditionalTypes && !scanner.hasPrecedingLineBreak() && parseOptional(SyntaxKind.ExtendsKeyword)) { + if (!inDisallowConditionalTypesContext() && !scanner.hasPrecedingLineBreak() && parseOptional(SyntaxKind.ExtendsKeyword)) { // The type following 'extends' is not permitted to be another conditional type - const extendsType = parseTypeWorker(/*noConditionalTypes*/ true); + const extendsType = disallowConditionalTypesAnd(parseType); parseExpected(SyntaxKind.QuestionToken); - const trueType = parseTypeWorker(); + const trueType = allowConditionalTypesAnd(parseType); parseExpected(SyntaxKind.ColonToken); - const falseType = parseTypeWorker(); + const falseType = allowConditionalTypesAnd(parseType); return finishNode(factory.createConditionalTypeNode(type, extendsType, trueType, falseType), pos); } return type; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 396805f71a6e8..cbaf27144f137 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -763,10 +763,11 @@ namespace ts { YieldContext = 1 << 13, // If node was parsed in the 'yield' context created when parsing a generator DecoratorContext = 1 << 14, // If node was parsed as part of a decorator AwaitContext = 1 << 15, // If node was parsed in the 'await' context created when parsing an async function - ThisNodeHasError = 1 << 16, // If the parser encountered an error when parsing the code that created this node - JavaScriptFile = 1 << 17, // If node was parsed in a JavaScript - ThisNodeOrAnySubNodesHasError = 1 << 18, // If this node or any of its children had an error - HasAggregatedChildData = 1 << 19, // If we've computed data from children and cached it in this node + DisallowConditionalTypesContext = 1 << 16, // If node was parsed in a context where conditional types are not allowed + ThisNodeHasError = 1 << 17, // If the parser encountered an error when parsing the code that created this node + JavaScriptFile = 1 << 18, // If node was parsed in a JavaScript + ThisNodeOrAnySubNodesHasError = 1 << 19, // If this node or any of its children had an error + HasAggregatedChildData = 1 << 20, // If we've computed data from children and cached it in this node // These flags will be set when the parser encounters a dynamic import expression or 'import.meta' to avoid // walking the tree if the flags are not set. However, these flags are just a approximation @@ -777,15 +778,15 @@ namespace ts { // removal, it is likely that users will add the import anyway. // The advantage of this approach is its simplicity. For the case of batch compilation, // we guarantee that users won't have to pay the price of walking the tree if a dynamic import isn't used. - /* @internal */ PossiblyContainsDynamicImport = 1 << 20, - /* @internal */ PossiblyContainsImportMeta = 1 << 21, + /* @internal */ PossiblyContainsDynamicImport = 1 << 21, + /* @internal */ PossiblyContainsImportMeta = 1 << 22, - JSDoc = 1 << 22, // If node was parsed inside jsdoc - /* @internal */ Ambient = 1 << 23, // If node was inside an ambient context -- a declaration file, or inside something with the `declare` modifier. - /* @internal */ InWithStatement = 1 << 24, // If any ancestor of node was the `statement` of a WithStatement (not the `expression`) - JsonFile = 1 << 25, // If node was parsed in a Json - /* @internal */ TypeCached = 1 << 26, // If a type was cached for node at any point - /* @internal */ Deprecated = 1 << 27, // If has '@deprecated' JSDoc tag + JSDoc = 1 << 23, // If node was parsed inside jsdoc + /* @internal */ Ambient = 1 << 24, // If node was inside an ambient context -- a declaration file, or inside something with the `declare` modifier. + /* @internal */ InWithStatement = 1 << 25, // If any ancestor of node was the `statement` of a WithStatement (not the `expression`) + JsonFile = 1 << 26, // If node was parsed in a Json + /* @internal */ TypeCached = 1 << 27, // If a type was cached for node at any point + /* @internal */ Deprecated = 1 << 28, // If has '@deprecated' JSDoc tag BlockScoped = Let | Const, @@ -793,7 +794,7 @@ namespace ts { ReachabilityAndEmitFlags = ReachabilityCheckFlags | HasAsyncFunctions, // Parsing context flags - ContextFlags = DisallowInContext | YieldContext | DecoratorContext | AwaitContext | JavaScriptFile | InWithStatement | Ambient, + ContextFlags = DisallowInContext | DisallowConditionalTypesContext | YieldContext | DecoratorContext | AwaitContext | JavaScriptFile | InWithStatement | Ambient, // Exclude these flags when parsing a Type TypeExcludesFlags = YieldContext | AwaitContext, @@ -3234,11 +3235,13 @@ namespace ts { export interface JSDocNonNullableType extends JSDocType { readonly kind: SyntaxKind.JSDocNonNullableType; readonly type: TypeNode; + readonly postfix: boolean; } export interface JSDocNullableType extends JSDocType { readonly kind: SyntaxKind.JSDocNullableType; readonly type: TypeNode; + readonly postfix: boolean; } export interface JSDocOptionalType extends JSDocType { @@ -7129,10 +7132,19 @@ namespace ts { parenthesizeExpressionOfExpressionStatement(expression: Expression): Expression; parenthesizeConciseBodyOfArrowFunction(body: Expression): Expression; parenthesizeConciseBodyOfArrowFunction(body: ConciseBody): ConciseBody; - parenthesizeMemberOfConditionalType(member: TypeNode): TypeNode; - parenthesizeMemberOfElementType(member: TypeNode): TypeNode; - parenthesizeElementTypeOfArrayType(member: TypeNode): TypeNode; - parenthesizeConstituentTypesOfUnionOrIntersectionType(members: readonly TypeNode[]): NodeArray; + parenthesizeCheckTypeOfConditionalType(type: TypeNode): TypeNode; + parenthesizeExtendsTypeOfConditionalType(type: TypeNode): TypeNode; + parenthesizeOperandOfTypeOperator(type: TypeNode): TypeNode; + parenthesizeOperandOfReadonlyTypeOperator(type: TypeNode): TypeNode; + parenthesizeNonArrayTypeOfPostfixType(type: TypeNode): TypeNode; + parenthesizeElementTypesOfTupleType(types: readonly (TypeNode | NamedTupleMember)[]): NodeArray; + parenthesizeElementTypeOfTupleType(type: TypeNode | NamedTupleMember): TypeNode | NamedTupleMember; + parenthesizeTypeOfOptionalType(type: TypeNode): TypeNode; + parenthesizeConstituentTypeOfUnionType(type: TypeNode): TypeNode; + parenthesizeConstituentTypesOfUnionType(constituents: readonly TypeNode[]): NodeArray; + parenthesizeConstituentTypeOfIntersectionType(type: TypeNode): TypeNode; + parenthesizeConstituentTypesOfIntersectionType(constituents: readonly TypeNode[]): NodeArray; + parenthesizeLeadingTypeArgument(typeNode: TypeNode): TypeNode; parenthesizeTypeArguments(typeParameters: readonly TypeNode[] | undefined): NodeArray | undefined; } @@ -7151,6 +7163,8 @@ namespace ts { export interface NodeFactory { /* @internal */ readonly parenthesizer: ParenthesizerRules; /* @internal */ readonly converters: NodeConverters; + /* @internal */ readonly baseFactory: BaseNodeFactory; + /* @internal */ readonly flags: NodeFactoryFlags; createNodeArray(elements?: readonly T[], hasTrailingComma?: boolean): NodeArray; // @@ -7549,9 +7563,9 @@ namespace ts { createJSDocAllType(): JSDocAllType; createJSDocUnknownType(): JSDocUnknownType; - createJSDocNonNullableType(type: TypeNode): JSDocNonNullableType; + createJSDocNonNullableType(type: TypeNode, postfix?: boolean): JSDocNonNullableType; updateJSDocNonNullableType(node: JSDocNonNullableType, type: TypeNode): JSDocNonNullableType; - createJSDocNullableType(type: TypeNode): JSDocNullableType; + createJSDocNullableType(type: TypeNode, postfix?: boolean): JSDocNullableType; updateJSDocNullableType(node: JSDocNullableType, type: TypeNode): JSDocNullableType; createJSDocOptionalType(type: TypeNode): JSDocOptionalType; updateJSDocOptionalType(node: JSDocOptionalType, type: TypeNode): JSDocOptionalType; diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index be02cb0a4f26f..9765e8a4fdaba 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -1119,8 +1119,17 @@ namespace ts.textChanges { return skipTrivia(s, 0) === s.length; } + // A transformation context that won't perform parenthesization, as some parenthesization rules + // are more aggressive than is strictly necessary. + const textChangesTransformationContext: TransformationContext = { + ...nullTransformationContext, + factory: createNodeFactory( + nullTransformationContext.factory.flags | NodeFactoryFlags.NoParenthesizerRules, + nullTransformationContext.factory.baseFactory), + }; + export function assignPositionsToNode(node: Node): Node { - const visited = visitEachChild(node, assignPositionsToNode, nullTransformationContext, assignPositionsToNodeArray, assignPositionsToNode); + const visited = visitEachChild(node, assignPositionsToNode, textChangesTransformationContext, assignPositionsToNodeArray, assignPositionsToNode); // create proxy node for non synthesized nodes const newNode = nodeIsSynthesized(visited) ? visited : Object.create(visited) as Node; setTextRangePosEnd(newNode, getPos(node), getEnd(node)); diff --git a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.nonNullableType.json b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.nonNullableType.json index 6753df2cc0668..9f30835104500 100644 --- a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.nonNullableType.json +++ b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.nonNullableType.json @@ -12,5 +12,6 @@ "flags": "JSDoc", "modifierFlagsCache": 0, "transformFlags": 1 - } + }, + "postfix": false } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.nonNullableType2.json b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.nonNullableType2.json index e579a0db6e3cd..0b4718e71ac93 100644 --- a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.nonNullableType2.json +++ b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.nonNullableType2.json @@ -12,5 +12,6 @@ "flags": "JSDoc", "modifierFlagsCache": 0, "transformFlags": 1 - } + }, + "postfix": true } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.nullableType.json b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.nullableType.json index bdc4edb280e66..17b7fa941ccf9 100644 --- a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.nullableType.json +++ b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.nullableType.json @@ -12,5 +12,6 @@ "flags": "JSDoc", "modifierFlagsCache": 0, "transformFlags": 1 - } + }, + "postfix": false } \ No newline at end of file diff --git a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.nullableType2.json b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.nullableType2.json index 44f20173cd3d7..76313b04a415c 100644 --- a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.nullableType2.json +++ b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.nullableType2.json @@ -12,5 +12,6 @@ "flags": "JSDoc", "modifierFlagsCache": 0, "transformFlags": 1 - } + }, + "postfix": true } \ No newline at end of file diff --git a/tests/baselines/reference/ambientConstLiterals.types b/tests/baselines/reference/ambientConstLiterals.types index 664e730dd63e6..2f7e3b678b0a1 100644 --- a/tests/baselines/reference/ambientConstLiterals.types +++ b/tests/baselines/reference/ambientConstLiterals.types @@ -12,7 +12,7 @@ enum E { A, B, C, "non identifier" } >A : E.A >B : E.B >C : E.C ->"non identifier" : typeof E["non identifier"] +>"non identifier" : (typeof E)["non identifier"] const c1 = "abc"; >c1 : "abc" @@ -54,8 +54,8 @@ const c8 = E.A; >A : E.A const c8b = E["non identifier"]; ->c8b : typeof E["non identifier"] ->E["non identifier"] : typeof E["non identifier"] +>c8b : (typeof E)["non identifier"] +>E["non identifier"] : (typeof E)["non identifier"] >E : typeof E >"non identifier" : "non identifier" diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index ce6ba3b45ccda..85204ebc69eab 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -523,16 +523,17 @@ declare namespace ts { YieldContext = 8192, DecoratorContext = 16384, AwaitContext = 32768, - ThisNodeHasError = 65536, - JavaScriptFile = 131072, - ThisNodeOrAnySubNodesHasError = 262144, - HasAggregatedChildData = 524288, - JSDoc = 4194304, - JsonFile = 33554432, + DisallowConditionalTypesContext = 65536, + ThisNodeHasError = 131072, + JavaScriptFile = 262144, + ThisNodeOrAnySubNodesHasError = 524288, + HasAggregatedChildData = 1048576, + JSDoc = 8388608, + JsonFile = 67108864, BlockScoped = 3, ReachabilityCheckFlags = 768, ReachabilityAndEmitFlags = 2816, - ContextFlags = 25358336, + ContextFlags = 50720768, TypeExcludesFlags = 40960, } export enum ModifierFlags { @@ -1805,10 +1806,12 @@ declare namespace ts { export interface JSDocNonNullableType extends JSDocType { readonly kind: SyntaxKind.JSDocNonNullableType; readonly type: TypeNode; + readonly postfix: boolean; } export interface JSDocNullableType extends JSDocType { readonly kind: SyntaxKind.JSDocNullableType; readonly type: TypeNode; + readonly postfix: boolean; } export interface JSDocOptionalType extends JSDocType { readonly kind: SyntaxKind.JSDocOptionalType; @@ -3654,9 +3657,9 @@ declare namespace ts { updateExternalModuleReference(node: ExternalModuleReference, expression: Expression): ExternalModuleReference; createJSDocAllType(): JSDocAllType; createJSDocUnknownType(): JSDocUnknownType; - createJSDocNonNullableType(type: TypeNode): JSDocNonNullableType; + createJSDocNonNullableType(type: TypeNode, postfix?: boolean): JSDocNonNullableType; updateJSDocNonNullableType(node: JSDocNonNullableType, type: TypeNode): JSDocNonNullableType; - createJSDocNullableType(type: TypeNode): JSDocNullableType; + createJSDocNullableType(type: TypeNode, postfix?: boolean): JSDocNullableType; updateJSDocNullableType(node: JSDocNullableType, type: TypeNode): JSDocNullableType; createJSDocOptionalType(type: TypeNode): JSDocOptionalType; updateJSDocOptionalType(node: JSDocOptionalType, type: TypeNode): JSDocOptionalType; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index c225bd3a9fe71..d97c42d74eac0 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -523,16 +523,17 @@ declare namespace ts { YieldContext = 8192, DecoratorContext = 16384, AwaitContext = 32768, - ThisNodeHasError = 65536, - JavaScriptFile = 131072, - ThisNodeOrAnySubNodesHasError = 262144, - HasAggregatedChildData = 524288, - JSDoc = 4194304, - JsonFile = 33554432, + DisallowConditionalTypesContext = 65536, + ThisNodeHasError = 131072, + JavaScriptFile = 262144, + ThisNodeOrAnySubNodesHasError = 524288, + HasAggregatedChildData = 1048576, + JSDoc = 8388608, + JsonFile = 67108864, BlockScoped = 3, ReachabilityCheckFlags = 768, ReachabilityAndEmitFlags = 2816, - ContextFlags = 25358336, + ContextFlags = 50720768, TypeExcludesFlags = 40960, } export enum ModifierFlags { @@ -1805,10 +1806,12 @@ declare namespace ts { export interface JSDocNonNullableType extends JSDocType { readonly kind: SyntaxKind.JSDocNonNullableType; readonly type: TypeNode; + readonly postfix: boolean; } export interface JSDocNullableType extends JSDocType { readonly kind: SyntaxKind.JSDocNullableType; readonly type: TypeNode; + readonly postfix: boolean; } export interface JSDocOptionalType extends JSDocType { readonly kind: SyntaxKind.JSDocOptionalType; @@ -3654,9 +3657,9 @@ declare namespace ts { updateExternalModuleReference(node: ExternalModuleReference, expression: Expression): ExternalModuleReference; createJSDocAllType(): JSDocAllType; createJSDocUnknownType(): JSDocUnknownType; - createJSDocNonNullableType(type: TypeNode): JSDocNonNullableType; + createJSDocNonNullableType(type: TypeNode, postfix?: boolean): JSDocNonNullableType; updateJSDocNonNullableType(node: JSDocNonNullableType, type: TypeNode): JSDocNonNullableType; - createJSDocNullableType(type: TypeNode): JSDocNullableType; + createJSDocNullableType(type: TypeNode, postfix?: boolean): JSDocNullableType; updateJSDocNullableType(node: JSDocNullableType, type: TypeNode): JSDocNullableType; createJSDocOptionalType(type: TypeNode): JSDocOptionalType; updateJSDocOptionalType(node: JSDocOptionalType, type: TypeNode): JSDocOptionalType; diff --git a/tests/baselines/reference/bitwiseNotOperatorWithEnumType.types b/tests/baselines/reference/bitwiseNotOperatorWithEnumType.types index 8400808656b6e..3ecc4a705a29e 100644 --- a/tests/baselines/reference/bitwiseNotOperatorWithEnumType.types +++ b/tests/baselines/reference/bitwiseNotOperatorWithEnumType.types @@ -5,7 +5,7 @@ enum ENUM1 { A, B, "" }; >ENUM1 : ENUM1 >A : ENUM1.A >B : ENUM1.B ->"" : typeof ENUM1[""] +>"" : (typeof ENUM1)[""] // enum type var var ResultIsNumber1 = ~ENUM1; diff --git a/tests/baselines/reference/constEnums.types b/tests/baselines/reference/constEnums.types index cee4da5c2e1ba..a5b83c46a4ef1 100644 --- a/tests/baselines/reference/constEnums.types +++ b/tests/baselines/reference/constEnums.types @@ -184,25 +184,25 @@ const enum Comments { >Comments : Comments "//", ->"//" : typeof Comments["//"] +>"//" : (typeof Comments)["//"] "/*", ->"/*" : typeof Comments["/*"] +>"/*" : (typeof Comments)["/*"] "*/", ->"*/" : typeof Comments["*/"] +>"*/" : (typeof Comments)["*/"] "///", ->"///" : typeof Comments["///"] +>"///" : (typeof Comments)["///"] "#", ->"#" : typeof Comments["#"] +>"#" : (typeof Comments)["#"] "", ->"-->" : typeof Comments["-->"] +>"-->" : (typeof Comments)["-->"] } module A { @@ -624,37 +624,37 @@ function baz(c: Comments) { >c : Comments case Comments["//"]: ->Comments["//"] : typeof Comments["//"] +>Comments["//"] : (typeof Comments)["//"] >Comments : typeof Comments >"//" : "//" case Comments["/*"]: ->Comments["/*"] : typeof Comments["/*"] +>Comments["/*"] : (typeof Comments)["/*"] >Comments : typeof Comments >"/*" : "/*" case Comments["*/"]: ->Comments["*/"] : typeof Comments["*/"] +>Comments["*/"] : (typeof Comments)["*/"] >Comments : typeof Comments >"*/" : "*/" case Comments["///"]: ->Comments["///"] : typeof Comments["///"] +>Comments["///"] : (typeof Comments)["///"] >Comments : typeof Comments >"///" : "///" case Comments["#"]: ->Comments["#"] : typeof Comments["#"] +>Comments["#"] : (typeof Comments)["#"] >Comments : typeof Comments >"#" : "#" case Comments[""]: ->Comments["-->"] : typeof Comments["-->"] +>Comments["-->"] : (typeof Comments)["-->"] >Comments : typeof Comments >"-->" : "-->" diff --git a/tests/baselines/reference/declFileEnums.types b/tests/baselines/reference/declFileEnums.types index ef3b86f95c4c4..a756898784ea2 100644 --- a/tests/baselines/reference/declFileEnums.types +++ b/tests/baselines/reference/declFileEnums.types @@ -83,7 +83,7 @@ enum e5 { >"Sunday" : e5.Sunday "Weekend days" ->"Weekend days" : typeof e5["Weekend days"] +>"Weekend days" : (typeof e5)["Weekend days"] } diff --git a/tests/baselines/reference/declarationEmitSpreadStringlyKeyedEnum.js b/tests/baselines/reference/declarationEmitSpreadStringlyKeyedEnum.js index d24b12c25ff6b..8ae33d5edd4bc 100644 --- a/tests/baselines/reference/declarationEmitSpreadStringlyKeyedEnum.js +++ b/tests/baselines/reference/declarationEmitSpreadStringlyKeyedEnum.js @@ -42,12 +42,12 @@ declare enum AgeGroups { } export declare const SpotifyAgeGroupEnum: { [x: number]: string; - "0-17": typeof AgeGroups["0-17"]; - "18-22": typeof AgeGroups["18-22"]; - "23-27": typeof AgeGroups["23-27"]; - "28-34": typeof AgeGroups["28-34"]; - "35-44": typeof AgeGroups["35-44"]; - "45-59": typeof AgeGroups["45-59"]; - "60-150": typeof AgeGroups["60-150"]; + "0-17": (typeof AgeGroups)["0-17"]; + "18-22": (typeof AgeGroups)["18-22"]; + "23-27": (typeof AgeGroups)["23-27"]; + "28-34": (typeof AgeGroups)["28-34"]; + "35-44": (typeof AgeGroups)["35-44"]; + "45-59": (typeof AgeGroups)["45-59"]; + "60-150": (typeof AgeGroups)["60-150"]; }; export {}; diff --git a/tests/baselines/reference/declarationEmitSpreadStringlyKeyedEnum.types b/tests/baselines/reference/declarationEmitSpreadStringlyKeyedEnum.types index 82c31d18e68b5..7124555580f3a 100644 --- a/tests/baselines/reference/declarationEmitSpreadStringlyKeyedEnum.types +++ b/tests/baselines/reference/declarationEmitSpreadStringlyKeyedEnum.types @@ -1,16 +1,16 @@ === tests/cases/compiler/declarationEmitSpreadStringlyKeyedEnum.ts === enum AgeGroups { "0-17" , "18-22" , "23-27" , "28-34" , "35-44" , "45-59" , "60-150" } >AgeGroups : AgeGroups ->"0-17" : typeof AgeGroups["0-17"] ->"18-22" : typeof AgeGroups["18-22"] ->"23-27" : typeof AgeGroups["23-27"] ->"28-34" : typeof AgeGroups["28-34"] ->"35-44" : typeof AgeGroups["35-44"] ->"45-59" : typeof AgeGroups["45-59"] ->"60-150" : typeof AgeGroups["60-150"] +>"0-17" : (typeof AgeGroups)["0-17"] +>"18-22" : (typeof AgeGroups)["18-22"] +>"23-27" : (typeof AgeGroups)["23-27"] +>"28-34" : (typeof AgeGroups)["28-34"] +>"35-44" : (typeof AgeGroups)["35-44"] +>"45-59" : (typeof AgeGroups)["45-59"] +>"60-150" : (typeof AgeGroups)["60-150"] export const SpotifyAgeGroupEnum = { ...AgeGroups }; ->SpotifyAgeGroupEnum : { [x: number]: string; "0-17": typeof AgeGroups["0-17"]; "18-22": typeof AgeGroups["18-22"]; "23-27": typeof AgeGroups["23-27"]; "28-34": typeof AgeGroups["28-34"]; "35-44": typeof AgeGroups["35-44"]; "45-59": typeof AgeGroups["45-59"]; "60-150": typeof AgeGroups["60-150"]; } ->{ ...AgeGroups } : { [x: number]: string; "0-17": typeof AgeGroups["0-17"]; "18-22": typeof AgeGroups["18-22"]; "23-27": typeof AgeGroups["23-27"]; "28-34": typeof AgeGroups["28-34"]; "35-44": typeof AgeGroups["35-44"]; "45-59": typeof AgeGroups["45-59"]; "60-150": typeof AgeGroups["60-150"]; } +>SpotifyAgeGroupEnum : { [x: number]: string; "0-17": (typeof AgeGroups)["0-17"]; "18-22": (typeof AgeGroups)["18-22"]; "23-27": (typeof AgeGroups)["23-27"]; "28-34": (typeof AgeGroups)["28-34"]; "35-44": (typeof AgeGroups)["35-44"]; "45-59": (typeof AgeGroups)["45-59"]; "60-150": (typeof AgeGroups)["60-150"]; } +>{ ...AgeGroups } : { [x: number]: string; "0-17": (typeof AgeGroups)["0-17"]; "18-22": (typeof AgeGroups)["18-22"]; "23-27": (typeof AgeGroups)["23-27"]; "28-34": (typeof AgeGroups)["28-34"]; "35-44": (typeof AgeGroups)["35-44"]; "45-59": (typeof AgeGroups)["45-59"]; "60-150": (typeof AgeGroups)["60-150"]; } >AgeGroups : typeof AgeGroups diff --git a/tests/baselines/reference/decrementOperatorWithEnumType.types b/tests/baselines/reference/decrementOperatorWithEnumType.types index 81f2a638fb4e1..45b86f2d2faf5 100644 --- a/tests/baselines/reference/decrementOperatorWithEnumType.types +++ b/tests/baselines/reference/decrementOperatorWithEnumType.types @@ -5,7 +5,7 @@ enum ENUM1 { A, B, "" }; >ENUM1 : ENUM1 >A : ENUM1.A >B : ENUM1.B ->"" : typeof ENUM1[""] +>"" : (typeof ENUM1)[""] // expression var ResultIsNumber1 = --ENUM1["A"]; diff --git a/tests/baselines/reference/decrementOperatorWithEnumTypeInvalidOperations.types b/tests/baselines/reference/decrementOperatorWithEnumTypeInvalidOperations.types index 1e469c9312cc7..3f25332999cd5 100644 --- a/tests/baselines/reference/decrementOperatorWithEnumTypeInvalidOperations.types +++ b/tests/baselines/reference/decrementOperatorWithEnumTypeInvalidOperations.types @@ -8,7 +8,7 @@ enum ENUM1 { A, B, "" }; >ENUM1 : ENUM1 >A : ENUM1.A >B : ENUM1.B ->"" : typeof ENUM1[""] +>"" : (typeof ENUM1)[""] // enum type var var ResultIsNumber1 = --ENUM; diff --git a/tests/baselines/reference/deleteOperatorWithEnumType.types b/tests/baselines/reference/deleteOperatorWithEnumType.types index 1ba8b696c5ea1..143bbda531af7 100644 --- a/tests/baselines/reference/deleteOperatorWithEnumType.types +++ b/tests/baselines/reference/deleteOperatorWithEnumType.types @@ -8,7 +8,7 @@ enum ENUM1 { A, B, "" }; >ENUM1 : ENUM1 >A : ENUM1.A >B : ENUM1.B ->"" : typeof ENUM1[""] +>"" : (typeof ENUM1)[""] // enum type var var ResultIsBoolean1 = delete ENUM; diff --git a/tests/baselines/reference/doubleUnderscoreEnumEmit.types b/tests/baselines/reference/doubleUnderscoreEnumEmit.types index 16690df1086e4..c1758414f1fc1 100644 --- a/tests/baselines/reference/doubleUnderscoreEnumEmit.types +++ b/tests/baselines/reference/doubleUnderscoreEnumEmit.types @@ -7,11 +7,11 @@ enum Foo { >1 : 1 "(Anonymous function)" = 2, ->"(Anonymous function)" : typeof Foo["(Anonymous function)"] +>"(Anonymous function)" : (typeof Foo)["(Anonymous function)"] >2 : 2 "(Anonymous class)" = 4, ->"(Anonymous class)" : typeof Foo["(Anonymous class)"] +>"(Anonymous class)" : (typeof Foo)["(Anonymous class)"] >4 : 4 "__call" = 10 diff --git a/tests/baselines/reference/enumErrors.types b/tests/baselines/reference/enumErrors.types index 05a74414ef05b..5888e0e3e673b 100644 --- a/tests/baselines/reference/enumErrors.types +++ b/tests/baselines/reference/enumErrors.types @@ -132,11 +132,11 @@ enum E13 { postColonValueComma: 2, >postColonValueComma : E13.postColonValueComma ->2 : typeof E13["2"] +>2 : (typeof E13)["2"] postColonValueSemicolon: 3; >postColonValueSemicolon : E13.postColonValueSemicolon ->3 : typeof E13["3"] +>3 : (typeof E13)["3"] }; @@ -146,7 +146,7 @@ enum E14 { a, b: any "hello" += 1, c, d} >b : E14.b >any : E14.any >"hello" : E14.hello ->1 : typeof E14["1"] +>1 : (typeof E14)["1"] >c : E14.c >d : E14.d diff --git a/tests/baselines/reference/enumIdentifierLiterals.types b/tests/baselines/reference/enumIdentifierLiterals.types index 7d5463c374087..e37e8f3a15951 100644 --- a/tests/baselines/reference/enumIdentifierLiterals.types +++ b/tests/baselines/reference/enumIdentifierLiterals.types @@ -3,17 +3,17 @@ enum Nums { >Nums : Nums 1.0, ->1.0 : typeof Nums["1"] +>1.0 : (typeof Nums)["1"] 11e-1, ->11e-1 : typeof Nums["1.1"] +>11e-1 : (typeof Nums)["1.1"] 0.12e1, ->0.12e1 : typeof Nums["1.2"] +>0.12e1 : (typeof Nums)["1.2"] "13e-1", ->"13e-1" : typeof Nums["13e-1"] +>"13e-1" : (typeof Nums)["13e-1"] 0xF00D ->0xF00D : typeof Nums["61453"] +>0xF00D : (typeof Nums)["61453"] } diff --git a/tests/baselines/reference/enumWithNegativeInfinityProperty.types b/tests/baselines/reference/enumWithNegativeInfinityProperty.types index b1cbe05f53ec5..3a795236c47a2 100644 --- a/tests/baselines/reference/enumWithNegativeInfinityProperty.types +++ b/tests/baselines/reference/enumWithNegativeInfinityProperty.types @@ -3,7 +3,7 @@ enum A { >A : A "-Infinity" = 1 ->"-Infinity" : typeof A["-Infinity"] +>"-Infinity" : (typeof A)["-Infinity"] >1 : 1 } diff --git a/tests/baselines/reference/enumWithQuotedElementName1.types b/tests/baselines/reference/enumWithQuotedElementName1.types index b3b708184aea5..2fd0e3f442c69 100644 --- a/tests/baselines/reference/enumWithQuotedElementName1.types +++ b/tests/baselines/reference/enumWithQuotedElementName1.types @@ -3,5 +3,5 @@ enum E { >E : E 'fo"o', ->'fo"o' : typeof E["fo\"o"] +>'fo"o' : (typeof E)["fo\"o"] } diff --git a/tests/baselines/reference/enumWithQuotedElementName2.types b/tests/baselines/reference/enumWithQuotedElementName2.types index 67fa731e88c29..ce871d0535267 100644 --- a/tests/baselines/reference/enumWithQuotedElementName2.types +++ b/tests/baselines/reference/enumWithQuotedElementName2.types @@ -3,5 +3,5 @@ enum E { >E : E "fo'o", ->"fo'o" : typeof E["fo'o"] +>"fo'o" : (typeof E)["fo'o"] } diff --git a/tests/baselines/reference/enumWithUnicodeEscape1.types b/tests/baselines/reference/enumWithUnicodeEscape1.types index 701f0f9bd3b37..e33394fba3153 100644 --- a/tests/baselines/reference/enumWithUnicodeEscape1.types +++ b/tests/baselines/reference/enumWithUnicodeEscape1.types @@ -3,6 +3,6 @@ enum E { >E : E 'gold \u2730' ->'gold \u2730' : typeof E["gold \u2730"] +>'gold \u2730' : (typeof E)["gold \u2730"] } diff --git a/tests/baselines/reference/incrementOperatorWithEnumType.types b/tests/baselines/reference/incrementOperatorWithEnumType.types index deb056503a61a..6c7c1720bf69f 100644 --- a/tests/baselines/reference/incrementOperatorWithEnumType.types +++ b/tests/baselines/reference/incrementOperatorWithEnumType.types @@ -5,7 +5,7 @@ enum ENUM1 { A, B, "" }; >ENUM1 : ENUM1 >A : ENUM1.A >B : ENUM1.B ->"" : typeof ENUM1[""] +>"" : (typeof ENUM1)[""] // expression var ResultIsNumber1 = ++ENUM1["B"]; diff --git a/tests/baselines/reference/incrementOperatorWithEnumTypeInvalidOperations.types b/tests/baselines/reference/incrementOperatorWithEnumTypeInvalidOperations.types index d4458ca41bb35..5c33351ea2345 100644 --- a/tests/baselines/reference/incrementOperatorWithEnumTypeInvalidOperations.types +++ b/tests/baselines/reference/incrementOperatorWithEnumTypeInvalidOperations.types @@ -8,7 +8,7 @@ enum ENUM1 { A, B, "" }; >ENUM1 : ENUM1 >A : ENUM1.A >B : ENUM1.B ->"" : typeof ENUM1[""] +>"" : (typeof ENUM1)[""] // enum type var var ResultIsNumber1 = ++ENUM; diff --git a/tests/baselines/reference/inferTypes1.errors.txt b/tests/baselines/reference/inferTypes1.errors.txt index be2fa68bfbba5..277f3e912d2a3 100644 --- a/tests/baselines/reference/inferTypes1.errors.txt +++ b/tests/baselines/reference/inferTypes1.errors.txt @@ -8,9 +8,9 @@ tests/cases/conformance/types/conditional/inferTypes1.ts(55,25): error TS2344: T tests/cases/conformance/types/conditional/inferTypes1.ts(56,25): error TS2344: Type 'Function' does not satisfy the constraint '(x: any) => any'. Type 'Function' provides no match for the signature '(x: any): any'. tests/cases/conformance/types/conditional/inferTypes1.ts(82,12): error TS1338: 'infer' declarations are only permitted in the 'extends' clause of a conditional type. -tests/cases/conformance/types/conditional/inferTypes1.ts(83,15): error TS1338: 'infer' declarations are only permitted in the 'extends' clause of a conditional type. -tests/cases/conformance/types/conditional/inferTypes1.ts(83,41): error TS1338: 'infer' declarations are only permitted in the 'extends' clause of a conditional type. -tests/cases/conformance/types/conditional/inferTypes1.ts(83,51): error TS1338: 'infer' declarations are only permitted in the 'extends' clause of a conditional type. +tests/cases/conformance/types/conditional/inferTypes1.ts(83,16): error TS1338: 'infer' declarations are only permitted in the 'extends' clause of a conditional type. +tests/cases/conformance/types/conditional/inferTypes1.ts(83,43): error TS1338: 'infer' declarations are only permitted in the 'extends' clause of a conditional type. +tests/cases/conformance/types/conditional/inferTypes1.ts(83,53): error TS1338: 'infer' declarations are only permitted in the 'extends' clause of a conditional type. tests/cases/conformance/types/conditional/inferTypes1.ts(84,15): error TS2304: Cannot find name 'U'. tests/cases/conformance/types/conditional/inferTypes1.ts(84,15): error TS4081: Exported type alias 'T62' has or is using private name 'U'. tests/cases/conformance/types/conditional/inferTypes1.ts(84,43): error TS2304: Cannot find name 'U'. @@ -120,12 +120,12 @@ tests/cases/conformance/types/conditional/inferTypes1.ts(153,40): error TS2322: type T60 = infer U; // Error ~~~~~~~ !!! error TS1338: 'infer' declarations are only permitted in the 'extends' clause of a conditional type. - type T61 = infer A extends infer B ? infer C : infer D; // Error - ~~~~~~~ + type T61 = (infer A) extends infer B ? infer C : infer D; // Error + ~~~~~~~ !!! error TS1338: 'infer' declarations are only permitted in the 'extends' clause of a conditional type. - ~~~~~~~ + ~~~~~~~ !!! error TS1338: 'infer' declarations are only permitted in the 'extends' clause of a conditional type. - ~~~~~~~ + ~~~~~~~ !!! error TS1338: 'infer' declarations are only permitted in the 'extends' clause of a conditional type. type T62 = U extends (infer U)[] ? U : U; // Error ~ @@ -136,7 +136,7 @@ tests/cases/conformance/types/conditional/inferTypes1.ts(153,40): error TS2322: !!! error TS2304: Cannot find name 'U'. ~ !!! error TS4081: Exported type alias 'T62' has or is using private name 'U'. - type T63 = T extends (infer A extends infer B ? infer C : infer D) ? string : number; + type T63 = T extends ((infer A) extends infer B ? infer C : infer D) ? string : number; type T70 = { x: T }; type T71 = T extends T70 ? T70 : never; diff --git a/tests/baselines/reference/inferTypes1.js b/tests/baselines/reference/inferTypes1.js index e3e2b88dd4edb..35421c73ad285 100644 --- a/tests/baselines/reference/inferTypes1.js +++ b/tests/baselines/reference/inferTypes1.js @@ -81,9 +81,9 @@ type T53 = X3<{ a: (x: number) => void, b: (x: string) => void }>; // never type T54 = X3<{ a: (x: number) => void, b: () => void }>; // number type T60 = infer U; // Error -type T61 = infer A extends infer B ? infer C : infer D; // Error +type T61 = (infer A) extends infer B ? infer C : infer D; // Error type T62 = U extends (infer U)[] ? U : U; // Error -type T63 = T extends (infer A extends infer B ? infer C : infer D) ? string : number; +type T63 = T extends ((infer A) extends infer B ? infer C : infer D) ? string : number; type T70 = { x: T }; type T71 = T extends T70 ? T70 : never; diff --git a/tests/baselines/reference/inferTypes1.symbols b/tests/baselines/reference/inferTypes1.symbols index 7e79885184007..33d8052a8c73e 100644 --- a/tests/baselines/reference/inferTypes1.symbols +++ b/tests/baselines/reference/inferTypes1.symbols @@ -349,33 +349,33 @@ type T60 = infer U; // Error >T60 : Symbol(T60, Decl(inferTypes1.ts, 79, 57)) >U : Symbol(U, Decl(inferTypes1.ts, 81, 16)) -type T61 = infer A extends infer B ? infer C : infer D; // Error +type T61 = (infer A) extends infer B ? infer C : infer D; // Error >T61 : Symbol(T61, Decl(inferTypes1.ts, 81, 19)) >T : Symbol(T, Decl(inferTypes1.ts, 82, 9)) ->A : Symbol(A, Decl(inferTypes1.ts, 82, 19)) ->B : Symbol(B, Decl(inferTypes1.ts, 82, 35)) ->C : Symbol(C, Decl(inferTypes1.ts, 82, 45)) ->D : Symbol(D, Decl(inferTypes1.ts, 82, 55)) +>A : Symbol(A, Decl(inferTypes1.ts, 82, 20)) +>B : Symbol(B, Decl(inferTypes1.ts, 82, 37)) +>C : Symbol(C, Decl(inferTypes1.ts, 82, 47)) +>D : Symbol(D, Decl(inferTypes1.ts, 82, 57)) type T62 = U extends (infer U)[] ? U : U; // Error ->T62 : Symbol(T62, Decl(inferTypes1.ts, 82, 58)) +>T62 : Symbol(T62, Decl(inferTypes1.ts, 82, 60)) >T : Symbol(T, Decl(inferTypes1.ts, 83, 9)) >U : Symbol(U) >U : Symbol(U, Decl(inferTypes1.ts, 83, 30)) >U : Symbol(U, Decl(inferTypes1.ts, 83, 30)) >U : Symbol(U) -type T63 = T extends (infer A extends infer B ? infer C : infer D) ? string : number; +type T63 = T extends ((infer A) extends infer B ? infer C : infer D) ? string : number; >T63 : Symbol(T63, Decl(inferTypes1.ts, 83, 44)) >T : Symbol(T, Decl(inferTypes1.ts, 84, 9)) >T : Symbol(T, Decl(inferTypes1.ts, 84, 9)) ->A : Symbol(A, Decl(inferTypes1.ts, 84, 30)) ->B : Symbol(B, Decl(inferTypes1.ts, 84, 46)) ->C : Symbol(C, Decl(inferTypes1.ts, 84, 56)) ->D : Symbol(D, Decl(inferTypes1.ts, 84, 66)) +>A : Symbol(A, Decl(inferTypes1.ts, 84, 31)) +>B : Symbol(B, Decl(inferTypes1.ts, 84, 48)) +>C : Symbol(C, Decl(inferTypes1.ts, 84, 58)) +>D : Symbol(D, Decl(inferTypes1.ts, 84, 68)) type T70 = { x: T }; ->T70 : Symbol(T70, Decl(inferTypes1.ts, 84, 88)) +>T70 : Symbol(T70, Decl(inferTypes1.ts, 84, 90)) >T : Symbol(T, Decl(inferTypes1.ts, 86, 9)) >x : Symbol(x, Decl(inferTypes1.ts, 86, 30)) >T : Symbol(T, Decl(inferTypes1.ts, 86, 9)) @@ -384,9 +384,9 @@ type T71 = T extends T70 ? T70 : never; >T71 : Symbol(T71, Decl(inferTypes1.ts, 86, 38)) >T : Symbol(T, Decl(inferTypes1.ts, 87, 9)) >T : Symbol(T, Decl(inferTypes1.ts, 87, 9)) ->T70 : Symbol(T70, Decl(inferTypes1.ts, 84, 88)) +>T70 : Symbol(T70, Decl(inferTypes1.ts, 84, 90)) >U : Symbol(U, Decl(inferTypes1.ts, 87, 33)) ->T70 : Symbol(T70, Decl(inferTypes1.ts, 84, 88)) +>T70 : Symbol(T70, Decl(inferTypes1.ts, 84, 90)) >U : Symbol(U, Decl(inferTypes1.ts, 87, 33)) type T72 = { y: T }; @@ -401,7 +401,7 @@ type T73 = T extends T72 ? T70 : never; // Error >T : Symbol(T, Decl(inferTypes1.ts, 90, 9)) >T72 : Symbol(T72, Decl(inferTypes1.ts, 87, 54)) >U : Symbol(U, Decl(inferTypes1.ts, 90, 33)) ->T70 : Symbol(T70, Decl(inferTypes1.ts, 84, 88)) +>T70 : Symbol(T70, Decl(inferTypes1.ts, 84, 90)) >U : Symbol(U, Decl(inferTypes1.ts, 90, 33)) type T74 = { x: T, y: U }; @@ -420,7 +420,7 @@ type T75 = T extends T74 ? T70 | T72 | T74 : ne >T74 : Symbol(T74, Decl(inferTypes1.ts, 90, 54)) >U : Symbol(U, Decl(inferTypes1.ts, 93, 33), Decl(inferTypes1.ts, 93, 42)) >U : Symbol(U, Decl(inferTypes1.ts, 93, 33), Decl(inferTypes1.ts, 93, 42)) ->T70 : Symbol(T70, Decl(inferTypes1.ts, 84, 88)) +>T70 : Symbol(T70, Decl(inferTypes1.ts, 84, 90)) >U : Symbol(U, Decl(inferTypes1.ts, 93, 33), Decl(inferTypes1.ts, 93, 42)) >T72 : Symbol(T72, Decl(inferTypes1.ts, 87, 54)) >U : Symbol(U, Decl(inferTypes1.ts, 93, 33), Decl(inferTypes1.ts, 93, 42)) diff --git a/tests/baselines/reference/inferTypes1.types b/tests/baselines/reference/inferTypes1.types index 4ef817304b407..c7a609b3eaed2 100644 --- a/tests/baselines/reference/inferTypes1.types +++ b/tests/baselines/reference/inferTypes1.types @@ -253,13 +253,13 @@ type T54 = X3<{ a: (x: number) => void, b: () => void }>; // number type T60 = infer U; // Error >T60 : U -type T61 = infer A extends infer B ? infer C : infer D; // Error +type T61 = (infer A) extends infer B ? infer C : infer D; // Error >T61 : T61 type T62 = U extends (infer U)[] ? U : U; // Error >T62 : any -type T63 = T extends (infer A extends infer B ? infer C : infer D) ? string : number; +type T63 = T extends ((infer A) extends infer B ? infer C : infer D) ? string : number; >T63 : T63 type T70 = { x: T }; diff --git a/tests/baselines/reference/inferTypesWithExtends1.js b/tests/baselines/reference/inferTypesWithExtends1.js new file mode 100644 index 0000000000000..6ce0739872373 --- /dev/null +++ b/tests/baselines/reference/inferTypesWithExtends1.js @@ -0,0 +1,298 @@ +//// [inferTypesWithExtends1.ts] +// infer to tuple element +type X1 = + T extends [infer U extends string] ? ["string", U] : + T extends [infer U extends number] ? ["number", U] : + never; + +type X1_T1 = X1<["a"]>; // ["string", "a"] +type X1_T2 = X1<[1]>; // ["number", 1] +type X1_T3 = X1<[object]>; // never + +// infer to argument +type X2 void> = + T extends (a: infer U extends string) => void ? ["string", U] : + T extends (a: infer U extends number) => void ? ["number", U] : + never; + +type X2_T1 = X2<(a: "a") => void>; // ["string", "a"] +type X2_T2 = X2<(a: 1) => void>; // ["number", 1] +type X2_T3 = X2<(a: object) => void>; // never + +// infer to return type +type X3 any> = + T extends (...args: any[]) => (infer U extends string) ? ["string", U] : + T extends (...args: any[]) => (infer U extends number) ? ["number", U] : + never; + +type X3_T1 = X3<() => "a">; // ["string", "a"] +type X3_T2 = X3<() => 1>; // ["number", 1] +type X3_T3 = X3<() => object>; // never + +// infer to instance type +type X4 any> = + T extends new (...args: any[]) => (infer U extends { a: string }) ? ["string", U] : + T extends new (...args: any[]) => (infer U extends { a: number }) ? ["number", U] : + never; + +type X4_T1 = X4 { a: "a" }>; // ["string", { a: "a" }] +type X4_T2 = X4 { a: 1 }>; // ["number", { a: 1 }] +type X4_T3 = X4 { a: object }>; // never + +// infer to type argument +type X5 = + T extends Promise ? ["string", U] : + T extends Promise ? ["number", U] : + never; + +type X5_T1 = X5>; // ["string", "a" | "b"] +type X5_T2 = X5>; // ["number", 1 | 2] +type X5_T3 = X5>; // never + +// infer to property type +type X6 = + T extends { a: infer U extends string } ? ["string", U] : + T extends { a: infer U extends number } ? ["number", U] : + never; + +type X6_T1 = X6<{ a: "a" }>; // ["string", "a"] +type X6_T2 = X6<{ a: 1 }>; // ["number", 1] +type X6_T3 = X6<{ a: object }>; // never + +// infer twice with same constraint +type X7 = + T extends { a: infer U extends string, b: infer U extends string } ? ["string", U] : + T extends { a: infer U extends number, b: infer U extends number } ? ["number", U] : + never; + +type X7_T1 = X7<{ a: "a", b: "b" }>; // ["string", "a" | "b"] +type X7_T2 = X7<{ a: 1, b: 2 }>; // ["number", 1 | 2] +type X7_T3 = X7<{ a: object, b: object }>; // never +type X7_T4 = X7<{ a: "a", b: 1 }>; // never + +// infer twice with missing second constraint (same behavior as class/interface) +type X8 = + T extends { a: infer U extends string, b: infer U } ? ["string", U] : + T extends { a: infer U extends number, b: infer U } ? ["number", U] : + never; + +type X8_T1 = X8<{ a: "a", b: "b" }>; // ["string", "a" | "b"] +type X8_T2 = X8<{ a: 1, b: 2 }>; // ["number", 1 | 2] +type X8_T3 = X8<{ a: object, b: object }>; // never +type X8_T4 = X8<{ a: "a", b: 1 }>; // never + +// infer twice with missing first constraint (same behavior as class/interface) +type X9 = + T extends { a: infer U, b: infer U extends string } ? ["string", U] : + T extends { a: infer U, b: infer U extends number } ? ["number", U] : + never; + +type X9_T1 = X9<{ a: "a", b: "b" }>; // ["string", "a" | "b"] +type X9_T2 = X9<{ a: 1, b: 2 }>; // ["number", 1 | 2] +type X9_T3 = X9<{ a: object, b: object }>; // never +type X9_T4 = X9<{ a: "a", b: 1 }>; // never + +// Speculative lookahead for `infer T extends U ?` +type X10 = T extends (infer U extends number ? 1 : 0) ? 1 : 0; // ok, parsed as conditional +type X10_Y1 = X10; +type X10_T1_T1 = X10_Y1; + +type X11 = T extends ((infer U) extends number ? 1 : 0) ? 1 : 0; // ok, parsed as conditional +type X12 = T extends (infer U extends number) ? 1 : 0; // ok, parsed as `infer..extends` (no trailing `?`) +type X13 = T extends infer U extends number ? 1 : 0; // ok, parsed as `infer..extends` (conditional types not allowed in 'extends type') +type X14 = T extends keyof infer U extends number ? 1 : 0; // ok, parsed as `infer..extends` (precedence wouldn't have parsed the `?` as part of a type operator) +type X15 = T extends { [P in infer U extends keyof T ? 1 : 0]: 1; } ? 1 : 0; // ok, parsed as conditional +type X16 = T extends { [P in infer U extends keyof T]: 1; } ? 1 : 0; // ok, parsed as `infer..extends` (no trailing `?`) +type X17 = T extends { [P in keyof T as infer U extends P ? 1 : 0]: 1; } ? 1 : 0; // ok, parsed as conditional +type X18 = T extends { [P in keyof T as infer U extends P]: 1; } ? 1 : 0; // ok, parsed as `infer..extends` (no trailing `?`) + +type X19 = T extends (infer U extends number) ? [T, U] : never; +type X19_T1 = X19<"a">; // never +type X19_T2 = X19<1>; // [1, 1] +type X19_T3 = X19<1 | "a">; // [1, 1] + +type X20 = T extends (infer U extends number) ? T extends (infer V extends U) ? [T, U, V] : never : never; +type X20_T1 = X20<1 | "a">; // [1, 1, 1] + +type X21 = T extends (infer U extends N) ? [T, U] : never; +type X21_T1 = X21<1, 1>; // [1, 1] +type X21_T2 = X21<1 | "a", 1>; // [1, 1] +type X21_T3 = X21<1 | 2, 1>; // [1, 1] +type X21_T4 = X21<1 | 2, 2 | 3>; // [2, 2] +type X21_T5 = X21<1 | 2, 3>; // never + +// from mongoose +type IfEquals = (() => T extends X ? 1 : 2) extends () => T extends Y ? 1 : 2 ? A : B; + +declare const x1: () => (T extends infer U extends number ? 1 : 0); +function f1() { + return x1; +} + +type ExpectNumber = T; +declare const x2: () => (T extends ExpectNumber ? 1 : 0); +function f2() { + return x2; +} + +//// [inferTypesWithExtends1.js] +"use strict"; +function f1() { + return x1; +} +function f2() { + return x2; +} + + +//// [inferTypesWithExtends1.d.ts] +declare type X1 = T extends [infer U extends string] ? ["string", U] : T extends [infer U extends number] ? ["number", U] : never; +declare type X1_T1 = X1<["a"]>; +declare type X1_T2 = X1<[1]>; +declare type X1_T3 = X1<[object]>; +declare type X2 void> = T extends (a: infer U extends string) => void ? ["string", U] : T extends (a: infer U extends number) => void ? ["number", U] : never; +declare type X2_T1 = X2<(a: "a") => void>; +declare type X2_T2 = X2<(a: 1) => void>; +declare type X2_T3 = X2<(a: object) => void>; +declare type X3 any> = T extends (...args: any[]) => (infer U extends string) ? ["string", U] : T extends (...args: any[]) => (infer U extends number) ? ["number", U] : never; +declare type X3_T1 = X3<() => "a">; +declare type X3_T2 = X3<() => 1>; +declare type X3_T3 = X3<() => object>; +declare type X4 any> = T extends new (...args: any[]) => (infer U extends { + a: string; +}) ? ["string", U] : T extends new (...args: any[]) => (infer U extends { + a: number; +}) ? ["number", U] : never; +declare type X4_T1 = X4 { + a: "a"; +}>; +declare type X4_T2 = X4 { + a: 1; +}>; +declare type X4_T3 = X4 { + a: object; +}>; +declare type X5 = T extends Promise ? ["string", U] : T extends Promise ? ["number", U] : never; +declare type X5_T1 = X5>; +declare type X5_T2 = X5>; +declare type X5_T3 = X5>; +declare type X6 = T extends { + a: infer U extends string; +} ? ["string", U] : T extends { + a: infer U extends number; +} ? ["number", U] : never; +declare type X6_T1 = X6<{ + a: "a"; +}>; +declare type X6_T2 = X6<{ + a: 1; +}>; +declare type X6_T3 = X6<{ + a: object; +}>; +declare type X7 = T extends { + a: infer U extends string; + b: infer U extends string; +} ? ["string", U] : T extends { + a: infer U extends number; + b: infer U extends number; +} ? ["number", U] : never; +declare type X7_T1 = X7<{ + a: "a"; + b: "b"; +}>; +declare type X7_T2 = X7<{ + a: 1; + b: 2; +}>; +declare type X7_T3 = X7<{ + a: object; + b: object; +}>; +declare type X7_T4 = X7<{ + a: "a"; + b: 1; +}>; +declare type X8 = T extends { + a: infer U extends string; + b: infer U; +} ? ["string", U] : T extends { + a: infer U extends number; + b: infer U; +} ? ["number", U] : never; +declare type X8_T1 = X8<{ + a: "a"; + b: "b"; +}>; +declare type X8_T2 = X8<{ + a: 1; + b: 2; +}>; +declare type X8_T3 = X8<{ + a: object; + b: object; +}>; +declare type X8_T4 = X8<{ + a: "a"; + b: 1; +}>; +declare type X9 = T extends { + a: infer U; + b: infer U extends string; +} ? ["string", U] : T extends { + a: infer U; + b: infer U extends number; +} ? ["number", U] : never; +declare type X9_T1 = X9<{ + a: "a"; + b: "b"; +}>; +declare type X9_T2 = X9<{ + a: 1; + b: 2; +}>; +declare type X9_T3 = X9<{ + a: object; + b: object; +}>; +declare type X9_T4 = X9<{ + a: "a"; + b: 1; +}>; +declare type X10 = T extends (infer U extends number ? 1 : 0) ? 1 : 0; +declare type X10_Y1 = X10; +declare type X10_T1_T1 = X10_Y1; +declare type X11 = T extends ((infer U) extends number ? 1 : 0) ? 1 : 0; +declare type X12 = T extends (infer U extends number) ? 1 : 0; +declare type X13 = T extends infer U extends number ? 1 : 0; +declare type X14 = T extends keyof infer U extends number ? 1 : 0; +declare type X15 = T extends { + [P in infer U extends keyof T ? 1 : 0]: 1; +} ? 1 : 0; +declare type X16 = T extends { + [P in infer U extends keyof T]: 1; +} ? 1 : 0; +declare type X17 = T extends { + [P in keyof T as infer U extends P ? 1 : 0]: 1; +} ? 1 : 0; +declare type X18 = T extends { + [P in keyof T as infer U extends P]: 1; +} ? 1 : 0; +declare type X19 = T extends (infer U extends number) ? [T, U] : never; +declare type X19_T1 = X19<"a">; +declare type X19_T2 = X19<1>; +declare type X19_T3 = X19<1 | "a">; +declare type X20 = T extends (infer U extends number) ? T extends (infer V extends U) ? [T, U, V] : never : never; +declare type X20_T1 = X20<1 | "a">; +declare type X21 = T extends (infer U extends N) ? [T, U] : never; +declare type X21_T1 = X21<1, 1>; +declare type X21_T2 = X21<1 | "a", 1>; +declare type X21_T3 = X21<1 | 2, 1>; +declare type X21_T4 = X21<1 | 2, 2 | 3>; +declare type X21_T5 = X21<1 | 2, 3>; +declare type IfEquals = (() => T extends X ? 1 : 2) extends () => T extends Y ? 1 : 2 ? A : B; +declare const x1: () => (T extends infer U extends number ? 1 : 0); +declare function f1(): () => T extends infer U extends number ? 1 : 0; +declare type ExpectNumber = T; +declare const x2: () => (T extends ExpectNumber ? 1 : 0); +declare function f2(): () => T extends infer U extends number ? 1 : 0; diff --git a/tests/baselines/reference/inferTypesWithExtends1.symbols b/tests/baselines/reference/inferTypesWithExtends1.symbols new file mode 100644 index 0000000000000..01cdc578af5e6 --- /dev/null +++ b/tests/baselines/reference/inferTypesWithExtends1.symbols @@ -0,0 +1,531 @@ +=== tests/cases/conformance/types/conditional/inferTypesWithExtends1.ts === +// infer to tuple element +type X1 = +>X1 : Symbol(X1, Decl(inferTypesWithExtends1.ts, 0, 0)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 1, 8)) + + T extends [infer U extends string] ? ["string", U] : +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 1, 8)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 2, 20)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 2, 20)) + + T extends [infer U extends number] ? ["number", U] : +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 1, 8)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 3, 20)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 3, 20)) + + never; + +type X1_T1 = X1<["a"]>; // ["string", "a"] +>X1_T1 : Symbol(X1_T1, Decl(inferTypesWithExtends1.ts, 4, 10)) +>X1 : Symbol(X1, Decl(inferTypesWithExtends1.ts, 0, 0)) + +type X1_T2 = X1<[1]>; // ["number", 1] +>X1_T2 : Symbol(X1_T2, Decl(inferTypesWithExtends1.ts, 6, 23)) +>X1 : Symbol(X1, Decl(inferTypesWithExtends1.ts, 0, 0)) + +type X1_T3 = X1<[object]>; // never +>X1_T3 : Symbol(X1_T3, Decl(inferTypesWithExtends1.ts, 7, 21)) +>X1 : Symbol(X1, Decl(inferTypesWithExtends1.ts, 0, 0)) + +// infer to argument +type X2 void> = +>X2 : Symbol(X2, Decl(inferTypesWithExtends1.ts, 8, 26)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 11, 8)) +>args : Symbol(args, Decl(inferTypesWithExtends1.ts, 11, 19)) + + T extends (a: infer U extends string) => void ? ["string", U] : +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 11, 8)) +>a : Symbol(a, Decl(inferTypesWithExtends1.ts, 12, 15)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 12, 23)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 12, 23)) + + T extends (a: infer U extends number) => void ? ["number", U] : +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 11, 8)) +>a : Symbol(a, Decl(inferTypesWithExtends1.ts, 13, 15)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 13, 23)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 13, 23)) + + never; + +type X2_T1 = X2<(a: "a") => void>; // ["string", "a"] +>X2_T1 : Symbol(X2_T1, Decl(inferTypesWithExtends1.ts, 14, 10)) +>X2 : Symbol(X2, Decl(inferTypesWithExtends1.ts, 8, 26)) +>a : Symbol(a, Decl(inferTypesWithExtends1.ts, 16, 17)) + +type X2_T2 = X2<(a: 1) => void>; // ["number", 1] +>X2_T2 : Symbol(X2_T2, Decl(inferTypesWithExtends1.ts, 16, 34)) +>X2 : Symbol(X2, Decl(inferTypesWithExtends1.ts, 8, 26)) +>a : Symbol(a, Decl(inferTypesWithExtends1.ts, 17, 17)) + +type X2_T3 = X2<(a: object) => void>; // never +>X2_T3 : Symbol(X2_T3, Decl(inferTypesWithExtends1.ts, 17, 32)) +>X2 : Symbol(X2, Decl(inferTypesWithExtends1.ts, 8, 26)) +>a : Symbol(a, Decl(inferTypesWithExtends1.ts, 18, 17)) + +// infer to return type +type X3 any> = +>X3 : Symbol(X3, Decl(inferTypesWithExtends1.ts, 18, 37)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 21, 8)) +>args : Symbol(args, Decl(inferTypesWithExtends1.ts, 21, 19)) + + T extends (...args: any[]) => (infer U extends string) ? ["string", U] : +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 21, 8)) +>args : Symbol(args, Decl(inferTypesWithExtends1.ts, 22, 15)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 22, 40)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 22, 40)) + + T extends (...args: any[]) => (infer U extends number) ? ["number", U] : +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 21, 8)) +>args : Symbol(args, Decl(inferTypesWithExtends1.ts, 23, 15)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 23, 40)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 23, 40)) + + never; + +type X3_T1 = X3<() => "a">; // ["string", "a"] +>X3_T1 : Symbol(X3_T1, Decl(inferTypesWithExtends1.ts, 24, 10)) +>X3 : Symbol(X3, Decl(inferTypesWithExtends1.ts, 18, 37)) + +type X3_T2 = X3<() => 1>; // ["number", 1] +>X3_T2 : Symbol(X3_T2, Decl(inferTypesWithExtends1.ts, 26, 27)) +>X3 : Symbol(X3, Decl(inferTypesWithExtends1.ts, 18, 37)) + +type X3_T3 = X3<() => object>; // never +>X3_T3 : Symbol(X3_T3, Decl(inferTypesWithExtends1.ts, 27, 25)) +>X3 : Symbol(X3, Decl(inferTypesWithExtends1.ts, 18, 37)) + +// infer to instance type +type X4 any> = +>X4 : Symbol(X4, Decl(inferTypesWithExtends1.ts, 28, 30)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 31, 8)) +>args : Symbol(args, Decl(inferTypesWithExtends1.ts, 31, 23)) + + T extends new (...args: any[]) => (infer U extends { a: string }) ? ["string", U] : +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 31, 8)) +>args : Symbol(args, Decl(inferTypesWithExtends1.ts, 32, 19)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 32, 44)) +>a : Symbol(a, Decl(inferTypesWithExtends1.ts, 32, 56)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 32, 44)) + + T extends new (...args: any[]) => (infer U extends { a: number }) ? ["number", U] : +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 31, 8)) +>args : Symbol(args, Decl(inferTypesWithExtends1.ts, 33, 19)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 33, 44)) +>a : Symbol(a, Decl(inferTypesWithExtends1.ts, 33, 56)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 33, 44)) + + never; + +type X4_T1 = X4 { a: "a" }>; // ["string", { a: "a" }] +>X4_T1 : Symbol(X4_T1, Decl(inferTypesWithExtends1.ts, 34, 10)) +>X4 : Symbol(X4, Decl(inferTypesWithExtends1.ts, 28, 30)) +>a : Symbol(a, Decl(inferTypesWithExtends1.ts, 36, 27)) + +type X4_T2 = X4 { a: 1 }>; // ["number", { a: 1 }] +>X4_T2 : Symbol(X4_T2, Decl(inferTypesWithExtends1.ts, 36, 38)) +>X4 : Symbol(X4, Decl(inferTypesWithExtends1.ts, 28, 30)) +>a : Symbol(a, Decl(inferTypesWithExtends1.ts, 37, 27)) + +type X4_T3 = X4 { a: object }>; // never +>X4_T3 : Symbol(X4_T3, Decl(inferTypesWithExtends1.ts, 37, 36)) +>X4 : Symbol(X4, Decl(inferTypesWithExtends1.ts, 28, 30)) +>a : Symbol(a, Decl(inferTypesWithExtends1.ts, 38, 27)) + +// infer to type argument +type X5 = +>X5 : Symbol(X5, Decl(inferTypesWithExtends1.ts, 38, 41)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 41, 8)) + + T extends Promise ? ["string", U] : +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 41, 8)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 42, 27)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 42, 27)) + + T extends Promise ? ["number", U] : +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 41, 8)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 43, 27)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 43, 27)) + + never; + +type X5_T1 = X5>; // ["string", "a" | "b"] +>X5_T1 : Symbol(X5_T1, Decl(inferTypesWithExtends1.ts, 44, 10)) +>X5 : Symbol(X5, Decl(inferTypesWithExtends1.ts, 38, 41)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --)) + +type X5_T2 = X5>; // ["number", 1 | 2] +>X5_T2 : Symbol(X5_T2, Decl(inferTypesWithExtends1.ts, 46, 36)) +>X5 : Symbol(X5, Decl(inferTypesWithExtends1.ts, 38, 41)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --)) + +type X5_T3 = X5>; // never +>X5_T3 : Symbol(X5_T3, Decl(inferTypesWithExtends1.ts, 47, 32)) +>X5 : Symbol(X5, Decl(inferTypesWithExtends1.ts, 38, 41)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --)) + +// infer to property type +type X6 = +>X6 : Symbol(X6, Decl(inferTypesWithExtends1.ts, 48, 34)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 51, 8)) + + T extends { a: infer U extends string } ? ["string", U] : +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 51, 8)) +>a : Symbol(a, Decl(inferTypesWithExtends1.ts, 52, 15)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 52, 24)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 52, 24)) + + T extends { a: infer U extends number } ? ["number", U] : +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 51, 8)) +>a : Symbol(a, Decl(inferTypesWithExtends1.ts, 53, 15)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 53, 24)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 53, 24)) + + never; + +type X6_T1 = X6<{ a: "a" }>; // ["string", "a"] +>X6_T1 : Symbol(X6_T1, Decl(inferTypesWithExtends1.ts, 54, 10)) +>X6 : Symbol(X6, Decl(inferTypesWithExtends1.ts, 48, 34)) +>a : Symbol(a, Decl(inferTypesWithExtends1.ts, 56, 17)) + +type X6_T2 = X6<{ a: 1 }>; // ["number", 1] +>X6_T2 : Symbol(X6_T2, Decl(inferTypesWithExtends1.ts, 56, 28)) +>X6 : Symbol(X6, Decl(inferTypesWithExtends1.ts, 48, 34)) +>a : Symbol(a, Decl(inferTypesWithExtends1.ts, 57, 17)) + +type X6_T3 = X6<{ a: object }>; // never +>X6_T3 : Symbol(X6_T3, Decl(inferTypesWithExtends1.ts, 57, 26)) +>X6 : Symbol(X6, Decl(inferTypesWithExtends1.ts, 48, 34)) +>a : Symbol(a, Decl(inferTypesWithExtends1.ts, 58, 17)) + +// infer twice with same constraint +type X7 = +>X7 : Symbol(X7, Decl(inferTypesWithExtends1.ts, 58, 31)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 61, 8)) + + T extends { a: infer U extends string, b: infer U extends string } ? ["string", U] : +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 61, 8)) +>a : Symbol(a, Decl(inferTypesWithExtends1.ts, 62, 15)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 62, 24), Decl(inferTypesWithExtends1.ts, 62, 51)) +>b : Symbol(b, Decl(inferTypesWithExtends1.ts, 62, 42)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 62, 24), Decl(inferTypesWithExtends1.ts, 62, 51)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 62, 24), Decl(inferTypesWithExtends1.ts, 62, 51)) + + T extends { a: infer U extends number, b: infer U extends number } ? ["number", U] : +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 61, 8)) +>a : Symbol(a, Decl(inferTypesWithExtends1.ts, 63, 15)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 63, 24), Decl(inferTypesWithExtends1.ts, 63, 51)) +>b : Symbol(b, Decl(inferTypesWithExtends1.ts, 63, 42)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 63, 24), Decl(inferTypesWithExtends1.ts, 63, 51)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 63, 24), Decl(inferTypesWithExtends1.ts, 63, 51)) + + never; + +type X7_T1 = X7<{ a: "a", b: "b" }>; // ["string", "a" | "b"] +>X7_T1 : Symbol(X7_T1, Decl(inferTypesWithExtends1.ts, 64, 10)) +>X7 : Symbol(X7, Decl(inferTypesWithExtends1.ts, 58, 31)) +>a : Symbol(a, Decl(inferTypesWithExtends1.ts, 66, 17)) +>b : Symbol(b, Decl(inferTypesWithExtends1.ts, 66, 25)) + +type X7_T2 = X7<{ a: 1, b: 2 }>; // ["number", 1 | 2] +>X7_T2 : Symbol(X7_T2, Decl(inferTypesWithExtends1.ts, 66, 36)) +>X7 : Symbol(X7, Decl(inferTypesWithExtends1.ts, 58, 31)) +>a : Symbol(a, Decl(inferTypesWithExtends1.ts, 67, 17)) +>b : Symbol(b, Decl(inferTypesWithExtends1.ts, 67, 23)) + +type X7_T3 = X7<{ a: object, b: object }>; // never +>X7_T3 : Symbol(X7_T3, Decl(inferTypesWithExtends1.ts, 67, 32)) +>X7 : Symbol(X7, Decl(inferTypesWithExtends1.ts, 58, 31)) +>a : Symbol(a, Decl(inferTypesWithExtends1.ts, 68, 17)) +>b : Symbol(b, Decl(inferTypesWithExtends1.ts, 68, 28)) + +type X7_T4 = X7<{ a: "a", b: 1 }>; // never +>X7_T4 : Symbol(X7_T4, Decl(inferTypesWithExtends1.ts, 68, 42)) +>X7 : Symbol(X7, Decl(inferTypesWithExtends1.ts, 58, 31)) +>a : Symbol(a, Decl(inferTypesWithExtends1.ts, 69, 17)) +>b : Symbol(b, Decl(inferTypesWithExtends1.ts, 69, 25)) + +// infer twice with missing second constraint (same behavior as class/interface) +type X8 = +>X8 : Symbol(X8, Decl(inferTypesWithExtends1.ts, 69, 34)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 72, 8)) + + T extends { a: infer U extends string, b: infer U } ? ["string", U] : +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 72, 8)) +>a : Symbol(a, Decl(inferTypesWithExtends1.ts, 73, 15)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 73, 24), Decl(inferTypesWithExtends1.ts, 73, 51)) +>b : Symbol(b, Decl(inferTypesWithExtends1.ts, 73, 42)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 73, 24), Decl(inferTypesWithExtends1.ts, 73, 51)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 73, 24), Decl(inferTypesWithExtends1.ts, 73, 51)) + + T extends { a: infer U extends number, b: infer U } ? ["number", U] : +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 72, 8)) +>a : Symbol(a, Decl(inferTypesWithExtends1.ts, 74, 15)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 74, 24), Decl(inferTypesWithExtends1.ts, 74, 51)) +>b : Symbol(b, Decl(inferTypesWithExtends1.ts, 74, 42)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 74, 24), Decl(inferTypesWithExtends1.ts, 74, 51)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 74, 24), Decl(inferTypesWithExtends1.ts, 74, 51)) + + never; + +type X8_T1 = X8<{ a: "a", b: "b" }>; // ["string", "a" | "b"] +>X8_T1 : Symbol(X8_T1, Decl(inferTypesWithExtends1.ts, 75, 10)) +>X8 : Symbol(X8, Decl(inferTypesWithExtends1.ts, 69, 34)) +>a : Symbol(a, Decl(inferTypesWithExtends1.ts, 77, 17)) +>b : Symbol(b, Decl(inferTypesWithExtends1.ts, 77, 25)) + +type X8_T2 = X8<{ a: 1, b: 2 }>; // ["number", 1 | 2] +>X8_T2 : Symbol(X8_T2, Decl(inferTypesWithExtends1.ts, 77, 36)) +>X8 : Symbol(X8, Decl(inferTypesWithExtends1.ts, 69, 34)) +>a : Symbol(a, Decl(inferTypesWithExtends1.ts, 78, 17)) +>b : Symbol(b, Decl(inferTypesWithExtends1.ts, 78, 23)) + +type X8_T3 = X8<{ a: object, b: object }>; // never +>X8_T3 : Symbol(X8_T3, Decl(inferTypesWithExtends1.ts, 78, 32)) +>X8 : Symbol(X8, Decl(inferTypesWithExtends1.ts, 69, 34)) +>a : Symbol(a, Decl(inferTypesWithExtends1.ts, 79, 17)) +>b : Symbol(b, Decl(inferTypesWithExtends1.ts, 79, 28)) + +type X8_T4 = X8<{ a: "a", b: 1 }>; // never +>X8_T4 : Symbol(X8_T4, Decl(inferTypesWithExtends1.ts, 79, 42)) +>X8 : Symbol(X8, Decl(inferTypesWithExtends1.ts, 69, 34)) +>a : Symbol(a, Decl(inferTypesWithExtends1.ts, 80, 17)) +>b : Symbol(b, Decl(inferTypesWithExtends1.ts, 80, 25)) + +// infer twice with missing first constraint (same behavior as class/interface) +type X9 = +>X9 : Symbol(X9, Decl(inferTypesWithExtends1.ts, 80, 34)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 83, 8)) + + T extends { a: infer U, b: infer U extends string } ? ["string", U] : +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 83, 8)) +>a : Symbol(a, Decl(inferTypesWithExtends1.ts, 84, 15)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 84, 24), Decl(inferTypesWithExtends1.ts, 84, 36)) +>b : Symbol(b, Decl(inferTypesWithExtends1.ts, 84, 27)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 84, 24), Decl(inferTypesWithExtends1.ts, 84, 36)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 84, 24), Decl(inferTypesWithExtends1.ts, 84, 36)) + + T extends { a: infer U, b: infer U extends number } ? ["number", U] : +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 83, 8)) +>a : Symbol(a, Decl(inferTypesWithExtends1.ts, 85, 15)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 85, 24), Decl(inferTypesWithExtends1.ts, 85, 36)) +>b : Symbol(b, Decl(inferTypesWithExtends1.ts, 85, 27)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 85, 24), Decl(inferTypesWithExtends1.ts, 85, 36)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 85, 24), Decl(inferTypesWithExtends1.ts, 85, 36)) + + never; + +type X9_T1 = X9<{ a: "a", b: "b" }>; // ["string", "a" | "b"] +>X9_T1 : Symbol(X9_T1, Decl(inferTypesWithExtends1.ts, 86, 10)) +>X9 : Symbol(X9, Decl(inferTypesWithExtends1.ts, 80, 34)) +>a : Symbol(a, Decl(inferTypesWithExtends1.ts, 88, 17)) +>b : Symbol(b, Decl(inferTypesWithExtends1.ts, 88, 25)) + +type X9_T2 = X9<{ a: 1, b: 2 }>; // ["number", 1 | 2] +>X9_T2 : Symbol(X9_T2, Decl(inferTypesWithExtends1.ts, 88, 36)) +>X9 : Symbol(X9, Decl(inferTypesWithExtends1.ts, 80, 34)) +>a : Symbol(a, Decl(inferTypesWithExtends1.ts, 89, 17)) +>b : Symbol(b, Decl(inferTypesWithExtends1.ts, 89, 23)) + +type X9_T3 = X9<{ a: object, b: object }>; // never +>X9_T3 : Symbol(X9_T3, Decl(inferTypesWithExtends1.ts, 89, 32)) +>X9 : Symbol(X9, Decl(inferTypesWithExtends1.ts, 80, 34)) +>a : Symbol(a, Decl(inferTypesWithExtends1.ts, 90, 17)) +>b : Symbol(b, Decl(inferTypesWithExtends1.ts, 90, 28)) + +type X9_T4 = X9<{ a: "a", b: 1 }>; // never +>X9_T4 : Symbol(X9_T4, Decl(inferTypesWithExtends1.ts, 90, 42)) +>X9 : Symbol(X9, Decl(inferTypesWithExtends1.ts, 80, 34)) +>a : Symbol(a, Decl(inferTypesWithExtends1.ts, 91, 17)) +>b : Symbol(b, Decl(inferTypesWithExtends1.ts, 91, 25)) + +// Speculative lookahead for `infer T extends U ?` +type X10 = T extends (infer U extends number ? 1 : 0) ? 1 : 0; // ok, parsed as conditional +>X10 : Symbol(X10, Decl(inferTypesWithExtends1.ts, 91, 34)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 94, 9)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 94, 9)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 94, 30)) + +type X10_Y1 = X10; +>X10_Y1 : Symbol(X10_Y1, Decl(inferTypesWithExtends1.ts, 94, 65)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 95, 12)) +>X10 : Symbol(X10, Decl(inferTypesWithExtends1.ts, 91, 34)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 95, 12)) + +type X10_T1_T1 = X10_Y1; +>X10_T1_T1 : Symbol(X10_T1_T1, Decl(inferTypesWithExtends1.ts, 95, 47)) +>X10_Y1 : Symbol(X10_Y1, Decl(inferTypesWithExtends1.ts, 94, 65)) + +type X11 = T extends ((infer U) extends number ? 1 : 0) ? 1 : 0; // ok, parsed as conditional +>X11 : Symbol(X11, Decl(inferTypesWithExtends1.ts, 96, 32)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 98, 9)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 98, 9)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 98, 31)) + +type X12 = T extends (infer U extends number) ? 1 : 0; // ok, parsed as `infer..extends` (no trailing `?`) +>X12 : Symbol(X12, Decl(inferTypesWithExtends1.ts, 98, 67)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 99, 9)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 99, 9)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 99, 30)) + +type X13 = T extends infer U extends number ? 1 : 0; // ok, parsed as `infer..extends` (conditional types not allowed in 'extends type') +>X13 : Symbol(X13, Decl(inferTypesWithExtends1.ts, 99, 57)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 100, 9)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 100, 9)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 100, 29)) + +type X14 = T extends keyof infer U extends number ? 1 : 0; // ok, parsed as `infer..extends` (precedence wouldn't have parsed the `?` as part of a type operator) +>X14 : Symbol(X14, Decl(inferTypesWithExtends1.ts, 100, 55)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 101, 9)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 101, 9)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 101, 35)) + +type X15 = T extends { [P in infer U extends keyof T ? 1 : 0]: 1; } ? 1 : 0; // ok, parsed as conditional +>X15 : Symbol(X15, Decl(inferTypesWithExtends1.ts, 101, 61)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 102, 9)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 102, 9)) +>P : Symbol(P, Decl(inferTypesWithExtends1.ts, 102, 27)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 102, 37)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 102, 9)) + +type X16 = T extends { [P in infer U extends keyof T]: 1; } ? 1 : 0; // ok, parsed as `infer..extends` (no trailing `?`) +>X16 : Symbol(X16, Decl(inferTypesWithExtends1.ts, 102, 79)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 103, 9)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 103, 9)) +>P : Symbol(P, Decl(inferTypesWithExtends1.ts, 103, 27)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 103, 37)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 103, 9)) + +type X17 = T extends { [P in keyof T as infer U extends P ? 1 : 0]: 1; } ? 1 : 0; // ok, parsed as conditional +>X17 : Symbol(X17, Decl(inferTypesWithExtends1.ts, 103, 71)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 104, 9)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 104, 9)) +>P : Symbol(P, Decl(inferTypesWithExtends1.ts, 104, 27)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 104, 9)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 104, 48)) +>P : Symbol(P, Decl(inferTypesWithExtends1.ts, 104, 27)) + +type X18 = T extends { [P in keyof T as infer U extends P]: 1; } ? 1 : 0; // ok, parsed as `infer..extends` (no trailing `?`) +>X18 : Symbol(X18, Decl(inferTypesWithExtends1.ts, 104, 84)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 105, 9)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 105, 9)) +>P : Symbol(P, Decl(inferTypesWithExtends1.ts, 105, 27)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 105, 9)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 105, 48)) +>P : Symbol(P, Decl(inferTypesWithExtends1.ts, 105, 27)) + +type X19 = T extends (infer U extends number) ? [T, U] : never; +>X19 : Symbol(X19, Decl(inferTypesWithExtends1.ts, 105, 76)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 107, 9)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 107, 9)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 107, 54)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 107, 9)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 107, 54)) + +type X19_T1 = X19<"a">; // never +>X19_T1 : Symbol(X19_T1, Decl(inferTypesWithExtends1.ts, 107, 90)) +>X19 : Symbol(X19, Decl(inferTypesWithExtends1.ts, 105, 76)) + +type X19_T2 = X19<1>; // [1, 1] +>X19_T2 : Symbol(X19_T2, Decl(inferTypesWithExtends1.ts, 108, 23)) +>X19 : Symbol(X19, Decl(inferTypesWithExtends1.ts, 105, 76)) + +type X19_T3 = X19<1 | "a">; // [1, 1] +>X19_T3 : Symbol(X19_T3, Decl(inferTypesWithExtends1.ts, 109, 21)) +>X19 : Symbol(X19, Decl(inferTypesWithExtends1.ts, 105, 76)) + +type X20 = T extends (infer U extends number) ? T extends (infer V extends U) ? [T, U, V] : never : never; +>X20 : Symbol(X20, Decl(inferTypesWithExtends1.ts, 110, 27)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 112, 9)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 112, 9)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 112, 30)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 112, 9)) +>V : Symbol(V, Decl(inferTypesWithExtends1.ts, 112, 67)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 112, 30)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 112, 9)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 112, 30)) +>V : Symbol(V, Decl(inferTypesWithExtends1.ts, 112, 67)) + +type X20_T1 = X20<1 | "a">; // [1, 1, 1] +>X20_T1 : Symbol(X20_T1, Decl(inferTypesWithExtends1.ts, 112, 109)) +>X20 : Symbol(X20, Decl(inferTypesWithExtends1.ts, 110, 27)) + +type X21 = T extends (infer U extends N) ? [T, U] : never; +>X21 : Symbol(X21, Decl(inferTypesWithExtends1.ts, 113, 27)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 115, 9)) +>N : Symbol(N, Decl(inferTypesWithExtends1.ts, 115, 11)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 115, 9)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 115, 48)) +>N : Symbol(N, Decl(inferTypesWithExtends1.ts, 115, 11)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 115, 9)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 115, 48)) + +type X21_T1 = X21<1, 1>; // [1, 1] +>X21_T1 : Symbol(X21_T1, Decl(inferTypesWithExtends1.ts, 115, 79)) +>X21 : Symbol(X21, Decl(inferTypesWithExtends1.ts, 113, 27)) + +type X21_T2 = X21<1 | "a", 1>; // [1, 1] +>X21_T2 : Symbol(X21_T2, Decl(inferTypesWithExtends1.ts, 116, 24)) +>X21 : Symbol(X21, Decl(inferTypesWithExtends1.ts, 113, 27)) + +type X21_T3 = X21<1 | 2, 1>; // [1, 1] +>X21_T3 : Symbol(X21_T3, Decl(inferTypesWithExtends1.ts, 117, 30)) +>X21 : Symbol(X21, Decl(inferTypesWithExtends1.ts, 113, 27)) + +type X21_T4 = X21<1 | 2, 2 | 3>; // [2, 2] +>X21_T4 : Symbol(X21_T4, Decl(inferTypesWithExtends1.ts, 118, 28)) +>X21 : Symbol(X21, Decl(inferTypesWithExtends1.ts, 113, 27)) + +type X21_T5 = X21<1 | 2, 3>; // never +>X21_T5 : Symbol(X21_T5, Decl(inferTypesWithExtends1.ts, 119, 32)) +>X21 : Symbol(X21, Decl(inferTypesWithExtends1.ts, 113, 27)) + +// from mongoose +type IfEquals = (() => T extends X ? 1 : 2) extends () => T extends Y ? 1 : 2 ? A : B; +>IfEquals : Symbol(IfEquals, Decl(inferTypesWithExtends1.ts, 120, 28)) +>X : Symbol(X, Decl(inferTypesWithExtends1.ts, 123, 14)) +>Y : Symbol(Y, Decl(inferTypesWithExtends1.ts, 123, 16)) +>A : Symbol(A, Decl(inferTypesWithExtends1.ts, 123, 19)) +>B : Symbol(B, Decl(inferTypesWithExtends1.ts, 123, 22)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 123, 30)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 123, 30)) +>X : Symbol(X, Decl(inferTypesWithExtends1.ts, 123, 14)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 123, 68)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 123, 68)) +>Y : Symbol(Y, Decl(inferTypesWithExtends1.ts, 123, 16)) +>A : Symbol(A, Decl(inferTypesWithExtends1.ts, 123, 19)) +>B : Symbol(B, Decl(inferTypesWithExtends1.ts, 123, 22)) + +declare const x1: () => (T extends infer U extends number ? 1 : 0); +>x1 : Symbol(x1, Decl(inferTypesWithExtends1.ts, 125, 13)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 125, 19)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 125, 19)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 125, 43)) + +function f1() { +>f1 : Symbol(f1, Decl(inferTypesWithExtends1.ts, 125, 70)) + + return x1; +>x1 : Symbol(x1, Decl(inferTypesWithExtends1.ts, 125, 13)) +} + +type ExpectNumber = T; +>ExpectNumber : Symbol(ExpectNumber, Decl(inferTypesWithExtends1.ts, 128, 1)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 130, 18)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 130, 18)) + +declare const x2: () => (T extends ExpectNumber ? 1 : 0); +>x2 : Symbol(x2, Decl(inferTypesWithExtends1.ts, 131, 13)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 131, 19)) +>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 131, 19)) +>ExpectNumber : Symbol(ExpectNumber, Decl(inferTypesWithExtends1.ts, 128, 1)) +>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 131, 56)) + +function f2() { +>f2 : Symbol(f2, Decl(inferTypesWithExtends1.ts, 131, 69)) + + return x2; +>x2 : Symbol(x2, Decl(inferTypesWithExtends1.ts, 131, 13)) +} diff --git a/tests/baselines/reference/inferTypesWithExtends1.types b/tests/baselines/reference/inferTypesWithExtends1.types new file mode 100644 index 0000000000000..b26386d0f4902 --- /dev/null +++ b/tests/baselines/reference/inferTypesWithExtends1.types @@ -0,0 +1,331 @@ +=== tests/cases/conformance/types/conditional/inferTypesWithExtends1.ts === +// infer to tuple element +type X1 = +>X1 : X1 + + T extends [infer U extends string] ? ["string", U] : + T extends [infer U extends number] ? ["number", U] : + never; + +type X1_T1 = X1<["a"]>; // ["string", "a"] +>X1_T1 : ["string", "a"] + +type X1_T2 = X1<[1]>; // ["number", 1] +>X1_T2 : ["number", 1] + +type X1_T3 = X1<[object]>; // never +>X1_T3 : never + +// infer to argument +type X2 void> = +>X2 : X2 +>args : any[] + + T extends (a: infer U extends string) => void ? ["string", U] : +>a : U + + T extends (a: infer U extends number) => void ? ["number", U] : +>a : U + + never; + +type X2_T1 = X2<(a: "a") => void>; // ["string", "a"] +>X2_T1 : ["string", "a"] +>a : "a" + +type X2_T2 = X2<(a: 1) => void>; // ["number", 1] +>X2_T2 : ["number", 1] +>a : 1 + +type X2_T3 = X2<(a: object) => void>; // never +>X2_T3 : never +>a : object + +// infer to return type +type X3 any> = +>X3 : X3 +>args : any[] + + T extends (...args: any[]) => (infer U extends string) ? ["string", U] : +>args : any[] + + T extends (...args: any[]) => (infer U extends number) ? ["number", U] : +>args : any[] + + never; + +type X3_T1 = X3<() => "a">; // ["string", "a"] +>X3_T1 : ["string", "a"] + +type X3_T2 = X3<() => 1>; // ["number", 1] +>X3_T2 : ["number", 1] + +type X3_T3 = X3<() => object>; // never +>X3_T3 : never + +// infer to instance type +type X4 any> = +>X4 : X4 +>args : any[] + + T extends new (...args: any[]) => (infer U extends { a: string }) ? ["string", U] : +>args : any[] +>a : string + + T extends new (...args: any[]) => (infer U extends { a: number }) ? ["number", U] : +>args : any[] +>a : number + + never; + +type X4_T1 = X4 { a: "a" }>; // ["string", { a: "a" }] +>X4_T1 : ["string", { a: "a"; }] +>a : "a" + +type X4_T2 = X4 { a: 1 }>; // ["number", { a: 1 }] +>X4_T2 : ["number", { a: 1; }] +>a : 1 + +type X4_T3 = X4 { a: object }>; // never +>X4_T3 : never +>a : object + +// infer to type argument +type X5 = +>X5 : X5 + + T extends Promise ? ["string", U] : + T extends Promise ? ["number", U] : + never; + +type X5_T1 = X5>; // ["string", "a" | "b"] +>X5_T1 : ["string", "a" | "b"] + +type X5_T2 = X5>; // ["number", 1 | 2] +>X5_T2 : ["number", 1 | 2] + +type X5_T3 = X5>; // never +>X5_T3 : never + +// infer to property type +type X6 = +>X6 : X6 + + T extends { a: infer U extends string } ? ["string", U] : +>a : U + + T extends { a: infer U extends number } ? ["number", U] : +>a : U + + never; + +type X6_T1 = X6<{ a: "a" }>; // ["string", "a"] +>X6_T1 : ["string", "a"] +>a : "a" + +type X6_T2 = X6<{ a: 1 }>; // ["number", 1] +>X6_T2 : ["number", 1] +>a : 1 + +type X6_T3 = X6<{ a: object }>; // never +>X6_T3 : never +>a : object + +// infer twice with same constraint +type X7 = +>X7 : X7 + + T extends { a: infer U extends string, b: infer U extends string } ? ["string", U] : +>a : U +>b : U + + T extends { a: infer U extends number, b: infer U extends number } ? ["number", U] : +>a : U +>b : U + + never; + +type X7_T1 = X7<{ a: "a", b: "b" }>; // ["string", "a" | "b"] +>X7_T1 : ["string", "a" | "b"] +>a : "a" +>b : "b" + +type X7_T2 = X7<{ a: 1, b: 2 }>; // ["number", 1 | 2] +>X7_T2 : ["number", 1 | 2] +>a : 1 +>b : 2 + +type X7_T3 = X7<{ a: object, b: object }>; // never +>X7_T3 : never +>a : object +>b : object + +type X7_T4 = X7<{ a: "a", b: 1 }>; // never +>X7_T4 : never +>a : "a" +>b : 1 + +// infer twice with missing second constraint (same behavior as class/interface) +type X8 = +>X8 : X8 + + T extends { a: infer U extends string, b: infer U } ? ["string", U] : +>a : U +>b : U + + T extends { a: infer U extends number, b: infer U } ? ["number", U] : +>a : U +>b : U + + never; + +type X8_T1 = X8<{ a: "a", b: "b" }>; // ["string", "a" | "b"] +>X8_T1 : ["string", "a" | "b"] +>a : "a" +>b : "b" + +type X8_T2 = X8<{ a: 1, b: 2 }>; // ["number", 1 | 2] +>X8_T2 : ["number", 1 | 2] +>a : 1 +>b : 2 + +type X8_T3 = X8<{ a: object, b: object }>; // never +>X8_T3 : never +>a : object +>b : object + +type X8_T4 = X8<{ a: "a", b: 1 }>; // never +>X8_T4 : never +>a : "a" +>b : 1 + +// infer twice with missing first constraint (same behavior as class/interface) +type X9 = +>X9 : X9 + + T extends { a: infer U, b: infer U extends string } ? ["string", U] : +>a : U +>b : U + + T extends { a: infer U, b: infer U extends number } ? ["number", U] : +>a : U +>b : U + + never; + +type X9_T1 = X9<{ a: "a", b: "b" }>; // ["string", "a" | "b"] +>X9_T1 : ["string", "a" | "b"] +>a : "a" +>b : "b" + +type X9_T2 = X9<{ a: 1, b: 2 }>; // ["number", 1 | 2] +>X9_T2 : ["number", 1 | 2] +>a : 1 +>b : 2 + +type X9_T3 = X9<{ a: object, b: object }>; // never +>X9_T3 : never +>a : object +>b : object + +type X9_T4 = X9<{ a: "a", b: 1 }>; // never +>X9_T4 : never +>a : "a" +>b : 1 + +// Speculative lookahead for `infer T extends U ?` +type X10 = T extends (infer U extends number ? 1 : 0) ? 1 : 0; // ok, parsed as conditional +>X10 : X10 + +type X10_Y1 = X10; +>X10_Y1 : X10_Y1 + +type X10_T1_T1 = X10_Y1; +>X10_T1_T1 : 0 + +type X11 = T extends ((infer U) extends number ? 1 : 0) ? 1 : 0; // ok, parsed as conditional +>X11 : X11 + +type X12 = T extends (infer U extends number) ? 1 : 0; // ok, parsed as `infer..extends` (no trailing `?`) +>X12 : X12 + +type X13 = T extends infer U extends number ? 1 : 0; // ok, parsed as `infer..extends` (conditional types not allowed in 'extends type') +>X13 : X13 + +type X14 = T extends keyof infer U extends number ? 1 : 0; // ok, parsed as `infer..extends` (precedence wouldn't have parsed the `?` as part of a type operator) +>X14 : X14 + +type X15 = T extends { [P in infer U extends keyof T ? 1 : 0]: 1; } ? 1 : 0; // ok, parsed as conditional +>X15 : X15 + +type X16 = T extends { [P in infer U extends keyof T]: 1; } ? 1 : 0; // ok, parsed as `infer..extends` (no trailing `?`) +>X16 : X16 + +type X17 = T extends { [P in keyof T as infer U extends P ? 1 : 0]: 1; } ? 1 : 0; // ok, parsed as conditional +>X17 : X17 + +type X18 = T extends { [P in keyof T as infer U extends P]: 1; } ? 1 : 0; // ok, parsed as `infer..extends` (no trailing `?`) +>X18 : X18 + +type X19 = T extends (infer U extends number) ? [T, U] : never; +>X19 : X19 + +type X19_T1 = X19<"a">; // never +>X19_T1 : never + +type X19_T2 = X19<1>; // [1, 1] +>X19_T2 : [1, 1] + +type X19_T3 = X19<1 | "a">; // [1, 1] +>X19_T3 : [1, 1] + +type X20 = T extends (infer U extends number) ? T extends (infer V extends U) ? [T, U, V] : never : never; +>X20 : X20 + +type X20_T1 = X20<1 | "a">; // [1, 1, 1] +>X20_T1 : [1, 1, 1] + +type X21 = T extends (infer U extends N) ? [T, U] : never; +>X21 : X21 + +type X21_T1 = X21<1, 1>; // [1, 1] +>X21_T1 : [1, 1] + +type X21_T2 = X21<1 | "a", 1>; // [1, 1] +>X21_T2 : [1, 1] + +type X21_T3 = X21<1 | 2, 1>; // [1, 1] +>X21_T3 : [1, 1] + +type X21_T4 = X21<1 | 2, 2 | 3>; // [2, 2] +>X21_T4 : [2, 2] + +type X21_T5 = X21<1 | 2, 3>; // never +>X21_T5 : never + +// from mongoose +type IfEquals = (() => T extends X ? 1 : 2) extends () => T extends Y ? 1 : 2 ? A : B; +>IfEquals : IfEquals + +declare const x1: () => (T extends infer U extends number ? 1 : 0); +>x1 : () => T extends infer U extends number ? 1 : 0 + +function f1() { +>f1 : () => () => T extends infer U extends number ? 1 : 0 + + return x1; +>x1 : () => T extends infer U extends number ? 1 : 0 +} + +type ExpectNumber = T; +>ExpectNumber : T + +declare const x2: () => (T extends ExpectNumber ? 1 : 0); +>x2 : () => T extends infer U extends number ? 1 : 0 + +function f2() { +>f2 : () => () => T extends infer U extends number ? 1 : 0 + + return x2; +>x2 : () => T extends infer U extends number ? 1 : 0 +} diff --git a/tests/baselines/reference/inferTypesWithExtends2.errors.txt b/tests/baselines/reference/inferTypesWithExtends2.errors.txt new file mode 100644 index 0000000000000..395e7e60fed0c --- /dev/null +++ b/tests/baselines/reference/inferTypesWithExtends2.errors.txt @@ -0,0 +1,21 @@ +tests/cases/conformance/types/conditional/inferTypesWithExtends2.ts(3,26): error TS2838: All declarations of 'U' must have identical constraints. +tests/cases/conformance/types/conditional/inferTypesWithExtends2.ts(3,53): error TS2838: All declarations of 'U' must have identical constraints. +tests/cases/conformance/types/conditional/inferTypesWithExtends2.ts(8,48): error TS2304: Cannot find name 'U'. + + +==== tests/cases/conformance/types/conditional/inferTypesWithExtends2.ts (3 errors) ==== + // infer twice with different constraints (same behavior as class/interface) + type X1 = + T extends { a: infer U extends string, b: infer U extends number } ? U : + ~ +!!! error TS2838: All declarations of 'U' must have identical constraints. + ~ +!!! error TS2838: All declarations of 'U' must have identical constraints. + never; + + // infer cannot reference type params in same 'extends' clause + type X2 = + T extends { a: infer U, b: infer V extends U } ? [U, V] : + ~ +!!! error TS2304: Cannot find name 'U'. + never; \ No newline at end of file diff --git a/tests/baselines/reference/inferTypesWithExtends2.js b/tests/baselines/reference/inferTypesWithExtends2.js new file mode 100644 index 0000000000000..17bba39edad56 --- /dev/null +++ b/tests/baselines/reference/inferTypesWithExtends2.js @@ -0,0 +1,13 @@ +//// [inferTypesWithExtends2.ts] +// infer twice with different constraints (same behavior as class/interface) +type X1 = + T extends { a: infer U extends string, b: infer U extends number } ? U : + never; + +// infer cannot reference type params in same 'extends' clause +type X2 = + T extends { a: infer U, b: infer V extends U } ? [U, V] : + never; + +//// [inferTypesWithExtends2.js] +"use strict"; diff --git a/tests/baselines/reference/inferTypesWithExtends2.symbols b/tests/baselines/reference/inferTypesWithExtends2.symbols new file mode 100644 index 0000000000000..3c8bf66bf8b7a --- /dev/null +++ b/tests/baselines/reference/inferTypesWithExtends2.symbols @@ -0,0 +1,32 @@ +=== tests/cases/conformance/types/conditional/inferTypesWithExtends2.ts === +// infer twice with different constraints (same behavior as class/interface) +type X1 = +>X1 : Symbol(X1, Decl(inferTypesWithExtends2.ts, 0, 0)) +>T : Symbol(T, Decl(inferTypesWithExtends2.ts, 1, 8)) + + T extends { a: infer U extends string, b: infer U extends number } ? U : +>T : Symbol(T, Decl(inferTypesWithExtends2.ts, 1, 8)) +>a : Symbol(a, Decl(inferTypesWithExtends2.ts, 2, 15)) +>U : Symbol(U, Decl(inferTypesWithExtends2.ts, 2, 24), Decl(inferTypesWithExtends2.ts, 2, 51)) +>b : Symbol(b, Decl(inferTypesWithExtends2.ts, 2, 42)) +>U : Symbol(U, Decl(inferTypesWithExtends2.ts, 2, 24), Decl(inferTypesWithExtends2.ts, 2, 51)) +>U : Symbol(U, Decl(inferTypesWithExtends2.ts, 2, 24), Decl(inferTypesWithExtends2.ts, 2, 51)) + + never; + +// infer cannot reference type params in same 'extends' clause +type X2 = +>X2 : Symbol(X2, Decl(inferTypesWithExtends2.ts, 3, 10)) +>T : Symbol(T, Decl(inferTypesWithExtends2.ts, 6, 8)) + + T extends { a: infer U, b: infer V extends U } ? [U, V] : +>T : Symbol(T, Decl(inferTypesWithExtends2.ts, 6, 8)) +>a : Symbol(a, Decl(inferTypesWithExtends2.ts, 7, 15)) +>U : Symbol(U, Decl(inferTypesWithExtends2.ts, 7, 24)) +>b : Symbol(b, Decl(inferTypesWithExtends2.ts, 7, 27)) +>V : Symbol(V, Decl(inferTypesWithExtends2.ts, 7, 36)) +>U : Symbol(U) +>U : Symbol(U, Decl(inferTypesWithExtends2.ts, 7, 24)) +>V : Symbol(V, Decl(inferTypesWithExtends2.ts, 7, 36)) + + never; diff --git a/tests/baselines/reference/inferTypesWithExtends2.types b/tests/baselines/reference/inferTypesWithExtends2.types new file mode 100644 index 0000000000000..e76936d07028a --- /dev/null +++ b/tests/baselines/reference/inferTypesWithExtends2.types @@ -0,0 +1,20 @@ +=== tests/cases/conformance/types/conditional/inferTypesWithExtends2.ts === +// infer twice with different constraints (same behavior as class/interface) +type X1 = +>X1 : X1 + + T extends { a: infer U extends string, b: infer U extends number } ? U : +>a : U +>b : U + + never; + +// infer cannot reference type params in same 'extends' clause +type X2 = +>X2 : X2 + + T extends { a: infer U, b: infer V extends U } ? [U, V] : +>a : U +>b : V + + never; diff --git a/tests/baselines/reference/infiniteConstraints.types b/tests/baselines/reference/infiniteConstraints.types index 3fa2bb1feedcd..6a3a1a075950c 100644 --- a/tests/baselines/reference/infiniteConstraints.types +++ b/tests/baselines/reference/infiniteConstraints.types @@ -15,7 +15,7 @@ type AProp = T >a : string declare function myBug< ->myBug : (arg: T) => T +>myBug : (arg: T) => T T extends { [K in keyof T]: T[K] extends AProp ? U : never } >(arg: T): T @@ -24,7 +24,7 @@ declare function myBug< const out = myBug({obj1: {a: "test"}}) >out : { obj1: { a: string; }; } >myBug({obj1: {a: "test"}}) : { obj1: { a: string; }; } ->myBug : (arg: T) => T +>myBug : (arg: T) => T >{obj1: {a: "test"}} : { obj1: { a: string; }; } >obj1 : { a: string; } >{a: "test"} : { a: string; } diff --git a/tests/baselines/reference/keyofAndIndexedAccess2.types b/tests/baselines/reference/keyofAndIndexedAccess2.types index 048e11cad674d..9fa1ac46783bb 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess2.types +++ b/tests/baselines/reference/keyofAndIndexedAccess2.types @@ -495,7 +495,7 @@ function fn4() { >'abc' : "abc" let y: ReadonlyArray[K] = 'abc'; ->y : readonly string[][K] +>y : (readonly string[])[K] >'abc' : "abc" } diff --git a/tests/baselines/reference/literalsInComputedProperties1.types b/tests/baselines/reference/literalsInComputedProperties1.types index 8a3e2dbd81acb..d417b9f40990f 100644 --- a/tests/baselines/reference/literalsInComputedProperties1.types +++ b/tests/baselines/reference/literalsInComputedProperties1.types @@ -161,20 +161,20 @@ enum X { >X : X 1 = 1, ->1 : typeof X["1"] +>1 : (typeof X)["1"] >1 : 1 [2] = 2, ->[2] : typeof X["2"] +>[2] : (typeof X)["2"] >2 : 2 >2 : 2 "3" = 3, ->"3" : typeof X["3"] +>"3" : (typeof X)["3"] >3 : 3 ["4"] = 4, ->["4"] : typeof X["4"] +>["4"] : (typeof X)["4"] >"4" : "4" >4 : 4 diff --git a/tests/baselines/reference/negateOperatorWithEnumType.types b/tests/baselines/reference/negateOperatorWithEnumType.types index 32eb5ebbe28ce..2398303496cfb 100644 --- a/tests/baselines/reference/negateOperatorWithEnumType.types +++ b/tests/baselines/reference/negateOperatorWithEnumType.types @@ -8,7 +8,7 @@ enum ENUM1 { A, B, "" }; >ENUM1 : ENUM1 >A : ENUM1.A >B : ENUM1.B ->"" : typeof ENUM1[""] +>"" : (typeof ENUM1)[""] // enum type var var ResultIsNumber1 = -ENUM; @@ -32,7 +32,7 @@ var ResultIsNumber3 = -(ENUM1.B + ENUM1[""]); >ENUM1.B : ENUM1.B >ENUM1 : typeof ENUM1 >B : ENUM1.B ->ENUM1[""] : typeof ENUM1[""] +>ENUM1[""] : (typeof ENUM1)[""] >ENUM1 : typeof ENUM1 >"" : "" diff --git a/tests/baselines/reference/parserEnum5.types b/tests/baselines/reference/parserEnum5.types index f44ae1b57ca21..68450ddbd137f 100644 --- a/tests/baselines/reference/parserEnum5.types +++ b/tests/baselines/reference/parserEnum5.types @@ -6,13 +6,13 @@ enum E2 { a, } enum E3 { a: 1, } >E3 : E3 >a : E3.a ->1 : typeof E3["1"] +>1 : (typeof E3)["1"] enum E1 { a, b: 1, c, d: 2 = 3 } >E1 : E1 >a : E1.a >b : E1.b ->1 : typeof E1["1"] +>1 : (typeof E1)["1"] >c : E1.c >d : E1.d >2 : E1.c diff --git a/tests/baselines/reference/parserEnum7.types b/tests/baselines/reference/parserEnum7.types index efc650edd6757..be81351603c66 100644 --- a/tests/baselines/reference/parserEnum7.types +++ b/tests/baselines/reference/parserEnum7.types @@ -3,7 +3,7 @@ enum E { >E : E 1, 2, 3 ->1 : typeof E["1"] ->2 : typeof E["2"] ->3 : typeof E["3"] +>1 : (typeof E)["1"] +>2 : (typeof E)["2"] +>3 : (typeof E)["3"] } diff --git a/tests/baselines/reference/plusOperatorWithEnumType.types b/tests/baselines/reference/plusOperatorWithEnumType.types index b762505d9f1b1..b19fd30a2090e 100644 --- a/tests/baselines/reference/plusOperatorWithEnumType.types +++ b/tests/baselines/reference/plusOperatorWithEnumType.types @@ -8,7 +8,7 @@ enum ENUM1 { A, B, "" }; >ENUM1 : ENUM1 >A : ENUM1.A >B : ENUM1.B ->"" : typeof ENUM1[""] +>"" : (typeof ENUM1)[""] // enum type var var ResultIsNumber1 = +ENUM; diff --git a/tests/baselines/reference/typeofOperatorWithEnumType.types b/tests/baselines/reference/typeofOperatorWithEnumType.types index 7f50a98e80cae..5b56029194632 100644 --- a/tests/baselines/reference/typeofOperatorWithEnumType.types +++ b/tests/baselines/reference/typeofOperatorWithEnumType.types @@ -8,7 +8,7 @@ enum ENUM1 { A, B, "" }; >ENUM1 : ENUM1 >A : ENUM1.A >B : ENUM1.B ->"" : typeof ENUM1[""] +>"" : (typeof ENUM1)[""] // enum type var var ResultIsString1 = typeof ENUM; diff --git a/tests/baselines/reference/voidOperatorWithEnumType.types b/tests/baselines/reference/voidOperatorWithEnumType.types index c5356d7f3bc7c..ff34ac7ee696e 100644 --- a/tests/baselines/reference/voidOperatorWithEnumType.types +++ b/tests/baselines/reference/voidOperatorWithEnumType.types @@ -8,7 +8,7 @@ enum ENUM1 { A, B, "" }; >ENUM1 : ENUM1 >A : ENUM1.A >B : ENUM1.B ->"" : typeof ENUM1[""] +>"" : (typeof ENUM1)[""] // enum type var var ResultIsAny1 = void ENUM; diff --git a/tests/cases/conformance/types/conditional/inferTypes1.ts b/tests/cases/conformance/types/conditional/inferTypes1.ts index afcb58aff5d64..42e5897dc663f 100644 --- a/tests/cases/conformance/types/conditional/inferTypes1.ts +++ b/tests/cases/conformance/types/conditional/inferTypes1.ts @@ -83,9 +83,9 @@ type T53 = X3<{ a: (x: number) => void, b: (x: string) => void }>; // never type T54 = X3<{ a: (x: number) => void, b: () => void }>; // number type T60 = infer U; // Error -type T61 = infer A extends infer B ? infer C : infer D; // Error +type T61 = (infer A) extends infer B ? infer C : infer D; // Error type T62 = U extends (infer U)[] ? U : U; // Error -type T63 = T extends (infer A extends infer B ? infer C : infer D) ? string : number; +type T63 = T extends ((infer A) extends infer B ? infer C : infer D) ? string : number; type T70 = { x: T }; type T71 = T extends T70 ? T70 : never; diff --git a/tests/cases/conformance/types/conditional/inferTypesWithExtends1.ts b/tests/cases/conformance/types/conditional/inferTypesWithExtends1.ts new file mode 100644 index 0000000000000..64034a62cb667 --- /dev/null +++ b/tests/cases/conformance/types/conditional/inferTypesWithExtends1.ts @@ -0,0 +1,138 @@ +// @strict: true +// @declaration: true + +// infer to tuple element +type X1 = + T extends [infer U extends string] ? ["string", U] : + T extends [infer U extends number] ? ["number", U] : + never; + +type X1_T1 = X1<["a"]>; // ["string", "a"] +type X1_T2 = X1<[1]>; // ["number", 1] +type X1_T3 = X1<[object]>; // never + +// infer to argument +type X2 void> = + T extends (a: infer U extends string) => void ? ["string", U] : + T extends (a: infer U extends number) => void ? ["number", U] : + never; + +type X2_T1 = X2<(a: "a") => void>; // ["string", "a"] +type X2_T2 = X2<(a: 1) => void>; // ["number", 1] +type X2_T3 = X2<(a: object) => void>; // never + +// infer to return type +type X3 any> = + T extends (...args: any[]) => (infer U extends string) ? ["string", U] : + T extends (...args: any[]) => (infer U extends number) ? ["number", U] : + never; + +type X3_T1 = X3<() => "a">; // ["string", "a"] +type X3_T2 = X3<() => 1>; // ["number", 1] +type X3_T3 = X3<() => object>; // never + +// infer to instance type +type X4 any> = + T extends new (...args: any[]) => (infer U extends { a: string }) ? ["string", U] : + T extends new (...args: any[]) => (infer U extends { a: number }) ? ["number", U] : + never; + +type X4_T1 = X4 { a: "a" }>; // ["string", { a: "a" }] +type X4_T2 = X4 { a: 1 }>; // ["number", { a: 1 }] +type X4_T3 = X4 { a: object }>; // never + +// infer to type argument +type X5 = + T extends Promise ? ["string", U] : + T extends Promise ? ["number", U] : + never; + +type X5_T1 = X5>; // ["string", "a" | "b"] +type X5_T2 = X5>; // ["number", 1 | 2] +type X5_T3 = X5>; // never + +// infer to property type +type X6 = + T extends { a: infer U extends string } ? ["string", U] : + T extends { a: infer U extends number } ? ["number", U] : + never; + +type X6_T1 = X6<{ a: "a" }>; // ["string", "a"] +type X6_T2 = X6<{ a: 1 }>; // ["number", 1] +type X6_T3 = X6<{ a: object }>; // never + +// infer twice with same constraint +type X7 = + T extends { a: infer U extends string, b: infer U extends string } ? ["string", U] : + T extends { a: infer U extends number, b: infer U extends number } ? ["number", U] : + never; + +type X7_T1 = X7<{ a: "a", b: "b" }>; // ["string", "a" | "b"] +type X7_T2 = X7<{ a: 1, b: 2 }>; // ["number", 1 | 2] +type X7_T3 = X7<{ a: object, b: object }>; // never +type X7_T4 = X7<{ a: "a", b: 1 }>; // never + +// infer twice with missing second constraint (same behavior as class/interface) +type X8 = + T extends { a: infer U extends string, b: infer U } ? ["string", U] : + T extends { a: infer U extends number, b: infer U } ? ["number", U] : + never; + +type X8_T1 = X8<{ a: "a", b: "b" }>; // ["string", "a" | "b"] +type X8_T2 = X8<{ a: 1, b: 2 }>; // ["number", 1 | 2] +type X8_T3 = X8<{ a: object, b: object }>; // never +type X8_T4 = X8<{ a: "a", b: 1 }>; // never + +// infer twice with missing first constraint (same behavior as class/interface) +type X9 = + T extends { a: infer U, b: infer U extends string } ? ["string", U] : + T extends { a: infer U, b: infer U extends number } ? ["number", U] : + never; + +type X9_T1 = X9<{ a: "a", b: "b" }>; // ["string", "a" | "b"] +type X9_T2 = X9<{ a: 1, b: 2 }>; // ["number", 1 | 2] +type X9_T3 = X9<{ a: object, b: object }>; // never +type X9_T4 = X9<{ a: "a", b: 1 }>; // never + +// Speculative lookahead for `infer T extends U ?` +type X10 = T extends (infer U extends number ? 1 : 0) ? 1 : 0; // ok, parsed as conditional +type X10_Y1 = X10; +type X10_T1_T1 = X10_Y1; + +type X11 = T extends ((infer U) extends number ? 1 : 0) ? 1 : 0; // ok, parsed as conditional +type X12 = T extends (infer U extends number) ? 1 : 0; // ok, parsed as `infer..extends` (no trailing `?`) +type X13 = T extends infer U extends number ? 1 : 0; // ok, parsed as `infer..extends` (conditional types not allowed in 'extends type') +type X14 = T extends keyof infer U extends number ? 1 : 0; // ok, parsed as `infer..extends` (precedence wouldn't have parsed the `?` as part of a type operator) +type X15 = T extends { [P in infer U extends keyof T ? 1 : 0]: 1; } ? 1 : 0; // ok, parsed as conditional +type X16 = T extends { [P in infer U extends keyof T]: 1; } ? 1 : 0; // ok, parsed as `infer..extends` (no trailing `?`) +type X17 = T extends { [P in keyof T as infer U extends P ? 1 : 0]: 1; } ? 1 : 0; // ok, parsed as conditional +type X18 = T extends { [P in keyof T as infer U extends P]: 1; } ? 1 : 0; // ok, parsed as `infer..extends` (no trailing `?`) + +type X19 = T extends (infer U extends number) ? [T, U] : never; +type X19_T1 = X19<"a">; // never +type X19_T2 = X19<1>; // [1, 1] +type X19_T3 = X19<1 | "a">; // [1, 1] + +type X20 = T extends (infer U extends number) ? T extends (infer V extends U) ? [T, U, V] : never : never; +type X20_T1 = X20<1 | "a">; // [1, 1, 1] + +type X21 = T extends (infer U extends N) ? [T, U] : never; +type X21_T1 = X21<1, 1>; // [1, 1] +type X21_T2 = X21<1 | "a", 1>; // [1, 1] +type X21_T3 = X21<1 | 2, 1>; // [1, 1] +type X21_T4 = X21<1 | 2, 2 | 3>; // [2, 2] +type X21_T5 = X21<1 | 2, 3>; // never + +// from mongoose +type IfEquals = (() => T extends X ? 1 : 2) extends () => T extends Y ? 1 : 2 ? A : B; + +declare const x1: () => (T extends infer U extends number ? 1 : 0); +function f1() { + return x1; +} + +type ExpectNumber = T; +declare const x2: () => (T extends ExpectNumber ? 1 : 0); +function f2() { + return x2; +} \ No newline at end of file diff --git a/tests/cases/conformance/types/conditional/inferTypesWithExtends2.ts b/tests/cases/conformance/types/conditional/inferTypesWithExtends2.ts new file mode 100644 index 0000000000000..98e99a55562ae --- /dev/null +++ b/tests/cases/conformance/types/conditional/inferTypesWithExtends2.ts @@ -0,0 +1,11 @@ +// @strict: true + +// infer twice with different constraints (same behavior as class/interface) +type X1 = + T extends { a: infer U extends string, b: infer U extends number } ? U : + never; + +// infer cannot reference type params in same 'extends' clause +type X2 = + T extends { a: infer U, b: infer V extends U } ? [U, V] : + never; \ No newline at end of file diff --git a/tests/cases/fourslash/refactorExtractType48.ts b/tests/cases/fourslash/refactorExtractType48.ts index d10af6bb6379e..afe41c716527e 100644 --- a/tests/cases/fourslash/refactorExtractType48.ts +++ b/tests/cases/fourslash/refactorExtractType48.ts @@ -1,13 +1,13 @@ /// -//// type Crazy = /*a*/T extends [infer P, (infer R extends string ? string : never)] ? P & R : string/*b*/; +//// type Crazy = /*a*/T extends [infer P, ((infer R) extends string ? string : never)] ? P & R : string/*b*/; goTo.select("a", "b"); edit.applyRefactor({ refactorName: "Extract type", actionName: "Extract to type alias", actionDescription: "Extract to type alias", - newContent: `type /*RENAME*/NewType = T extends [infer P, (infer R extends string ? string : never)] ? P & R : string; + newContent: `type /*RENAME*/NewType = T extends [infer P, ((infer R) extends string ? string : never)] ? P & R : string; type Crazy = NewType;`, });