diff --git a/packages/typescript/src/enums/objectFlags.enum.ts b/packages/typescript/src/enums/objectFlags.enum.ts index 63ed4e4d3c93f..428cf7fbf4992 100644 --- a/packages/typescript/src/enums/objectFlags.enum.ts +++ b/packages/typescript/src/enums/objectFlags.enum.ts @@ -42,6 +42,7 @@ export enum ObjectFlags { IsGenericObjectType = 1 << 23, IsGenericIndexType = 1 << 24, IsGenericType = IsGenericObjectType | IsGenericIndexType, + IsNarrowingType = 1 << 31, ContainsIntersections = 1 << 25, IsUnknownLikeUnionComputed = 1 << 26, IsUnknownLikeUnion = 1 << 27, diff --git a/packages/typescript/src/enums/objectFlags.ts b/packages/typescript/src/enums/objectFlags.ts index 1c17a70d5f797..6135b66c8df35 100644 --- a/packages/typescript/src/enums/objectFlags.ts +++ b/packages/typescript/src/enums/objectFlags.ts @@ -42,6 +42,7 @@ export var ObjectFlags: any; ObjectFlags[ObjectFlags["IsGenericObjectType"] = 8388608] = "IsGenericObjectType"; ObjectFlags[ObjectFlags["IsGenericIndexType"] = 16777216] = "IsGenericIndexType"; ObjectFlags[ObjectFlags["IsGenericType"] = 25165824] = "IsGenericType"; + ObjectFlags[ObjectFlags["IsNarrowingType"] = -2147483648] = "IsNarrowingType"; ObjectFlags[ObjectFlags["ContainsIntersections"] = 33554432] = "ContainsIntersections"; ObjectFlags[ObjectFlags["IsUnknownLikeUnionComputed"] = 67108864] = "IsUnknownLikeUnionComputed"; ObjectFlags[ObjectFlags["IsUnknownLikeUnion"] = 134217728] = "IsUnknownLikeUnion"; diff --git a/tools/scripts/tsc/ast.json b/tools/scripts/tsc/ast.json index d15f18aa35b81..e6a66bc76d8fd 100644 --- a/tools/scripts/tsc/ast.json +++ b/tools/scripts/tsc/ast.json @@ -3036,6 +3036,16 @@ { "name": "WhenFalse", "type": "Expression" + }, + { + "name": "FlowNodeWhenTrue", + "type": "*FlowNode", + "goOnly": true + }, + { + "name": "FlowNodeWhenFalse", + "type": "*FlowNode", + "goOnly": true } ], "arena": true diff --git a/tsc/internal/api/enum_values_generated.go b/tsc/internal/api/enum_values_generated.go index 8a6c2d8b92f21..a531c1a0cb78d 100644 --- a/tsc/internal/api/enum_values_generated.go +++ b/tsc/internal/api/enum_values_generated.go @@ -243,6 +243,7 @@ func main() { "IsGenericObjectType": toInt32(checker.ObjectFlagsIsGenericObjectType), "IsGenericIndexType": toInt32(checker.ObjectFlagsIsGenericIndexType), "IsGenericType": toInt32(checker.ObjectFlagsIsGenericType), + "IsNarrowingType": toInt32(checker.ObjectFlagsIsNarrowingType), "ContainsIntersections": toInt32(checker.ObjectFlagsContainsIntersections), "IsUnknownLikeUnionComputed": toInt32(checker.ObjectFlagsIsUnknownLikeUnionComputed), "IsUnknownLikeUnion": toInt32(checker.ObjectFlagsIsUnknownLikeUnion), diff --git a/tsc/internal/ast/ast_generated.go b/tsc/internal/ast/ast_generated.go index ea7b17ee4cb47..91f6373b82a67 100644 --- a/tsc/internal/ast/ast_generated.go +++ b/tsc/internal/ast/ast_generated.go @@ -4095,11 +4095,13 @@ func IsSatisfiesExpression(node *Node) bool { type ConditionalExpression struct { ExpressionBase CompositeBase - Condition *Expression - QuestionToken *QuestionToken - WhenTrue *Expression - ColonToken *ColonToken - WhenFalse *Expression + Condition *Expression + QuestionToken *QuestionToken + WhenTrue *Expression + ColonToken *ColonToken + WhenFalse *Expression + FlowNodeWhenTrue *FlowNode + FlowNodeWhenFalse *FlowNode } func (f *NodeFactory) NewConditionalExpression(condition *Expression, questionToken *QuestionToken, whenTrue *Expression, colonToken *ColonToken, whenFalse *Expression) *Node { diff --git a/tsc/internal/binder/binder.go b/tsc/internal/binder/binder.go index e20bbaea3ce0b..8341e72763b72 100644 --- a/tsc/internal/binder/binder.go +++ b/tsc/internal/binder/binder.go @@ -69,6 +69,7 @@ type Binder struct { emitFlags ast.NodeFlags seenThisKeyword bool hasExplicitReturn bool + inReturnPosition bool hasFlowEffects bool inAssignmentPattern bool seenParseError bool @@ -1483,6 +1484,10 @@ func (b *Binder) bindContainer(node *ast.Node, containerFlags ContainerFlags) { saveContainer := b.container saveThisContainer := b.thisContainer savedBlockScopeContainer := b.blockScopeContainer + savedInReturnPosition := b.inReturnPosition + if ast.IsArrowFunction(node) && !ast.IsBlock(node.Body()) { + b.inReturnPosition = true + } // Depending on what kind of node this is, we may have to adjust the current container // and block-container. If the current node is a container, then it is automatically // considered the current block-container as well. Also, for containers that we know @@ -1620,6 +1625,7 @@ func (b *Binder) bindContainer(node *ast.Node, containerFlags ContainerFlags) { if ast.IsSourceFile(node) && ast.IsExternalOrCommonJSModule(node.AsSourceFile()) || ast.IsAmbientModule(node) { b.bindCommonJSTypeExports(node.Symbol()) } + b.inReturnPosition = savedInReturnPosition b.container = saveContainer b.thisContainer = saveThisContainer b.blockScopeContainer = savedBlockScopeContainer @@ -1959,7 +1965,10 @@ func (b *Binder) bindIfStatement(node *ast.Node) { } func (b *Binder) bindReturnStatement(node *ast.Node) { + savedInReturnPosition := b.inReturnPosition + b.inReturnPosition = true b.bind(node.Expression()) + b.inReturnPosition = savedInReturnPosition if b.currentReturnTarget != nil { b.addAntecedent(b.currentReturnTarget, b.currentFlow) } @@ -2318,10 +2327,16 @@ func (b *Binder) bindConditionalExpressionFlow(node *ast.Node) { b.hasFlowEffects = false b.bindCondition(expr.Condition, trueLabel, falseLabel) b.currentFlow = b.finishFlowLabel(trueLabel) + if b.inReturnPosition { + expr.FlowNodeWhenTrue = b.currentFlow + } b.bind(expr.QuestionToken) b.bind(expr.WhenTrue) b.addAntecedent(postExpressionLabel, b.currentFlow) b.currentFlow = b.finishFlowLabel(falseLabel) + if b.inReturnPosition { + expr.FlowNodeWhenFalse = b.currentFlow + } b.bind(expr.ColonToken) b.bind(expr.WhenFalse) b.addAntecedent(postExpressionLabel, b.currentFlow) diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index d19cf6b214b79..6bc5d4f286f66 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -214,6 +214,13 @@ type InstantiationExpressionKey struct { type SubstitutionTypeKey struct { baseId TypeId constraintId TypeId + isNarrowing bool +} + +type NarrowableReturnTypeKey struct { + typeId TypeId + typeArguments CacheHashKey + nonPrimitiveExtendsTypes CacheHashKey } // ReverseMappedTypeKey @@ -642,6 +649,7 @@ type Checker struct { discriminatedContextualTypes map[DiscriminatedContextualTypeKey]*Type instantiationExpressionTypes map[InstantiationExpressionKey]*Type substitutionTypes map[SubstitutionTypeKey]*Type + narrowableReturnTypes map[NarrowableReturnTypeKey]bool reverseMappedCache map[ReverseMappedTypeKey]*Type reverseHomomorphicMappedCache map[ReverseMappedTypeKey]*Type iterationTypesCache map[IterationTypesKey]IterationTypes @@ -677,6 +685,7 @@ type Checker struct { typeNodeLinks core.LinkStore[*ast.Node, TypeNodeLinks] enumMemberLinks core.LinkStore[*ast.Node, EnumMemberLinks] assertionLinks core.LinkStore[*ast.Node, AssertionLinks] + contextualReturnTypeLinks core.LinkStore[*ast.Node, ContextualReturnTypeLinks] arrayLiteralLinks core.LinkStore[*ast.Node, ArrayLiteralLinks] switchStatementLinks core.LinkStore[*ast.Node, SwitchStatementLinks] jsxElementLinks core.LinkStore[*ast.Node, JsxElementLinks] @@ -957,6 +966,7 @@ func NewChecker(program Program, tracer *Tracer) (*Checker, *sync.Mutex) { c.discriminatedContextualTypes = make(map[DiscriminatedContextualTypeKey]*Type) c.instantiationExpressionTypes = make(map[InstantiationExpressionKey]*Type) c.substitutionTypes = make(map[SubstitutionTypeKey]*Type) + c.narrowableReturnTypes = make(map[NarrowableReturnTypeKey]bool) c.reverseMappedCache = make(map[ReverseMappedTypeKey]*Type) c.reverseHomomorphicMappedCache = make(map[ReverseMappedTypeKey]*Type) c.iterationTypesCache = make(map[IterationTypesKey]IterationTypes) @@ -4173,7 +4183,366 @@ func (c *Checker) checkReturnExpression(container *ast.Node, unwrappedReturnType effectiveExpr = c.getEffectiveCheckNode(expr) } errorNode := core.IfElse(inReturnStatement && !inConditionalExpression, node, effectiveExpr) - c.checkTypeAssignableToAndOptionallyElaborate(unwrappedExprType, unwrappedReturnType, errorNode, effectiveExpr, nil, nil) + // If the return type is not narrowable, we simply check if the return expression type is assignable to the return type. + if unwrappedReturnType.flags&(TypeFlagsIndexedAccess|TypeFlagsConditional) == 0 || !c.couldContainTypeVariables(unwrappedReturnType) { + c.checkTypeAssignableToAndOptionallyElaborate(unwrappedExprType, unwrappedReturnType, errorNode, effectiveExpr, nil, nil) + return + } + // If the type of the return expression is assignable to the original return type, we don't need to narrow the return type. + if c.isTypeAssignableTo(unwrappedExprType, unwrappedReturnType) { + return + } + + // There are two cases for obtaining a position in the control-flow graph on which references will be analyzed: + // - When the return expression is defined, and it is one of the two branches of a conditional expression, then the position is the expression itself: + // `function foo(...) { + // return cond ? |expr| : ... + // }` + // - When the return expression is undefined, or it is defined and it is not one of the branches of a conditional expression, then the position is the return statement itself: + // `function foo(...) { + // |return expr;| + // }` + // or + // `function foo(...) { + // |return;| + // }` + narrowPosition := node + narrowFlowNode := getFlowNodeOfNode(node) + if expr != nil && ast.IsConditionalExpression(expr.Parent) { + conditional := expr.Parent.AsConditionalExpression() + if conditional.WhenTrue == expr { + narrowFlowNode = conditional.FlowNodeWhenTrue + } else { + narrowFlowNode = conditional.FlowNodeWhenFalse + } + narrowPosition = expr + } + if narrowFlowNode == nil { + c.checkTypeAssignableToAndOptionallyElaborate(unwrappedExprType, unwrappedReturnType, errorNode, effectiveExpr, nil, nil) + return + } + + allTypeParameters := c.appendTypeParameters(c.getOuterTypeParameters(container, false /*includeThisTypes*/), container.TypeParameters()) + narrowableTypeParameters := c.getNarrowableTypeParameters(allTypeParameters) + if len(narrowableTypeParameters) == 0 || !c.isNarrowableReturnType(unwrappedReturnType) { + c.checkTypeAssignableToAndOptionallyElaborate(unwrappedExprType, unwrappedReturnType, errorNode, effectiveExpr, nil, nil) + return + } + + var narrowedTypeParameters []*Type + var narrowedTypes []*Type + for _, narrowable := range narrowableTypeParameters { + reference := narrowable.reference + reference.Parent = narrowPosition.Parent + reference.FlowNodeData().FlowNode = narrowFlowNode + // Set the symbol of the synthetic reference. This allows us to get its type at a location where the reference may be shadowed. + baseReference := reference + for ast.IsAccessExpression(baseReference) { + baseReference = baseReference.Expression() + } + c.symbolNodeLinks.Get(baseReference).resolvedSymbol = narrowable.symbol + initialType := c.getNarrowableTypeForReferenceEx(narrowable.typeParameter, reference, CheckModeNormal, true /*forReturnTypeNarrowing*/) + if initialType == narrowable.typeParameter { + continue + } + narrowedType := c.getFlowTypeOfReference(reference, initialType) + // If attempting to narrow the expression type did not produce a narrower type, discard this type parameter from narrowing. + if narrowedType.flags&TypeFlagsAnyOrUnknown != 0 || c.isErrorType(narrowedType) || narrowedType == narrowable.typeParameter || narrowedType == c.getBaseConstraintOrType(narrowable.typeParameter) { + continue + } + narrowedTypeParameters = append(narrowedTypeParameters, narrowable.typeParameter) + narrowedTypes = append(narrowedTypes, c.getNarrowingSubstitutionType(narrowable.typeParameter, narrowedType)) + } + + narrowedReturnType := unwrappedReturnType + if len(narrowedTypeParameters) != 0 { + narrowedReturnType = c.instantiateType(unwrappedReturnType, newTypeMapper(narrowedTypeParameters, narrowedTypes)) + } + if expr != nil { + links := c.contextualReturnTypeLinks.Get(expr) + if links.contextualReturnType == nil { + links.contextualReturnType = narrowedReturnType + } + } + narrowedExprType := exprType + if expr != nil { + narrowedExprType = c.checkExpression(expr) + } + if functionFlags&ast.FunctionFlagsAsync != 0 { + narrowedExprType = c.checkAwaitedType(narrowedExprType, false /*withAlias*/, node, diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member) + } + c.checkTypeAssignableToAndOptionallyElaborate(narrowedExprType, narrowedReturnType, errorNode, effectiveExpr, nil, nil) +} + +type narrowableTypeParameter struct { + typeParameter *Type + symbol *ast.Symbol + reference *ast.Node +} + +type typeParameterReferencePath struct { + valid bool + found bool + path []*ast.Node +} + +// Narrowable type parameters have a union constraint and are syntactically used as the type of a single parameter in the function, and nothing else. +func (c *Checker) getNarrowableTypeParameters(candidates []*Type) []narrowableTypeParameter { + var result []narrowableTypeParameter + for _, typeParameter := range candidates { + constraint := c.getConstraintOfTypeParameter(typeParameter) + if constraint == nil || constraint.flags&TypeFlagsUnion == 0 || typeParameter.symbol == nil || len(typeParameter.symbol.Declarations) != 1 { + continue + } + container := typeParameter.symbol.Declarations[0].Parent + if !ast.IsFunctionLike(container) { + continue + } + var parameter *ast.Node + var referencePath []*ast.Node + invalidReference := false + for _, candidate := range container.Parameters() { + typeNode := candidate.Type() + if typeNode == nil { + continue + } + pathResult := c.getValidTypeParameterReference(typeNode, typeParameter, nil) + if !pathResult.valid { + invalidReference = true + break + } + if pathResult.found { + if parameter != nil { + invalidReference = true + break + } + parameter = candidate + referencePath = pathResult.path + } + } + if invalidReference || parameter == nil || !c.validateNarrowableReferenceOptionality(parameter, constraint, referencePath) { + continue + } + symbol, reference := c.constructNarrowableReference(parameter, referencePath) + if symbol == nil || symbol == c.unknownSymbol || reference == nil { + continue + } + result = append(result, narrowableTypeParameter{ + typeParameter: typeParameter, + symbol: symbol, + reference: reference, + }) + } + return result +} + +// Given a type node and a type parameter T, getValidTypeParameterReference validates every syntactic occurrence of T and collects a path to a valid occurrence. +// A valid result with found false means no references were found; an invalid result means invalid or multiple valid references were found; a valid, found result +// contains the path to the one valid reference. The initially empty path accumulates valid property accesses. +func (c *Checker) getValidTypeParameterReference(typeNode *ast.Node, typeParameter *Type, path []*ast.Node) typeParameterReferencePath { + validNoReference := typeParameterReferencePath{valid: true} + invalid := typeParameterReferencePath{} + switch typeNode.Kind { + case ast.KindTypeReference: + if len(typeNode.TypeArguments()) == 0 && c.getSymbolFromTypeReference(typeNode) == typeParameter.symbol { + return typeParameterReferencePath{valid: true, found: true, path: path} + } + // Find type arguments that reference the type parameter. + var referencedTypeArgument *ast.Node + for _, typeArgument := range typeNode.TypeArguments() { + if c.isTypeParameterReferenced(typeParameter, typeArgument) { + if referencedTypeArgument != nil { + return invalid + } + referencedTypeArgument = typeArgument + } + } + if referencedTypeArgument == nil { + return validNoReference + } + t := c.getTypeFromTypeReference(typeNode) + if !ast.IsTypeReferenceNode(referencedTypeArgument) || c.getTypeFromTypeNode(referencedTypeArgument) != typeParameter || t.symbol == nil || len(t.symbol.Declarations) != 1 { + return invalid + } + typeDeclaration := t.symbol.Declarations[0] + aliasDeclaration := typeDeclaration + if ast.IsTypeLiteralNode(typeDeclaration) { + aliasDeclaration = ast.WalkUpParenthesizedTypes(typeDeclaration.Parent) + if !ast.IsTypeAliasDeclaration(aliasDeclaration) { + return invalid + } + } else if !ast.IsInterfaceDeclaration(typeDeclaration) && !ast.IsClassLike(typeDeclaration) { + return invalid + } + typeArgumentIndex := slices.Index(typeNode.TypeArguments(), referencedTypeArgument) + typeParameters := aliasDeclaration.TypeParameters() + if typeArgumentIndex < 0 || typeArgumentIndex >= len(typeParameters) { + return invalid + } + matchingTypeParameter := c.getDeclaredTypeOfTypeParameter(c.getSymbolOfDeclaration(typeParameters[typeArgumentIndex])) + return c.getValidTypeParameterReference(typeDeclaration, matchingTypeParameter, path) + case ast.KindInterfaceDeclaration, ast.KindClassDeclaration, ast.KindClassExpression: + var heritageClauses *ast.NodeList + switch typeNode.Kind { + case ast.KindInterfaceDeclaration: + heritageClauses = typeNode.AsInterfaceDeclaration().HeritageClauses + case ast.KindClassDeclaration: + heritageClauses = typeNode.AsClassDeclaration().HeritageClauses + case ast.KindClassExpression: + heritageClauses = typeNode.AsClassExpression().HeritageClauses + } + var referencedHeritageType *ast.Node + if heritageClauses != nil { + for _, clause := range heritageClauses.Nodes { + for _, heritageType := range clause.AsHeritageClause().Types.Nodes { + if c.isTypeParameterReferenced(typeParameter, heritageType) { + if referencedHeritageType != nil { + return invalid + } + referencedHeritageType = heritageType + } + } + } + } + memberResult := c.getValidTypeParameterReferenceFromMembers(typeNode.Members(), typeParameter, path) + if !memberResult.valid { + return invalid + } + if referencedHeritageType != nil { + if memberResult.found { + return invalid + } + return c.getValidTypeParameterReference(referencedHeritageType, typeParameter, path) + } + return memberResult + case ast.KindTypeLiteral: + return c.getValidTypeParameterReferenceFromMembers(typeNode.Members(), typeParameter, path) + case ast.KindIntersectionType: + result := validNoReference + for _, constituent := range typeNode.AsIntersectionTypeNode().Types.Nodes { + constituentResult := c.getValidTypeParameterReference(constituent, typeParameter, path) + if !constituentResult.valid || result.found && constituentResult.found { + return invalid + } + if constituentResult.found { + result = constituentResult + } + } + return result + default: + // If we see a reference to the type parameter in the type node here, invalidate the whole thing. + if c.isTypeParameterReferenced(typeParameter, typeNode) { + return invalid + } + return validNoReference + } +} + +func (c *Checker) getValidTypeParameterReferenceFromMembers(members []*ast.Node, typeParameter *Type, path []*ast.Node) typeParameterReferencePath { + result := typeParameterReferencePath{valid: true} + for _, member := range members { + if !c.isTypeParameterReferenced(typeParameter, member) { + continue + } + if (!ast.IsPropertySignatureDeclaration(member) && !ast.IsPropertyDeclaration(member)) || (!ast.IsIdentifier(member.Name()) && !ast.IsStringLiteral(member.Name())) || member.Type() == nil { + return typeParameterReferencePath{} + } + memberPath := append(slices.Clone(path), member) + memberResult := c.getValidTypeParameterReference(member.Type(), typeParameter, memberPath) + if !memberResult.valid || result.found && memberResult.found { + return typeParameterReferencePath{} + } + if memberResult.found { + result = memberResult + } + } + return result +} + +func (c *Checker) validateNarrowableReferenceOptionality(parameter *ast.Node, constraint *Type, path []*ast.Node) bool { + // `function f(obj: { prop?: { prop2: T }})` is not allowed. + for _, member := range path[:max(0, len(path)-1)] { + if member.QuestionToken() != nil { + return false + } + } + parameterIsOptional := parameter.QuestionToken() != nil + // `function f(obj?: { prop: T })` is not allowed. + if parameterIsOptional && len(path) != 0 { + return false + } + isOptional := parameterIsOptional || len(path) != 0 && path[len(path)-1].QuestionToken() != nil + // `function f(obj?: T)` is not allowed under `strictNullChecks`. + return !isOptional || !c.strictNullChecks || c.containsUndefinedType(constraint) +} + +// Given a parameter declaration and a name path to a reference of type parameter T in the parameter's type, +// constructNarrowableReference constructs a reference to the parameter property corresponding to T. +// Examples: +// `constructNarrowableReference(param: { b: T }, [b])` => `param.b` +// `constructNarrowableReference(param: { a: { b: T } }, [a, b])` => `param.a.b` +// `constructNarrowableReference(param: { "a b": T }, ["a b"])` => `param["a b"]` +// `constructNarrowableReference({ b }: { b: T }, [b])` => `b` +// `constructNarrowableReference({ a }: { a: { b: T } }, [a, b])` => `a.b` +// `constructNarrowableReference({ a: { b } }: { a: { b: T } }, [a, b])` => `b` +func (c *Checker) constructNarrowableReference(parameter *ast.Node, path []*ast.Node) (*ast.Symbol, *ast.Node) { + currentName := parameter.Name() + pathIndex := 0 + for ; pathIndex < len(path); pathIndex++ { + if ast.IsIdentifier(currentName) { + break + } + if !ast.IsObjectBindingPattern(currentName) { + // Shouldn't happen unless the program has errors. + return nil, nil + } + propertyName, ok := c.getLiteralPropertyNameText(path[pathIndex].Name()) + if !ok { + return nil, nil + } + // Find the binding element corresponding to the property name. + bindingElement := core.Find(currentName.Elements(), func(element *ast.Node) bool { + name, hasName := c.getDestructuringPropertyName(element) + return hasName && name == propertyName + }) + if bindingElement == nil { + // Shouldn't happen unless the program has errors. + return nil, nil + } + currentName = bindingElement.Name() + } + if !ast.IsIdentifier(currentName) { + return nil, nil + } + reference := c.factory.NewIdentifier(currentName.Text()) + symbol := c.getSymbolOfDeclaration(currentName.Parent) + for ; pathIndex < len(path); pathIndex++ { + name := path[pathIndex].Name() + if ast.IsIdentifier(name) { + reference = c.factory.NewPropertyAccessExpression(reference, nil, c.factory.NewIdentifier(name.Text()), ast.NodeFlagsNone) + } else { + reference = c.factory.NewElementAccessExpression(reference, nil, c.factory.DeepCloneNode(name), ast.NodeFlagsNone) + } + } + ast.SetParentInChildren(reference) + return symbol, reference +} + +func (c *Checker) isTypeParameterReferenced(typeParameter *Type, node *ast.Node) bool { + if ast.IsTypeReferenceNode(node) { + if len(node.TypeArguments()) == 0 && c.getSymbolFromTypeReference(node) == typeParameter.symbol { + return true + } + return core.Some(node.TypeArguments(), func(typeArgument *ast.Node) bool { + return c.isTypeParameterReferenced(typeParameter, typeArgument) + }) + } + if ast.IsTypeQueryNode(node) { + return c.isTypeParameterPossiblyReferenced(typeParameter, node) + } + return node.ForEachChild(func(child *ast.Node) bool { + return c.isTypeParameterReferenced(typeParameter, child) + }) } func (c *Checker) checkWithStatement(node *ast.Node) { @@ -22839,15 +23208,27 @@ func (c *Checker) getConditionalTypeInstantiation(t *Type, mapper *TypeMapper, f if root.isDistributive { distributionType = c.getReducedType(newMapper.Map(checkType)) } + var narrowingBaseType *Type + forNarrowing := distributionType != nil && isNarrowingSubstitutionType(distributionType) && c.isNarrowableConditionalType(t, nil, mapper) + if forNarrowing { + narrowingBaseType = distributionType.AsSubstitutionType().baseType + distributionType = c.getReducedType(distributionType.AsSubstitutionType().constraint) + } // Distributive conditional types are distributed over union types. For example, when the // distributive conditional type T extends U ? X : Y is instantiated with A | B for T, the // result is (A extends U ? X : Y) | (B extends U ? X : Y). if distributionType != nil && checkType != distributionType && distributionType.flags&(TypeFlagsUnion|TypeFlagsNever) != 0 { - result = c.mapTypeWithAlias(distributionType, func(t *Type) *Type { - return c.getConditionalType(root, prependTypeMapping(checkType, t, newMapper), forConstraint, nil) - }, alias) + if narrowingBaseType != nil { + result = c.mapTypeToIntersection(distributionType, func(t *Type) *Type { + return c.getConditionalType(root, prependTypeMapping(checkType, c.getNarrowingSubstitutionType(narrowingBaseType, t), newMapper), forConstraint, nil, forNarrowing) + }) + } else { + result = c.mapTypeWithAlias(distributionType, func(t *Type) *Type { + return c.getConditionalType(root, prependTypeMapping(checkType, t, newMapper), forConstraint, nil, false) + }, alias) + } } else { - result = c.getConditionalType(root, newMapper, forConstraint, alias) + result = c.getConditionalType(root, newMapper, forConstraint, alias, forNarrowing) } root.instantiations[key] = result } @@ -24638,7 +25019,7 @@ func (c *Checker) getTypeFromConditionalTypeNode(node *ast.Node) *Type { instantiations: nil, alias: alias, } - links.resolvedType = c.getConditionalType(root, nil /*mapper*/, false /*forConstraint*/, nil) + links.resolvedType = c.getConditionalType(root, nil /*mapper*/, false /*forConstraint*/, nil, false /*forNarrowing*/) if outerTypeParameters != nil { root.instantiations = make(map[CacheHashKey]*Type) root.instantiations[getConditionalTypeKey(outerTypeParameters, nil /*alias*/, false /*forConstraint*/)] = links.resolvedType @@ -24647,7 +25028,7 @@ func (c *Checker) getTypeFromConditionalTypeNode(node *ast.Node) *Type { return links.resolvedType } -func (c *Checker) getConditionalType(root *ConditionalRoot, mapper *TypeMapper, forConstraint bool, alias *TypeAlias) *Type { +func (c *Checker) getConditionalType(root *ConditionalRoot, mapper *TypeMapper, forConstraint bool, alias *TypeAlias, forNarrowing bool) *Type { var result *Type var extraTypes []*Type tailCount := 0 @@ -24669,13 +25050,17 @@ func (c *Checker) getConditionalType(root *ConditionalRoot, mapper *TypeMapper, if checkType == c.wildcardType || extendsType == c.wildcardType { return c.wildcardType } + effectiveCheckType := checkType + if forNarrowing && isNarrowingSubstitutionType(checkType) { + effectiveCheckType = checkType.AsSubstitutionType().constraint + } checkTypeNode := ast.SkipTypeParentheses(root.node.CheckType) extendsTypeNode := ast.SkipTypeParentheses(root.node.ExtendsType) // When the check and extends types are simple tuple types of the same arity, we defer resolution of the // conditional type when any tuple elements are generic. This is such that non-distributable conditional // types can be written `[X] extends [Y] ? ...` and be deferred similarly to `X extends Y ? ...`. checkTuples := c.isSimpleTupleType(checkTypeNode) && c.isSimpleTupleType(extendsTypeNode) && len(checkTypeNode.Elements()) == len(extendsTypeNode.Elements()) - checkTypeDeferred := c.isDeferredType(checkType, checkTuples) + checkTypeDeferred := c.isDeferredType(effectiveCheckType, checkTuples) var combinedMapper *TypeMapper if len(root.inferTypeParameters) != 0 { // When we're looking at making an inference for an infer type, when we get its constraint, it'll automagically be @@ -24720,18 +25105,18 @@ func (c *Checker) getConditionalType(root *ConditionalRoot, mapper *TypeMapper, } // We attempt to resolve the conditional type only when the check and extends types are non-generic if !checkTypeDeferred && !c.isDeferredType(inferredExtendsType, checkTuples) { - // Return falseType for a definitely false extends check. We check an instantiations of the two + // Return falseType for a definitely false extends check. We check an instantiation of the two // types with type parameters mapped to the wildcard type, the most permissive instantiations // possible (the wildcard type is assignable to and from all types). If those are not related, // then no instantiations will be and we can just return the false branch type. - if inferredExtendsType.flags&TypeFlagsAnyOrUnknown == 0 && (checkType.flags&TypeFlagsAny != 0 || !c.isTypeAssignableTo(c.getPermissiveInstantiation(checkType), c.getPermissiveInstantiation(inferredExtendsType))) { + if inferredExtendsType.flags&TypeFlagsAnyOrUnknown == 0 && (effectiveCheckType.flags&TypeFlagsAny != 0 || !c.isTypeAssignableTo(c.getPermissiveInstantiation(effectiveCheckType), c.getPermissiveInstantiation(inferredExtendsType))) { // Return union of trueType and falseType for 'any' since it matches anything. Furthermore, for a // distributive conditional type applied to the constraint of a type variable, include trueType if // there are possible values of the check type that are also possible values of the extends type. // We use a reverse assignability check as it is less expensive than the comparable relationship // and avoids false positives of a non-empty intersection check. - if checkType.flags&TypeFlagsAny != 0 || forConstraint && inferredExtendsType.flags&TypeFlagsNever == 0 && someType(c.getPermissiveInstantiation(inferredExtendsType), func(t *Type) bool { - return c.isTypeAssignableTo(t, c.getPermissiveInstantiation(checkType)) + if effectiveCheckType.flags&TypeFlagsAny != 0 || forConstraint && inferredExtendsType.flags&TypeFlagsNever == 0 && someType(c.getPermissiveInstantiation(inferredExtendsType), func(t *Type) bool { + return c.isTypeAssignableTo(t, c.getPermissiveInstantiation(effectiveCheckType)) }) { extraTypes = append(extraTypes, c.instantiateType(c.getTypeFromTypeNode(root.node.TrueType), core.OrElse(combinedMapper, mapper))) } @@ -24762,7 +25147,7 @@ func (c *Checker) getConditionalType(root *ConditionalRoot, mapper *TypeMapper, // that has no constraint. This ensures that, for example, the type // type Foo = T extends { x: string } ? string : number // doesn't immediately resolve to 'string' instead of being deferred. - if inferredExtendsType.flags&TypeFlagsAnyOrUnknown != 0 || c.isTypeAssignableTo(c.getRestrictiveInstantiation(checkType), c.getRestrictiveInstantiation(inferredExtendsType)) { + if inferredExtendsType.flags&TypeFlagsAnyOrUnknown != 0 || c.isTypeAssignableTo(c.getRestrictiveInstantiation(effectiveCheckType), c.getRestrictiveInstantiation(inferredExtendsType)) { trueType := c.getTypeFromTypeNode(root.node.TrueType) trueMapper := core.OrElse(combinedMapper, mapper) if newRoot, newRootMapper := c.getTailRecursionRoot(trueType, trueMapper); newRoot != nil { @@ -24902,6 +25287,99 @@ func (c *Checker) getFalseTypeFromConditionalType(t *Type) *Type { return d.resolvedFalseType } +func (c *Checker) isNarrowableReturnType(t *Type) bool { + if t.flags&TypeFlagsConditional != 0 { + return c.isNarrowableConditionalType(t, nil, nil) + } + return t.flags&TypeFlagsIndexedAccess != 0 && t.AsIndexedAccessType().indexType.flags&TypeFlagsTypeParameter != 0 +} + +func (c *Checker) isNarrowableConditionalType(t *Type, hadNonPrimitiveExtendsTypes []*Type, mapper *TypeMapper) bool { + d := t.AsConditionalType() + var typeArguments []*Type + if mapper != nil { + typeArguments = core.Map(d.root.outerTypeParameters, func(t *Type) *Type { + mapped := mapper.Map(t) + if isNarrowingSubstitutionType(mapped) { + return mapped.AsSubstitutionType().baseType + } + return mapped + }) + } + key := NarrowableReturnTypeKey{ + typeId: t.id, + typeArguments: getTypeListKey(typeArguments), + nonPrimitiveExtendsTypes: getTypeListKey(hadNonPrimitiveExtendsTypes), + } + if result, ok := c.narrowableReturnTypes[key]; ok { + return result + } + var nonNarrowingMapper *TypeMapper + if len(d.root.outerTypeParameters) != 0 && typeArguments != nil { + nonNarrowingMapper = newTypeMapper(d.root.outerTypeParameters, typeArguments) + } + instantiatedType := c.instantiateType(t, nonNarrowingMapper) + result := instantiatedType.flags&TypeFlagsConditional != 0 && c.isNarrowableConditionalTypeWorker(instantiatedType, hadNonPrimitiveExtendsTypes) + c.narrowableReturnTypes[key] = result + return result +} + +// A narrowable conditional type has the shape `T extends A ? TrueBranch : FalseBranch`, where: +// (0) The conditional type is distributive. +// (1) The conditional type has no `infer` type parameters. +// (2) The check type is a narrowable type parameter, meaning a type parameter with a union constraint. +// (3) A is a type or union of types that are supertypes of the type parameter's union constraint. +// (4) At most one extends type has a non-primitive type. +// (5) TrueBranch and FalseBranch are valid recursively; in particular, the false-most branch must be `never`. +func (c *Checker) isNarrowableConditionalTypeWorker(t *Type, hadNonPrimitiveExtendsTypes []*Type) bool { + d := t.AsConditionalType() + // (0) + if !d.root.isDistributive { + return false + } + // (1) + if len(d.root.inferTypeParameters) != 0 { + return false + } + // (2) + if d.checkType.flags&TypeFlagsTypeParameter == 0 { + return false + } + // (2) + constraintType := c.getConstraintOfTypeParameter(d.checkType) + if constraintType == nil || constraintType.flags&TypeFlagsUnion == 0 { + return false + } + // (3) + if !everyType(d.extendsType, func(extendsType *Type) bool { + return core.Some(constraintType.Types(), func(constraintType *Type) bool { + return c.isTypeAssignableTo(constraintType, extendsType) + }) + }) { + return false + } + // (4) + hasNonPrimitive := someType(d.extendsType, func(t *Type) bool { + return t.flags&TypeFlagsPrimitive == 0 + }) + if hasNonPrimitive && slices.Contains(hadNonPrimitiveExtendsTypes, d.checkType) { + return false + } + if hasNonPrimitive { + hadNonPrimitiveExtendsTypes = append(slices.Clone(hadNonPrimitiveExtendsTypes), d.checkType) + } + // (5) + trueType := c.getTrueTypeFromConditionalType(t) + if trueType.flags&TypeFlagsConditional != 0 && !c.isNarrowableConditionalType(trueType, hadNonPrimitiveExtendsTypes, nil) { + return false + } + falseType := c.getFalseTypeFromConditionalType(t) + if falseType.flags&TypeFlagsConditional != 0 { + return c.isNarrowableConditionalType(falseType, hadNonPrimitiveExtendsTypes, nil) + } + return falseType == c.neverType +} + func (c *Checker) getInferredTrueTypeFromConditionalType(t *Type) *Type { d := t.AsConditionalType() if d.resolvedInferredTrueType == nil { @@ -25912,6 +26390,28 @@ func (c *Checker) mapType(t *Type, f func(*Type) *Type) *Type { return c.mapTypeEx(t, f, false /*noReductions*/) } +// mapTypeToIntersection is similar to mapType, but creates an intersection with the result of mapping over a union type. +func (c *Checker) mapTypeToIntersection(t *Type, f func(*Type) *Type) *Type { + if t.flags&TypeFlagsNever != 0 { + return t + } + if t.flags&TypeFlagsUnion == 0 { + return f(t) + } + u := t.AsUnionType() + types := u.types + if u.origin != nil && u.origin.flags&TypeFlagsUnion != 0 { + types = u.origin.Types() + } + mappedTypes := core.Map(types, func(t *Type) *Type { + if t.flags&TypeFlagsUnion != 0 { + return c.mapTypeToIntersection(t, f) + } + return f(t) + }) + return c.getIntersectionType(mappedTypes) +} + func (c *Checker) mapTypeEx(t *Type, f func(*Type) *Type, noReductions bool) *Type { if t.flags&TypeFlagsNever != 0 { return t @@ -27743,7 +28243,7 @@ func indexTypeLessThan(indexType *Type, limit int) bool { func (c *Checker) getNoInferType(t *Type) *Type { if c.isNoInferTargetType(t) { - return c.getOrCreateSubstitutionType(t, c.unknownType) + return c.getOrCreateSubstitutionType(t, c.unknownType, false) } return t } @@ -27762,19 +28262,33 @@ func (c *Checker) getSubstitutionType(baseType *Type, constraint *Type) *Type { if constraint.flags&TypeFlagsAnyOrUnknown != 0 || constraint == baseType || baseType.flags&TypeFlagsAny != 0 { return baseType } - return c.getOrCreateSubstitutionType(baseType, constraint) + return c.getOrCreateSubstitutionType(baseType, constraint, false) +} + +func (c *Checker) getNarrowingSubstitutionType(baseType *Type, constraint *Type) *Type { + if constraint.flags&TypeFlagsAnyOrUnknown != 0 || constraint == baseType || baseType.flags&TypeFlagsAny != 0 { + return baseType + } + return c.getOrCreateSubstitutionType(baseType, constraint, true) } -func (c *Checker) getOrCreateSubstitutionType(baseType *Type, constraint *Type) *Type { - key := SubstitutionTypeKey{baseId: baseType.id, constraintId: constraint.id} +func (c *Checker) getOrCreateSubstitutionType(baseType *Type, constraint *Type, isNarrowing bool) *Type { + key := SubstitutionTypeKey{baseId: baseType.id, constraintId: constraint.id, isNarrowing: isNarrowing} if cached := c.substitutionTypes[key]; cached != nil { return cached } result := c.newSubstitutionType(baseType, constraint) + if isNarrowing { + result.objectFlags |= ObjectFlagsIsNarrowingType + } c.substitutionTypes[key] = result return result } +func isNarrowingSubstitutionType(t *Type) bool { + return t.flags&TypeFlagsSubstitution != 0 && t.objectFlags&ObjectFlagsIsNarrowingType != 0 +} + func (c *Checker) getBaseConstraintOrType(t *Type) *Type { constraint := c.getBaseConstraintOfType(t) if constraint != nil { @@ -28410,10 +28924,13 @@ func (c *Checker) getNormalizedUnionOrIntersectionType(t *Type, writing bool) *T func (c *Checker) shouldNormalizeIntersection(t *Type) bool { hasInstantiable := false hasNullableOrEmpty := false + hasNarrowingSubstitution := false for _, t := range t.Types() { hasInstantiable = hasInstantiable || t.flags&TypeFlagsInstantiable != 0 hasNullableOrEmpty = hasNullableOrEmpty || t.flags&TypeFlagsNullable != 0 || c.IsEmptyAnonymousObjectType(t) - if hasInstantiable && hasNullableOrEmpty { + // This avoids displaying error messages with types like `T & T` when narrowing a return type. + hasNarrowingSubstitution = hasNarrowingSubstitution || isNarrowingSubstitutionType(t) + if hasInstantiable && hasNullableOrEmpty || hasNarrowingSubstitution { return true } } @@ -29701,6 +30218,12 @@ func (c *Checker) getContextualType(node *ast.Node, contextFlags ContextFlags) * if index >= 0 { return c.contextualInfos[index].t } + if links := c.contextualReturnTypeLinks.TryGet(node); links != nil && links.contextualReturnType != nil { + if node.Flags&ast.NodeFlagsAwaitContext != 0 { + return c.getUnionType([]*Type{links.contextualReturnType, c.createPromiseLikeType(links.contextualReturnType)}) + } + return links.contextualReturnType + } parent := node.Parent switch parent.Kind { case ast.KindVariableDeclaration, ast.KindParameter, ast.KindPropertyDeclaration, ast.KindPropertySignature, ast.KindBindingElement: @@ -29971,9 +30494,15 @@ func (c *Checker) getContextualTypeForStaticPropertyDeclaration(declaration *ast func (c *Checker) getContextualTypeForReturnExpression(node *ast.Node, contextFlags ContextFlags) *Type { fn := ast.GetContainingFunction(node) if fn != nil { + functionFlags := ast.GetFunctionFlags(fn) + if links := c.contextualReturnTypeLinks.TryGet(node); links != nil && links.contextualReturnType != nil { + if functionFlags&ast.FunctionFlagsAsync != 0 { + return c.getUnionType([]*Type{links.contextualReturnType, c.createPromiseLikeType(links.contextualReturnType)}) + } + return links.contextualReturnType + } contextualReturnType := c.getContextualReturnType(fn, contextFlags) if contextualReturnType != nil { - functionFlags := ast.GetFunctionFlags(fn) if functionFlags&ast.FunctionFlagsGenerator != 0 { isAsyncGenerator := (functionFlags & ast.FunctionFlagsAsync) != 0 if contextualReturnType.flags&TypeFlagsUnion != 0 { @@ -31839,6 +32368,10 @@ func (c *Checker) getTargetType(t *Type) *Type { } func (c *Checker) getNarrowableTypeForReference(t *Type, reference *ast.Node, checkMode CheckMode) *Type { + return c.getNarrowableTypeForReferenceEx(t, reference, checkMode, false /*forReturnTypeNarrowing*/) +} + +func (c *Checker) getNarrowableTypeForReferenceEx(t *Type, reference *ast.Node, checkMode CheckMode, forReturnTypeNarrowing bool) *Type { if c.isNoInferType(t) { t = t.AsSubstitutionType().baseType } @@ -31849,7 +32382,7 @@ func (c *Checker) getNarrowableTypeForReference(t *Type, reference *ast.Node, ch // control flow analysis an opportunity to narrow it further. For example, for a reference of a type // parameter type 'T extends string | undefined' with a contextual type 'string', we substitute // 'string | undefined' to give control flow analysis the opportunity to narrow to type 'string'. - substituteConstraints := checkMode&CheckModeInferential == 0 && someType(t, c.isGenericTypeWithUnionConstraint) && (c.isConstraintPosition(t, reference) || c.hasContextualTypeWithNoGenericTypes(reference, checkMode)) + substituteConstraints := checkMode&CheckModeInferential == 0 && someType(t, c.isGenericTypeWithUnionConstraint) && (forReturnTypeNarrowing || c.isConstraintPosition(t, reference) || c.hasContextualTypeWithNoGenericTypes(reference, checkMode)) if substituteConstraints { return c.mapType(t, c.getBaseConstraintOrType) } diff --git a/tsc/internal/checker/types.go b/tsc/internal/checker/types.go index 2e8bacbd5df29..dc6e3939cb1d1 100644 --- a/tsc/internal/checker/types.go +++ b/tsc/internal/checker/types.go @@ -398,6 +398,12 @@ type AssertionLinks struct { exprType *Type // Assertion expression type } +// Links for return expressions + +type ContextualReturnTypeLinks struct { + contextualReturnType *Type // If the node is a return statement's expression, this is its contextual return type. +} + // SourceFile links type SourceFileLinks struct { @@ -642,6 +648,7 @@ const ( ObjectFlagsIsGenericObjectType = 1 << 23 // Union or intersection contains generic object type ObjectFlagsIsGenericIndexType = 1 << 24 // Union or intersection contains generic index type ObjectFlagsIsGenericType = ObjectFlagsIsGenericObjectType | ObjectFlagsIsGenericIndexType + ObjectFlagsIsNarrowingType = 1 << 31 // Substitution type that comes from type narrowing // Flags that require TypeFlags.Union ObjectFlagsContainsIntersections = 1 << 25 // Union contains intersections ObjectFlagsIsUnknownLikeUnionComputed = 1 << 26 // IsUnknownLikeUnion flag has been computed @@ -1224,6 +1231,15 @@ type StringMappingType struct { func (t *StringMappingType) Target() *Type { return t.target } +// Type parameter substitution (TypeFlagsSubstitution) +// - Substitution types are created for type parameters or indexed access types that occur in the true branch of a conditional type. +// For example, in `T extends string ? Foo : Bar`, the reference to T in Foo resolves as a substitution type that substitutes +// `string & T` for T. Thus, if Foo has a string constraint on its type parameter, T will satisfy it. +// - Substitution types are also created for `NoInfer` types. Those are represented as substitution types where the constraint is +// `unknown`, which is never generated for the case above. +// - Substitution types are also created for return type narrowing. If a type parameter T is linked to a parameter x and x's narrowed +// type is S, we represent that with a substitution type with base T and constraint S. The resulting substitution type has +// ObjectFlagsIsNarrowingType set. type SubstitutionType struct { ConstrainedType baseType *Type // Target type diff --git a/tsc/internal/fourslash/tests/returnTypeNarrowingAfterCachingTypes_test.go b/tsc/internal/fourslash/tests/returnTypeNarrowingAfterCachingTypes_test.go new file mode 100644 index 0000000000000..836aebc94464b --- /dev/null +++ b/tsc/internal/fourslash/tests/returnTypeNarrowingAfterCachingTypes_test.go @@ -0,0 +1,31 @@ +package fourslash_test + +import ( + "testing" + + "github.com/microsoft/TypeScript/tsc/internal/fourslash" + "github.com/microsoft/TypeScript/tsc/internal/testutil" +) + +func TestReturnTypeNarrowingAfterCachingTypes(t *testing.T) { + t.Parallel() + defer testutil.RecoverAndFail(t, "Panic on fourslash test") + const content = `function h(x: T): T extends true ? 1 : T extends false ? 2 : never { + if (x) { + return 1; + } + return 2; +}` + f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content) + defer done() + f.VerifySemanticTokens(t, []fourslash.SemanticToken{ + {Type: "function.declaration", Text: "h"}, + {Type: "typeParameter.declaration", Text: "T"}, + {Type: "parameter.declaration", Text: "x"}, + {Type: "typeParameter", Text: "T"}, + {Type: "typeParameter", Text: "T"}, + {Type: "typeParameter", Text: "T"}, + {Type: "parameter", Text: "x"}, + }) + f.VerifyNoErrors(t) +} diff --git a/tsc/testdata/baselines/reference/compiler/dependentReturnType1.errors.txt b/tsc/testdata/baselines/reference/compiler/dependentReturnType1.errors.txt new file mode 100644 index 0000000000000..4332400eaf6a2 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/dependentReturnType1.errors.txt @@ -0,0 +1,645 @@ +dependentReturnType1.ts(11,9): error TS2322: Type 'number' is not assignable to type 'A[T]'. + Type 'number' is not assignable to type 'string'. +dependentReturnType1.ts(26,9): error TS2322: Type '""' is not assignable to type 'C[T]'. + Type '""' is not assignable to type 'never'. +dependentReturnType1.ts(35,9): error TS2322: Type '""' is not assignable to type 'never'. +dependentReturnType1.ts(69,9): error TS2322: Type '{ a: "a"; b: "b"; c: "c"; d: "d"; e: "e"; f: "f"; }' is not assignable to type 'T extends 1 ? One : T extends 2 ? Two : T extends 3 ? Three : Four'. +dependentReturnType1.ts(71,5): error TS2322: Type '{ a: "a"; b: "b"; c: "c"; d: "d"; e: "e"; f: "f"; g: "g"; }' is not assignable to type 'T extends 1 ? One : T extends 2 ? Two : T extends 3 ? Three : Four'. +dependentReturnType1.ts(80,22): error TS2353: Object literal may only specify known properties, and 'b' does not exist in type 'Three & Four'. +dependentReturnType1.ts(96,9): error TS2322: Type 'LeftOut' is not assignable to type 'Arg extends LeftIn ? LeftOut : Arg extends RightIn ? RightOut : never'. +dependentReturnType1.ts(98,9): error TS2322: Type 'RightOut' is not assignable to type 'Arg extends LeftIn ? LeftOut : Arg extends RightIn ? RightOut : never'. +dependentReturnType1.ts(115,9): error TS2322: Type 'number' is not assignable to type 'T extends Dog ? number : string'. +dependentReturnType1.ts(117,5): error TS2322: Type 'string' is not assignable to type 'T extends Dog ? number : string'. +dependentReturnType1.ts(152,13): error TS2322: Type 'string' is not assignable to type 'T extends string ? this : T extends undefined ? string : never'. +dependentReturnType1.ts(154,9): error TS2322: Type 'this' is not assignable to type 'T extends string ? this : T extends undefined ? string : never'. + Type 'Unnamed' is not assignable to type 'T extends string ? this : T extends undefined ? string : never'. +dependentReturnType1.ts(169,13): error TS2322: Type 'this' is not assignable to type 'string'. + Type 'Unnamed' is not assignable to type 'string'. +dependentReturnType1.ts(172,9): error TS2322: Type 'T & {}' is not assignable to type 'this'. + 'this' could be instantiated with an arbitrary type which could be unrelated to 'T & {}'. +dependentReturnType1.ts(206,24): error TS2322: Type 'string' is not assignable to type 'number'. +dependentReturnType1.ts(206,28): error TS2322: Type 'number' is not assignable to type 'string'. +dependentReturnType1.ts(243,9): error TS2322: Type '""' is not assignable to type 'T extends 1 | 2 ? T extends 1 ? string : T extends 2 ? boolean : never : T extends 3 ? number : never'. +dependentReturnType1.ts(245,9): error TS2322: Type 'true' is not assignable to type 'T extends 1 | 2 ? T extends 1 ? string : T extends 2 ? boolean : never : T extends 3 ? number : never'. +dependentReturnType1.ts(247,5): error TS2322: Type '3' is not assignable to type 'T extends 1 | 2 ? T extends 1 ? string : T extends 2 ? boolean : never : T extends 3 ? number : never'. +dependentReturnType1.ts(258,9): error TS2322: Type 'string' is not assignable to type 'T extends { a: string; } ? number : T extends { b: number; } ? string : never'. +dependentReturnType1.ts(261,5): error TS2322: Type 'number' is not assignable to type 'T extends { a: string; } ? number : T extends { b: number; } ? string : never'. +dependentReturnType1.ts(275,9): error TS2322: Type '1' is not assignable to type 'HelperCond<{ x: U; y: V; }, { x: string; y: true; }, 1, { x: number; y: false; }, 2>'. +dependentReturnType1.ts(278,9): error TS2322: Type '2' is not assignable to type 'HelperCond<{ x: U; y: V; }, { x: string; y: true; }, 1, { x: number; y: false; }, 2>'. +dependentReturnType1.ts(280,5): error TS2322: Type '0' is not assignable to type 'HelperCond<{ x: U; y: V; }, { x: string; y: true; }, 1, { x: number; y: false; }, 2>'. +dependentReturnType1.ts(302,9): error TS2322: Type 'string' is not assignable to type 'string[]'. +dependentReturnType1.ts(334,9): error TS2322: Type '1' is not assignable to type '4'. +dependentReturnType1.ts(367,13): error TS2322: Type 'number' is not assignable to type 'T extends 1 ? number : T extends 2 ? string : never'. +dependentReturnType1.ts(369,9): error TS2322: Type 'string' is not assignable to type 'T extends 1 ? number : T extends 2 ? string : never'. +dependentReturnType1.ts(392,9): error TS2322: Type '2' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'. +dependentReturnType1.ts(402,13): error TS2322: Type 'number' is not assignable to type 'string'. +dependentReturnType1.ts(412,9): error TS2322: Type 'true' is not assignable to type 'T extends [infer R] ? R : T extends number ? boolean : never'. +dependentReturnType1.ts(414,5): error TS2322: Type '""' is not assignable to type 'T extends [infer R] ? R : T extends number ? boolean : never'. +dependentReturnType1.ts(439,15): error TS2322: Type '1' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'. +dependentReturnType1.ts(441,11): error TS2322: Type '2' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'. +dependentReturnType1.ts(470,13): error TS2322: Type 'R' is not assignable to type 'ConditionalReturnType'. +dependentReturnType1.ts(472,13): error TS2322: Type 'R' is not assignable to type 'ConditionalReturnType'. +dependentReturnType1.ts(474,13): error TS2322: Type 'T' is not assignable to type 'ConditionalReturnType'. +dependentReturnType1.ts(488,9): error TS2322: Type 'R' is not assignable to type 'ConditionalReturnType'. +dependentReturnType1.ts(514,5): error TS2322: Type '1' is not assignable to type 'never'. + + +==== dependentReturnType1.ts (39 errors) ==== + interface A { + 1: number; + 2: string; + } + + function f1(x: T): A[T] { + if (x === 1) { + return 0; // Ok + } + else { + return 1; // Error + ~~~~~~ +!!! error TS2322: Type 'number' is not assignable to type 'A[T]'. +!!! error TS2322: Type 'number' is not assignable to type 'string'. + } + } + + interface C { + 1: number; + 2: string; + 3: boolean; + } + + function f2(x: T): C[T] { + if (x === 1) { + return 0; // Ok + } + else { + return ""; // Error, returned expression needs to have type string & boolean (= never) + ~~~~~~ +!!! error TS2322: Type '""' is not assignable to type 'C[T]'. +!!! error TS2322: Type '""' is not assignable to type 'never'. + } + } + + function f3(x: T): T extends 1 ? number : T extends 2 ? string : T extends 3 ? boolean : never { + if (x === 1) { + return 0; // Ok + } + else { + return ""; // Error, returned expression needs to have type string & boolean (= never) + ~~~~~~ +!!! error TS2322: Type '""' is not assignable to type 'never'. + } + } + + interface One { + a: "a"; + b: "b"; + c: "c"; + d: "d"; + } + + interface Two { + a: "a"; + b: "b"; + e: "e"; + f: "f"; + } + + interface Three { + a: "a"; + c: "c"; + e: "e"; + g: "g"; + } + + interface Four { + a: "a"; + d: "d"; + f: "f"; + g: "g"; + } + // Badly written conditional return type, will not trigger narrowing + function f10(x: T): T extends 1 ? One : T extends 2 ? Two : T extends 3 ? Three : Four { + if (x === 1 || x === 2) { + return { a: "a", b: "b", c: "c", d: "d", e: "e", f: "f" }; // Error + ~~~~~~ +!!! error TS2322: Type '{ a: "a"; b: "b"; c: "c"; d: "d"; e: "e"; f: "f"; }' is not assignable to type 'T extends 1 ? One : T extends 2 ? Two : T extends 3 ? Three : Four'. + } + return { a: "a", b: "b", c: "c", d: "d", e: "e", f: "f", g: "g" }; // Error + ~~~~~~ +!!! error TS2322: Type '{ a: "a"; b: "b"; c: "c"; d: "d"; e: "e"; f: "f"; g: "g"; }' is not assignable to type 'T extends 1 ? One : T extends 2 ? Two : T extends 3 ? Three : Four'. + } + // Well written conditional + function f101(x: T): T extends 1 ? One : T extends 2 ? Two : T extends 3 ? Three : T extends 4 ? Four : never { + if (x === 1 || x === 2) { + return { a: "a", b: "b", c: "c", d: "d", e: "e", f: "f" }; // Ok + } + // Excess property becomes a problem with the change, + // because we now check assignability to a narrower type... + return { a: "a", b: "b", c: "c", d: "d", e: "e", f: "f", g: "g" }; // EPC Error + ~ +!!! error TS2353: Object literal may only specify known properties, and 'b' does not exist in type 'Three & Four'. + } + + // This will not work for several reasons: + // - first because the constraint of type parameter `Arg` is generic, + // so attempting to narrow the type of `arg` in the `if` would result in type `Arg & LeftIn`, + // which when substituted in the conditional return type, would not further resolve that conditional type + // - second because the `else` branch would never work because we don't narrow the type of `arg` to `Arg & RightIn` + function conditionalProducingIf( + arg: Arg, + cond: (arg: LeftIn | RightIn) => arg is LeftIn, + produceLeftOut: (arg: LeftIn) => LeftOut, + produceRightOut: (arg: RightIn) => RightOut): + Arg extends LeftIn ? LeftOut : Arg extends RightIn ? RightOut : never + { + if (cond(arg)) { + return produceLeftOut(arg); + ~~~~~~ +!!! error TS2322: Type 'LeftOut' is not assignable to type 'Arg extends LeftIn ? LeftOut : Arg extends RightIn ? RightOut : never'. + } else { + return produceRightOut(arg as RightIn); + ~~~~~~ +!!! error TS2322: Type 'RightOut' is not assignable to type 'Arg extends LeftIn ? LeftOut : Arg extends RightIn ? RightOut : never'. + } + } + + interface Animal { + name: string; + } + + interface Dog extends Animal { + bark: () => string; + } + + // This would be unsafe to narrow. + declare function isDog(x: Animal): x is Dog; + declare function doggy(x: Dog): number; + function f12(x: T): T extends Dog ? number : string { + if (isDog(x)) { // `x` has type `T & Dog` here + return doggy(x); + ~~~~~~ +!!! error TS2322: Type 'number' is not assignable to type 'T extends Dog ? number : string'. + } + return ""; // Error: Should not work because we can't express "not a Dog" in the type system + ~~~~~~ +!!! error TS2322: Type 'string' is not assignable to type 'T extends Dog ? number : string'. + } + + // Cannot narrow `keyof` too eagerly or something like the below breaks + function f(entry: EntryId): Entry[EntryId] { + const entries = {} as Entry; + return entries[entry]; + } + + // Works the same as before + declare function takeA(val: 'A'): void; + export function bounceAndTakeIfA(value: AB): AB { + if (value === 'A') { + takeA(value); + takeAB(value); + return value; + } + + return value; + function takeAB(val: AB): void {} + } + + // Works the same as before + export function bbb(value: AB): "a" { + if (value === "a") { + return value; + } + return "a"; + } + + class Unnamed { + root!: { name: string }; + // Error: No narrowing because parameter is optional but `T` doesn't allow for undefined + name(name?: T): T extends string ? this : T extends undefined ? string : never { + if (typeof name === 'undefined') { + return this.root.name; + ~~~~~~ +!!! error TS2322: Type 'string' is not assignable to type 'T extends string ? this : T extends undefined ? string : never'. + } + return this; + ~~~~~~ +!!! error TS2322: Type 'this' is not assignable to type 'T extends string ? this : T extends undefined ? string : never'. +!!! error TS2322: Type 'Unnamed' is not assignable to type 'T extends string ? this : T extends undefined ? string : never'. + } + + // Good conditional + name2(name?: T): T extends string ? this : T extends undefined ? string : never { + if (typeof name === 'undefined') { + return this.root.name; // Ok + } + this.root.name = name; + return this; // Ok + } + + // Good conditional, wrong return expressions + name3(name?: T): T extends string ? this : T extends undefined ? string : never { + if (typeof name === 'undefined') { + return this; // Error + ~~~~~~ +!!! error TS2322: Type 'this' is not assignable to type 'string'. +!!! error TS2322: Type 'Unnamed' is not assignable to type 'string'. + } + this.root.name = name; + return name; // Error + ~~~~~~ +!!! error TS2322: Type 'T & {}' is not assignable to type 'this'. +!!! error TS2322: 'this' could be instantiated with an arbitrary type which could be unrelated to 'T & {}'. + } + } + + // Conditional expressions + interface Aa { + 1: number; + 2: string; + 3: boolean; + } + + function trivialConditional(x: T): Aa[T] { + if (x !== 1) { + return x === 2 ? "" : true; + } + else { + return 0; + } + } + + function conditional(x: T): + T extends true ? 1 : T extends false ? 2 : never { + return x ? 1 : 2; // Ok + } + + function contextualConditional( + x: T + ): T extends "a" ? "a" : T extends "b" ? number : never { + return x === "a" ? x : parseInt(x); // Ok + } + + function conditionalWithError( + x: T + ): T extends "a" ? number : T extends "b" ? string : never { + return x === "a" ? x : parseInt(x); // Error + ~ +!!! error TS2322: Type 'string' is not assignable to type 'number'. + ~~~~~~~~~~~ +!!! error TS2322: Type 'number' is not assignable to type 'string'. + } + + // Multiple indexed type reductions + interface BB { + "a": number; + [y: number]: string; + } + + interface AA { + "c": BB[T]; + "d": boolean, + } + + function reduction(x: T, y: U): AA[U] { + if (y === "c" && x === "a") { + // AA[U='c'] -> BB[T] + // BB[T='a'] -> number + return 0; // Ok + } + + return undefined as never; + } + + // Substitution types are not narrowed + function subsCond( + x: T, + ): T extends 1 | 2 + ? T extends 1 + ? string + : T extends 2 + ? boolean + : never + : T extends 3 + ? number + : never { + if (x === 1) { + return ""; + ~~~~~~ +!!! error TS2322: Type '""' is not assignable to type 'T extends 1 | 2 ? T extends 1 ? string : T extends 2 ? boolean : never : T extends 3 ? number : never'. + } else if (x == 2) { + return true; + ~~~~~~ +!!! error TS2322: Type 'true' is not assignable to type 'T extends 1 | 2 ? T extends 1 ? string : T extends 2 ? boolean : never : T extends 3 ? number : never'. + } + return 3; + ~~~~~~ +!!! error TS2322: Type '3' is not assignable to type 'T extends 1 | 2 ? T extends 1 ? string : T extends 2 ? boolean : never : T extends 3 ? number : never'. + } + + + // Unsafe: check types overlap + declare function q(x: object): x is { b: number }; + function foo( + x: T, + ): T extends { a: string } ? number : T extends { b: number } ? string : never { + if (q(x)) { + x.b; + return ""; + ~~~~~~ +!!! error TS2322: Type 'string' is not assignable to type 'T extends { a: string; } ? number : T extends { b: number; } ? string : never'. + } + x.a; + return 1; + ~~~~~~ +!!! error TS2322: Type 'number' is not assignable to type 'T extends { a: string; } ? number : T extends { b: number; } ? string : never'. + } + + let y = { a: "", b: 1 } + const r = foo<{ a: string }>(y); // type says number but actually string + + type HelperCond = T extends A ? R1 : T extends B ? R2 : never; + + // We don't narrow the return type because the conditionals are not distributive + function foo2(x: U, y: V): + HelperCond<{ x: U, y: V }, + { x: string, y: true }, 1, + { x: number, y: false }, 2> { + if (typeof x === "string" && y === true) { + return 1; // Error + ~~~~~~ +!!! error TS2322: Type '1' is not assignable to type 'HelperCond<{ x: U; y: V; }, { x: string; y: true; }, 1, { x: number; y: false; }, 2>'. + } + if (typeof x === "number" && y === false) { + return 2; // Error + ~~~~~~ +!!! error TS2322: Type '2' is not assignable to type 'HelperCond<{ x: U; y: V; }, { x: string; y: true; }, 1, { x: number; y: false; }, 2>'. + } + return 0; // Error + ~~~~~~ +!!! error TS2322: Type '0' is not assignable to type 'HelperCond<{ x: U; y: V; }, { x: string; y: true; }, 1, { x: number; y: false; }, 2>'. + } + + // From https://github.com/microsoft/TypeScript/issues/24929#issue-332087943 + declare function isString(s: unknown): s is string; + // capitalize a string or each element of an array of strings + function capitalize( + input: T + ): T extends string[] ? string[] : T extends string ? string : never { + if (isString(input)) { + return input[0].toUpperCase() + input.slice(1); // Ok + } else { + return input.map(elt => capitalize(elt)); // Ok + } + } + + function badCapitalize( + input: T + ): T extends string[] ? string[] : T extends string ? string : never { + if (isString(input)) { + return input[0].toUpperCase() + input.slice(1); // Ok + } else { + return input[0].toUpperCase() + input.slice(1); // Bad, error + ~~~~~~ +!!! error TS2322: Type 'string' is not assignable to type 'string[]'. + } + } + + // No narrowing because conditional's extends type is different from type parameter constraint types + function voidRet( + x: T + ): T extends {} ? void : T extends undefined ? number : never { + if (x) { + return; + } + return 1; + } + + // Multiple type parameters at once + function woo( + x: T, + y: U, + ): T extends string + ? U extends string + ? 1 + : U extends number + ? 2 + : never + : T extends number + ? U extends number + ? 3 + : U extends string + ? 4 + : never + : never { + if (typeof x === "number" && typeof y === "string") { + return 1; // Good error + ~~~~~~ +!!! error TS2322: Type '1' is not assignable to type '4'. + } + return undefined as any; + } + + function ttt( + x: T, + y: U, + ): T extends string + ? U extends string + ? 1 + : U extends number + ? 2 + : never + : T extends number + ? U extends number + ? 3 + : U extends string + ? 4 + : never + : never { + if (typeof x === "number" && typeof y === "string") { + return 4; // Ok + } + + return undefined as any; + } + + // Shadowing of the narrowed reference + function shadowing(x: T): T extends 1 ? number : T extends 2 ? string : never { + if (true) { + let x: number = Math.random() ? 1 : 2; + if (x === 1) { + return 1; // Error + ~~~~~~ +!!! error TS2322: Type 'number' is not assignable to type 'T extends 1 ? number : T extends 2 ? string : never'. + } + return ""; // Error + ~~~~~~ +!!! error TS2322: Type 'string' is not assignable to type 'T extends 1 ? number : T extends 2 ? string : never'. + } + } + + function noShadowing(x: T): T extends 1 ? number : T extends 2 ? string : never { + if (true) { + if (x === 1) { + return 1; // Ok + } + return ""; // Ok + } + } + + // If the narrowing reference is out of scope, we simply won't narrow its type + declare let someX: boolean; + function scope2(a: T): T extends true ? 1 : T extends false ? 2 : never { + if ((true)) { + const someX = a; + if (someX) { // We narrow `someX` and the return type here + return 1; + } + } + if (!someX) { // This is a different `someX`, so we don't narrow here + return 2; + ~~~~~~ +!!! error TS2322: Type '2' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'. + } + + return undefined as any; + } + + function moreShadowing(x: T): T extends 1 ? number : T extends 2 ? string : never { + if (x === 2) { + let x: number = Math.random() ? 1 : 2; + if (x === 1) { + return 1; // Error + ~~~~~~ +!!! error TS2322: Type 'number' is not assignable to type 'string'. + } + return ""; // Ok + } + return 0; // Ok + } + + // This would be unsafe to narrow due to `infer` type. + function withInfer(x: T): T extends [infer R] ? R : T extends number ? boolean : never { + if (typeof x === "number") { + return true; + ~~~~~~ +!!! error TS2322: Type 'true' is not assignable to type 'T extends [infer R] ? R : T extends number ? boolean : never'. + } + return ""; + ~~~~~~ +!!! error TS2322: Type '""' is not assignable to type 'T extends [infer R] ? R : T extends number ? boolean : never'. + } + + const withInferResult = withInfer(["a"] as const); // The type says it returns `"a"`, but the function actually returns `""`. + + // Ok + async function abool(x: T): Promise { + if (x) { + return 1; + } + return 2; + } + + // Ok + function* bbool(x: T): Generator { + yield 3; + if (x) { + return 1; + } + return 2; + } + + // We don't do the same type of narrowing for `yield` statements + function* cbool(x: T): Generator { + if (x) { + yield 1; + ~ +!!! error TS2322: Type '1' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'. + } + yield 2; + ~ +!!! error TS2322: Type '2' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'. + return 0; + } + + // From #33912 + abstract class Operation { + abstract perform(t: T): R; + } + + type ConditionalReturnType | undefined> = + EOp extends Operation ? R : EOp extends undefined ? T | R : never; + + + class ConditionalOperation< + T, + R, + EOp extends Operation | undefined, + > extends Operation> { + constructor( + private predicate: (value: T) => boolean, + private thenOp: Operation, + private elseOp?: EOp, + ) { + super(); + } + + // We won't try to narrow the return type because `T` is declared on the class and we don't analyze this case. + perform(t: T): ConditionalReturnType { + if (this.predicate(t)) { + return this.thenOp.perform(t); // Bad: this is assignable to all of the branches of the conditional, but we still can't return it + ~~~~~~ +!!! error TS2322: Type 'R' is not assignable to type 'ConditionalReturnType'. + } else if (typeof this.elseOp !== "undefined") { + return this.elseOp.perform(t); // Would be ok + ~~~~~~ +!!! error TS2322: Type 'R' is not assignable to type 'ConditionalReturnType'. + } else { + return t; // Would be ok + ~~~~~~ +!!! error TS2322: Type 'T' is not assignable to type 'ConditionalReturnType'. + } + } + } + + // Like the version above, we will not attempt to narrow because there's more than one reference to `T`, + // because `T` shows up in the type of `predicate`. + function perform | undefined>( + t: T, + predicate: (value: T) => boolean, + thenOp: Operation, + elseOp?: EOp, + ): ConditionalReturnType { + if (predicate(t)) { + return thenOp.perform(t); // Bad: this is assignable to all of the branches of the conditional, but we still can't return it + ~~~~~~ +!!! error TS2322: Type 'R' is not assignable to type 'ConditionalReturnType'. + } else if (elseOp !== undefined) { + return elseOp.perform(t); // Would be ok + } else { + return t; // Would be ok + } + } + + // Return conditional expressions with parentheses + function returnStuff1(x: T ): T extends true ? 1 : T extends false ? 2 : never { + return (x ? (1) : 2); + } + + function returnStuff2(x: T ): + T extends 1 ? "one" : T extends 2 ? "two" : T extends "a" ? 0 : never { + return (typeof x === "string" ? 0 : (x === 1 ? ("one") : "two")); + } + + // If the conditional type's input is `never`, then it resolves to `never`: + function neverOk(x: T): T extends true ? 1 : T extends false ? 2 : never { + if (x === true) { + return 1; + } + if (x === false) { + return 2; + } + return 1; + ~~~~~~ +!!! error TS2322: Type '1' is not assignable to type 'never'. + } \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/dependentReturnType1.symbols b/tsc/testdata/baselines/reference/compiler/dependentReturnType1.symbols new file mode 100644 index 0000000000000..21111daa5b92a --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/dependentReturnType1.symbols @@ -0,0 +1,1386 @@ +//// [tests/cases/compiler/dependentReturnType1.ts] //// + +=== dependentReturnType1.ts === +interface A { +>A : Symbol(A, Decl(dependentReturnType1.ts, 0, 0)) + + 1: number; +>1 : Symbol(A[1], Decl(dependentReturnType1.ts, 0, 13)) + + 2: string; +>2 : Symbol(A[2], Decl(dependentReturnType1.ts, 1, 14)) +} + +function f1(x: T): A[T] { +>f1 : Symbol(f1, Decl(dependentReturnType1.ts, 3, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 5, 12)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 5, 29)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 5, 12)) +>A : Symbol(A, Decl(dependentReturnType1.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 5, 12)) + + if (x === 1) { +>x : Symbol(x, Decl(dependentReturnType1.ts, 5, 29)) + + return 0; // Ok + } + else { + return 1; // Error + } +} + +interface C { +>C : Symbol(C, Decl(dependentReturnType1.ts, 12, 1)) + + 1: number; +>1 : Symbol(C[1], Decl(dependentReturnType1.ts, 14, 13)) + + 2: string; +>2 : Symbol(C[2], Decl(dependentReturnType1.ts, 15, 14)) + + 3: boolean; +>3 : Symbol(C[3], Decl(dependentReturnType1.ts, 16, 14)) +} + +function f2(x: T): C[T] { +>f2 : Symbol(f2, Decl(dependentReturnType1.ts, 18, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 20, 12)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 20, 33)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 20, 12)) +>C : Symbol(C, Decl(dependentReturnType1.ts, 12, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 20, 12)) + + if (x === 1) { +>x : Symbol(x, Decl(dependentReturnType1.ts, 20, 33)) + + return 0; // Ok + } + else { + return ""; // Error, returned expression needs to have type string & boolean (= never) + } +} + +function f3(x: T): T extends 1 ? number : T extends 2 ? string : T extends 3 ? boolean : never { +>f3 : Symbol(f3, Decl(dependentReturnType1.ts, 27, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 29, 12)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 29, 33)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 29, 12)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 29, 12)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 29, 12)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 29, 12)) + + if (x === 1) { +>x : Symbol(x, Decl(dependentReturnType1.ts, 29, 33)) + + return 0; // Ok + } + else { + return ""; // Error, returned expression needs to have type string & boolean (= never) + } +} + +interface One { +>One : Symbol(One, Decl(dependentReturnType1.ts, 36, 1)) + + a: "a"; +>a : Symbol(One.a, Decl(dependentReturnType1.ts, 38, 15)) + + b: "b"; +>b : Symbol(One.b, Decl(dependentReturnType1.ts, 39, 11)) + + c: "c"; +>c : Symbol(One.c, Decl(dependentReturnType1.ts, 40, 11)) + + d: "d"; +>d : Symbol(One.d, Decl(dependentReturnType1.ts, 41, 11)) +} + +interface Two { +>Two : Symbol(Two, Decl(dependentReturnType1.ts, 43, 1)) + + a: "a"; +>a : Symbol(Two.a, Decl(dependentReturnType1.ts, 45, 15)) + + b: "b"; +>b : Symbol(Two.b, Decl(dependentReturnType1.ts, 46, 11)) + + e: "e"; +>e : Symbol(Two.e, Decl(dependentReturnType1.ts, 47, 11)) + + f: "f"; +>f : Symbol(Two.f, Decl(dependentReturnType1.ts, 48, 11)) +} + +interface Three { +>Three : Symbol(Three, Decl(dependentReturnType1.ts, 50, 1)) + + a: "a"; +>a : Symbol(Three.a, Decl(dependentReturnType1.ts, 52, 17)) + + c: "c"; +>c : Symbol(Three.c, Decl(dependentReturnType1.ts, 53, 11)) + + e: "e"; +>e : Symbol(Three.e, Decl(dependentReturnType1.ts, 54, 11)) + + g: "g"; +>g : Symbol(Three.g, Decl(dependentReturnType1.ts, 55, 11)) +} + +interface Four { +>Four : Symbol(Four, Decl(dependentReturnType1.ts, 57, 1)) + + a: "a"; +>a : Symbol(Four.a, Decl(dependentReturnType1.ts, 59, 16)) + + d: "d"; +>d : Symbol(Four.d, Decl(dependentReturnType1.ts, 60, 11)) + + f: "f"; +>f : Symbol(Four.f, Decl(dependentReturnType1.ts, 61, 11)) + + g: "g"; +>g : Symbol(Four.g, Decl(dependentReturnType1.ts, 62, 11)) +} +// Badly written conditional return type, will not trigger narrowing +function f10(x: T): T extends 1 ? One : T extends 2 ? Two : T extends 3 ? Three : Four { +>f10 : Symbol(f10, Decl(dependentReturnType1.ts, 64, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 66, 13)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 66, 38)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 66, 13)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 66, 13)) +>One : Symbol(One, Decl(dependentReturnType1.ts, 36, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 66, 13)) +>Two : Symbol(Two, Decl(dependentReturnType1.ts, 43, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 66, 13)) +>Three : Symbol(Three, Decl(dependentReturnType1.ts, 50, 1)) +>Four : Symbol(Four, Decl(dependentReturnType1.ts, 57, 1)) + + if (x === 1 || x === 2) { +>x : Symbol(x, Decl(dependentReturnType1.ts, 66, 38)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 66, 38)) + + return { a: "a", b: "b", c: "c", d: "d", e: "e", f: "f" }; // Error +>a : Symbol(a, Decl(dependentReturnType1.ts, 68, 16)) +>b : Symbol(b, Decl(dependentReturnType1.ts, 68, 24)) +>c : Symbol(c, Decl(dependentReturnType1.ts, 68, 32)) +>d : Symbol(d, Decl(dependentReturnType1.ts, 68, 40)) +>e : Symbol(e, Decl(dependentReturnType1.ts, 68, 48)) +>f : Symbol(f, Decl(dependentReturnType1.ts, 68, 56)) + } + return { a: "a", b: "b", c: "c", d: "d", e: "e", f: "f", g: "g" }; // Error +>a : Symbol(a, Decl(dependentReturnType1.ts, 70, 12)) +>b : Symbol(b, Decl(dependentReturnType1.ts, 70, 20)) +>c : Symbol(c, Decl(dependentReturnType1.ts, 70, 28)) +>d : Symbol(d, Decl(dependentReturnType1.ts, 70, 36)) +>e : Symbol(e, Decl(dependentReturnType1.ts, 70, 44)) +>f : Symbol(f, Decl(dependentReturnType1.ts, 70, 52)) +>g : Symbol(g, Decl(dependentReturnType1.ts, 70, 60)) +} +// Well written conditional +function f101(x: T): T extends 1 ? One : T extends 2 ? Two : T extends 3 ? Three : T extends 4 ? Four : never { +>f101 : Symbol(f101, Decl(dependentReturnType1.ts, 71, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 73, 14)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 73, 39)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 73, 14)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 73, 14)) +>One : Symbol(One, Decl(dependentReturnType1.ts, 36, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 73, 14)) +>Two : Symbol(Two, Decl(dependentReturnType1.ts, 43, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 73, 14)) +>Three : Symbol(Three, Decl(dependentReturnType1.ts, 50, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 73, 14)) +>Four : Symbol(Four, Decl(dependentReturnType1.ts, 57, 1)) + + if (x === 1 || x === 2) { +>x : Symbol(x, Decl(dependentReturnType1.ts, 73, 39)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 73, 39)) + + return { a: "a", b: "b", c: "c", d: "d", e: "e", f: "f" }; // Ok +>a : Symbol(a, Decl(dependentReturnType1.ts, 75, 16)) +>b : Symbol(b, Decl(dependentReturnType1.ts, 75, 24)) +>c : Symbol(c, Decl(dependentReturnType1.ts, 75, 32)) +>d : Symbol(d, Decl(dependentReturnType1.ts, 75, 40)) +>e : Symbol(e, Decl(dependentReturnType1.ts, 75, 48)) +>f : Symbol(f, Decl(dependentReturnType1.ts, 75, 56)) + } + // Excess property becomes a problem with the change, + // because we now check assignability to a narrower type... + return { a: "a", b: "b", c: "c", d: "d", e: "e", f: "f", g: "g" }; // EPC Error +>a : Symbol(a, Decl(dependentReturnType1.ts, 79, 12)) +>b : Symbol(b, Decl(dependentReturnType1.ts, 79, 20)) +>c : Symbol(c, Decl(dependentReturnType1.ts, 79, 28)) +>d : Symbol(d, Decl(dependentReturnType1.ts, 79, 36)) +>e : Symbol(e, Decl(dependentReturnType1.ts, 79, 44)) +>f : Symbol(f, Decl(dependentReturnType1.ts, 79, 52)) +>g : Symbol(g, Decl(dependentReturnType1.ts, 79, 60)) +} + +// This will not work for several reasons: +// - first because the constraint of type parameter `Arg` is generic, +// so attempting to narrow the type of `arg` in the `if` would result in type `Arg & LeftIn`, +// which when substituted in the conditional return type, would not further resolve that conditional type +// - second because the `else` branch would never work because we don't narrow the type of `arg` to `Arg & RightIn` +function conditionalProducingIf( +>conditionalProducingIf : Symbol(conditionalProducingIf, Decl(dependentReturnType1.ts, 80, 1)) +>LeftIn : Symbol(LeftIn, Decl(dependentReturnType1.ts, 87, 32)) +>RightIn : Symbol(RightIn, Decl(dependentReturnType1.ts, 87, 39)) +>LeftOut : Symbol(LeftOut, Decl(dependentReturnType1.ts, 87, 48)) +>RightOut : Symbol(RightOut, Decl(dependentReturnType1.ts, 87, 57)) +>Arg : Symbol(Arg, Decl(dependentReturnType1.ts, 87, 67)) +>LeftIn : Symbol(LeftIn, Decl(dependentReturnType1.ts, 87, 32)) +>RightIn : Symbol(RightIn, Decl(dependentReturnType1.ts, 87, 39)) + + arg: Arg, +>arg : Symbol(arg, Decl(dependentReturnType1.ts, 87, 98)) +>Arg : Symbol(Arg, Decl(dependentReturnType1.ts, 87, 67)) + + cond: (arg: LeftIn | RightIn) => arg is LeftIn, +>cond : Symbol(cond, Decl(dependentReturnType1.ts, 88, 13)) +>arg : Symbol(arg, Decl(dependentReturnType1.ts, 89, 11)) +>LeftIn : Symbol(LeftIn, Decl(dependentReturnType1.ts, 87, 32)) +>RightIn : Symbol(RightIn, Decl(dependentReturnType1.ts, 87, 39)) +>arg : Symbol(arg, Decl(dependentReturnType1.ts, 89, 11)) +>LeftIn : Symbol(LeftIn, Decl(dependentReturnType1.ts, 87, 32)) + + produceLeftOut: (arg: LeftIn) => LeftOut, +>produceLeftOut : Symbol(produceLeftOut, Decl(dependentReturnType1.ts, 89, 51)) +>arg : Symbol(arg, Decl(dependentReturnType1.ts, 90, 21)) +>LeftIn : Symbol(LeftIn, Decl(dependentReturnType1.ts, 87, 32)) +>LeftOut : Symbol(LeftOut, Decl(dependentReturnType1.ts, 87, 48)) + + produceRightOut: (arg: RightIn) => RightOut): +>produceRightOut : Symbol(produceRightOut, Decl(dependentReturnType1.ts, 90, 45)) +>arg : Symbol(arg, Decl(dependentReturnType1.ts, 91, 22)) +>RightIn : Symbol(RightIn, Decl(dependentReturnType1.ts, 87, 39)) +>RightOut : Symbol(RightOut, Decl(dependentReturnType1.ts, 87, 57)) + + Arg extends LeftIn ? LeftOut : Arg extends RightIn ? RightOut : never +>Arg : Symbol(Arg, Decl(dependentReturnType1.ts, 87, 67)) +>LeftIn : Symbol(LeftIn, Decl(dependentReturnType1.ts, 87, 32)) +>LeftOut : Symbol(LeftOut, Decl(dependentReturnType1.ts, 87, 48)) +>Arg : Symbol(Arg, Decl(dependentReturnType1.ts, 87, 67)) +>RightIn : Symbol(RightIn, Decl(dependentReturnType1.ts, 87, 39)) +>RightOut : Symbol(RightOut, Decl(dependentReturnType1.ts, 87, 57)) +{ + if (cond(arg)) { +>cond : Symbol(cond, Decl(dependentReturnType1.ts, 88, 13)) +>arg : Symbol(arg, Decl(dependentReturnType1.ts, 87, 98)) + + return produceLeftOut(arg); +>produceLeftOut : Symbol(produceLeftOut, Decl(dependentReturnType1.ts, 89, 51)) +>arg : Symbol(arg, Decl(dependentReturnType1.ts, 87, 98)) + + } else { + return produceRightOut(arg as RightIn); +>produceRightOut : Symbol(produceRightOut, Decl(dependentReturnType1.ts, 90, 45)) +>arg : Symbol(arg, Decl(dependentReturnType1.ts, 87, 98)) +>RightIn : Symbol(RightIn, Decl(dependentReturnType1.ts, 87, 39)) + } +} + +interface Animal { +>Animal : Symbol(Animal, Decl(dependentReturnType1.ts, 99, 1)) + + name: string; +>name : Symbol(Animal.name, Decl(dependentReturnType1.ts, 101, 18)) +} + +interface Dog extends Animal { +>Dog : Symbol(Dog, Decl(dependentReturnType1.ts, 103, 1)) +>Animal : Symbol(Animal, Decl(dependentReturnType1.ts, 99, 1)) + + bark: () => string; +>bark : Symbol(Dog.bark, Decl(dependentReturnType1.ts, 105, 30)) +} + +// This would be unsafe to narrow. +declare function isDog(x: Animal): x is Dog; +>isDog : Symbol(isDog, Decl(dependentReturnType1.ts, 107, 1)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 110, 23)) +>Animal : Symbol(Animal, Decl(dependentReturnType1.ts, 99, 1)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 110, 23)) +>Dog : Symbol(Dog, Decl(dependentReturnType1.ts, 103, 1)) + +declare function doggy(x: Dog): number; +>doggy : Symbol(doggy, Decl(dependentReturnType1.ts, 110, 44)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 111, 23)) +>Dog : Symbol(Dog, Decl(dependentReturnType1.ts, 103, 1)) + +function f12(x: T): T extends Dog ? number : string { +>f12 : Symbol(f12, Decl(dependentReturnType1.ts, 111, 39)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 112, 13)) +>Animal : Symbol(Animal, Decl(dependentReturnType1.ts, 99, 1)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 112, 31)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 112, 13)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 112, 13)) +>Dog : Symbol(Dog, Decl(dependentReturnType1.ts, 103, 1)) + + if (isDog(x)) { // `x` has type `T & Dog` here +>isDog : Symbol(isDog, Decl(dependentReturnType1.ts, 107, 1)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 112, 31)) + + return doggy(x); +>doggy : Symbol(doggy, Decl(dependentReturnType1.ts, 110, 44)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 112, 31)) + } + return ""; // Error: Should not work because we can't express "not a Dog" in the type system +} + +// Cannot narrow `keyof` too eagerly or something like the below breaks +function f(entry: EntryId): Entry[EntryId] { +>f : Symbol(f, Decl(dependentReturnType1.ts, 117, 1)) +>Entry : Symbol(Entry, Decl(dependentReturnType1.ts, 120, 11)) +>index : Symbol(index, Decl(dependentReturnType1.ts, 120, 28)) +>EntryId : Symbol(EntryId, Decl(dependentReturnType1.ts, 120, 63)) +>Entry : Symbol(Entry, Decl(dependentReturnType1.ts, 120, 11)) +>entry : Symbol(entry, Decl(dependentReturnType1.ts, 120, 93)) +>EntryId : Symbol(EntryId, Decl(dependentReturnType1.ts, 120, 63)) +>Entry : Symbol(Entry, Decl(dependentReturnType1.ts, 120, 11)) +>EntryId : Symbol(EntryId, Decl(dependentReturnType1.ts, 120, 63)) + + const entries = {} as Entry; +>entries : Symbol(entries, Decl(dependentReturnType1.ts, 121, 9)) +>Entry : Symbol(Entry, Decl(dependentReturnType1.ts, 120, 11)) + + return entries[entry]; +>entries : Symbol(entries, Decl(dependentReturnType1.ts, 121, 9)) +>entry : Symbol(entry, Decl(dependentReturnType1.ts, 120, 93)) +} + +// Works the same as before +declare function takeA(val: 'A'): void; +>takeA : Symbol(takeA, Decl(dependentReturnType1.ts, 123, 1)) +>val : Symbol(val, Decl(dependentReturnType1.ts, 126, 23)) + +export function bounceAndTakeIfA(value: AB): AB { +>bounceAndTakeIfA : Symbol(bounceAndTakeIfA, Decl(dependentReturnType1.ts, 126, 39)) +>AB : Symbol(AB, Decl(dependentReturnType1.ts, 127, 33)) +>value : Symbol(value, Decl(dependentReturnType1.ts, 127, 55)) +>AB : Symbol(AB, Decl(dependentReturnType1.ts, 127, 33)) +>AB : Symbol(AB, Decl(dependentReturnType1.ts, 127, 33)) + + if (value === 'A') { +>value : Symbol(value, Decl(dependentReturnType1.ts, 127, 55)) + + takeA(value); +>takeA : Symbol(takeA, Decl(dependentReturnType1.ts, 123, 1)) +>value : Symbol(value, Decl(dependentReturnType1.ts, 127, 55)) + + takeAB(value); +>takeAB : Symbol(takeAB, Decl(dependentReturnType1.ts, 134, 17)) +>value : Symbol(value, Decl(dependentReturnType1.ts, 127, 55)) + + return value; +>value : Symbol(value, Decl(dependentReturnType1.ts, 127, 55)) + } + + return value; +>value : Symbol(value, Decl(dependentReturnType1.ts, 127, 55)) + + function takeAB(val: AB): void {} +>takeAB : Symbol(takeAB, Decl(dependentReturnType1.ts, 134, 17)) +>val : Symbol(val, Decl(dependentReturnType1.ts, 135, 20)) +>AB : Symbol(AB, Decl(dependentReturnType1.ts, 127, 33)) +} + +// Works the same as before +export function bbb(value: AB): "a" { +>bbb : Symbol(bbb, Decl(dependentReturnType1.ts, 136, 1)) +>AB : Symbol(AB, Decl(dependentReturnType1.ts, 139, 20)) +>value : Symbol(value, Decl(dependentReturnType1.ts, 139, 42)) +>AB : Symbol(AB, Decl(dependentReturnType1.ts, 139, 20)) + + if (value === "a") { +>value : Symbol(value, Decl(dependentReturnType1.ts, 139, 42)) + + return value; +>value : Symbol(value, Decl(dependentReturnType1.ts, 139, 42)) + } + return "a"; +} + +class Unnamed { +>Unnamed : Symbol(Unnamed, Decl(dependentReturnType1.ts, 144, 1)) + + root!: { name: string }; +>root : Symbol(Unnamed.root, Decl(dependentReturnType1.ts, 146, 15)) +>name : Symbol(name, Decl(dependentReturnType1.ts, 147, 12)) + + // Error: No narrowing because parameter is optional but `T` doesn't allow for undefined + name(name?: T): T extends string ? this : T extends undefined ? string : never { +>name : Symbol(Unnamed.name, Decl(dependentReturnType1.ts, 147, 28)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 149, 9)) +>name : Symbol(name, Decl(dependentReturnType1.ts, 149, 27)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 149, 9)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 149, 9)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 149, 9)) + + if (typeof name === 'undefined') { +>name : Symbol(name, Decl(dependentReturnType1.ts, 149, 27)) + + return this.root.name; +>this.root.name : Symbol(name, Decl(dependentReturnType1.ts, 147, 12)) +>this.root : Symbol(Unnamed.root, Decl(dependentReturnType1.ts, 146, 15)) +>this : Symbol(Unnamed, Decl(dependentReturnType1.ts, 144, 1)) +>root : Symbol(Unnamed.root, Decl(dependentReturnType1.ts, 146, 15)) +>name : Symbol(name, Decl(dependentReturnType1.ts, 147, 12)) + } + return this; +>this : Symbol(Unnamed, Decl(dependentReturnType1.ts, 144, 1)) + } + + // Good conditional + name2(name?: T): T extends string ? this : T extends undefined ? string : never { +>name2 : Symbol(Unnamed.name2, Decl(dependentReturnType1.ts, 154, 5)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 157, 10)) +>name : Symbol(name, Decl(dependentReturnType1.ts, 157, 40)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 157, 10)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 157, 10)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 157, 10)) + + if (typeof name === 'undefined') { +>name : Symbol(name, Decl(dependentReturnType1.ts, 157, 40)) + + return this.root.name; // Ok +>this.root.name : Symbol(name, Decl(dependentReturnType1.ts, 147, 12)) +>this.root : Symbol(Unnamed.root, Decl(dependentReturnType1.ts, 146, 15)) +>this : Symbol(Unnamed, Decl(dependentReturnType1.ts, 144, 1)) +>root : Symbol(Unnamed.root, Decl(dependentReturnType1.ts, 146, 15)) +>name : Symbol(name, Decl(dependentReturnType1.ts, 147, 12)) + } + this.root.name = name; +>this.root.name : Symbol(name, Decl(dependentReturnType1.ts, 147, 12)) +>this.root : Symbol(Unnamed.root, Decl(dependentReturnType1.ts, 146, 15)) +>this : Symbol(Unnamed, Decl(dependentReturnType1.ts, 144, 1)) +>root : Symbol(Unnamed.root, Decl(dependentReturnType1.ts, 146, 15)) +>name : Symbol(name, Decl(dependentReturnType1.ts, 147, 12)) +>name : Symbol(name, Decl(dependentReturnType1.ts, 157, 40)) + + return this; // Ok +>this : Symbol(Unnamed, Decl(dependentReturnType1.ts, 144, 1)) + } + + // Good conditional, wrong return expressions + name3(name?: T): T extends string ? this : T extends undefined ? string : never { +>name3 : Symbol(Unnamed.name3, Decl(dependentReturnType1.ts, 163, 5)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 166, 10)) +>name : Symbol(name, Decl(dependentReturnType1.ts, 166, 40)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 166, 10)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 166, 10)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 166, 10)) + + if (typeof name === 'undefined') { +>name : Symbol(name, Decl(dependentReturnType1.ts, 166, 40)) + + return this; // Error +>this : Symbol(Unnamed, Decl(dependentReturnType1.ts, 144, 1)) + } + this.root.name = name; +>this.root.name : Symbol(name, Decl(dependentReturnType1.ts, 147, 12)) +>this.root : Symbol(Unnamed.root, Decl(dependentReturnType1.ts, 146, 15)) +>this : Symbol(Unnamed, Decl(dependentReturnType1.ts, 144, 1)) +>root : Symbol(Unnamed.root, Decl(dependentReturnType1.ts, 146, 15)) +>name : Symbol(name, Decl(dependentReturnType1.ts, 147, 12)) +>name : Symbol(name, Decl(dependentReturnType1.ts, 166, 40)) + + return name; // Error +>name : Symbol(name, Decl(dependentReturnType1.ts, 166, 40)) + } +} + +// Conditional expressions +interface Aa { +>Aa : Symbol(Aa, Decl(dependentReturnType1.ts, 173, 1)) + + 1: number; +>1 : Symbol(Aa[1], Decl(dependentReturnType1.ts, 176, 14)) + + 2: string; +>2 : Symbol(Aa[2], Decl(dependentReturnType1.ts, 177, 14)) + + 3: boolean; +>3 : Symbol(Aa[3], Decl(dependentReturnType1.ts, 178, 14)) +} + +function trivialConditional(x: T): Aa[T] { +>trivialConditional : Symbol(trivialConditional, Decl(dependentReturnType1.ts, 180, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 182, 28)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 182, 49)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 182, 28)) +>Aa : Symbol(Aa, Decl(dependentReturnType1.ts, 173, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 182, 28)) + + if (x !== 1) { +>x : Symbol(x, Decl(dependentReturnType1.ts, 182, 49)) + + return x === 2 ? "" : true; +>x : Symbol(x, Decl(dependentReturnType1.ts, 182, 49)) + } + else { + return 0; + } +} + +function conditional(x: T): +>conditional : Symbol(conditional, Decl(dependentReturnType1.ts, 189, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 191, 21)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 191, 40)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 191, 21)) + + T extends true ? 1 : T extends false ? 2 : never { +>T : Symbol(T, Decl(dependentReturnType1.ts, 191, 21)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 191, 21)) + + return x ? 1 : 2; // Ok +>x : Symbol(x, Decl(dependentReturnType1.ts, 191, 40)) +} + +function contextualConditional( +>contextualConditional : Symbol(contextualConditional, Decl(dependentReturnType1.ts, 194, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 196, 31)) + + x: T +>x : Symbol(x, Decl(dependentReturnType1.ts, 196, 52)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 196, 31)) + +): T extends "a" ? "a" : T extends "b" ? number : never { +>T : Symbol(T, Decl(dependentReturnType1.ts, 196, 31)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 196, 31)) + + return x === "a" ? x : parseInt(x); // Ok +>x : Symbol(x, Decl(dependentReturnType1.ts, 196, 52)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 196, 52)) +>parseInt : Symbol(parseInt, Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 196, 52)) +} + +function conditionalWithError( +>conditionalWithError : Symbol(conditionalWithError, Decl(dependentReturnType1.ts, 200, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 202, 30)) + + x: T +>x : Symbol(x, Decl(dependentReturnType1.ts, 202, 51)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 202, 30)) + +): T extends "a" ? number : T extends "b" ? string : never { +>T : Symbol(T, Decl(dependentReturnType1.ts, 202, 30)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 202, 30)) + + return x === "a" ? x : parseInt(x); // Error +>x : Symbol(x, Decl(dependentReturnType1.ts, 202, 51)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 202, 51)) +>parseInt : Symbol(parseInt, Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 202, 51)) +} + +// Multiple indexed type reductions +interface BB { +>BB : Symbol(BB, Decl(dependentReturnType1.ts, 206, 1)) + + "a": number; +>"a" : Symbol(BB["a"], Decl(dependentReturnType1.ts, 209, 14)) + + [y: number]: string; +>y : Symbol(y, Decl(dependentReturnType1.ts, 211, 5)) +} + +interface AA { +>AA : Symbol(AA, Decl(dependentReturnType1.ts, 212, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 214, 13)) +>BB : Symbol(BB, Decl(dependentReturnType1.ts, 206, 1)) + + "c": BB[T]; +>"c" : Symbol(AA["c"], Decl(dependentReturnType1.ts, 214, 34)) +>BB : Symbol(BB, Decl(dependentReturnType1.ts, 206, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 214, 13)) + + "d": boolean, +>"d" : Symbol(AA["d"], Decl(dependentReturnType1.ts, 215, 15)) +} + +function reduction(x: T, y: U): AA[U] { +>reduction : Symbol(reduction, Decl(dependentReturnType1.ts, 217, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 219, 19)) +>BB : Symbol(BB, Decl(dependentReturnType1.ts, 206, 1)) +>U : Symbol(U, Decl(dependentReturnType1.ts, 219, 38)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 219, 60)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 219, 19)) +>y : Symbol(y, Decl(dependentReturnType1.ts, 219, 65)) +>U : Symbol(U, Decl(dependentReturnType1.ts, 219, 38)) +>AA : Symbol(AA, Decl(dependentReturnType1.ts, 212, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 219, 19)) +>U : Symbol(U, Decl(dependentReturnType1.ts, 219, 38)) + + if (y === "c" && x === "a") { +>y : Symbol(y, Decl(dependentReturnType1.ts, 219, 65)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 219, 60)) + + // AA[U='c'] -> BB[T] + // BB[T='a'] -> number + return 0; // Ok + } + + return undefined as never; +>undefined : Symbol(undefined) +} + +// Substitution types are not narrowed +function subsCond( +>subsCond : Symbol(subsCond, Decl(dependentReturnType1.ts, 227, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 230, 18)) + + x: T, +>x : Symbol(x, Decl(dependentReturnType1.ts, 230, 39)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 230, 18)) + +): T extends 1 | 2 +>T : Symbol(T, Decl(dependentReturnType1.ts, 230, 18)) + + ? T extends 1 +>T : Symbol(T, Decl(dependentReturnType1.ts, 230, 18)) + + ? string + : T extends 2 +>T : Symbol(T, Decl(dependentReturnType1.ts, 230, 18)) + + ? boolean + : never + : T extends 3 +>T : Symbol(T, Decl(dependentReturnType1.ts, 230, 18)) + + ? number + : never { + if (x === 1) { +>x : Symbol(x, Decl(dependentReturnType1.ts, 230, 39)) + + return ""; + } else if (x == 2) { +>x : Symbol(x, Decl(dependentReturnType1.ts, 230, 39)) + + return true; + } + return 3; +} + + +// Unsafe: check types overlap +declare function q(x: object): x is { b: number }; +>q : Symbol(q, Decl(dependentReturnType1.ts, 247, 1)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 251, 19)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 251, 19)) +>b : Symbol(b, Decl(dependentReturnType1.ts, 251, 37)) + +function foo( +>foo : Symbol(foo, Decl(dependentReturnType1.ts, 251, 50)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 252, 13)) +>a : Symbol(a, Decl(dependentReturnType1.ts, 252, 24)) +>b : Symbol(b, Decl(dependentReturnType1.ts, 252, 40)) + + x: T, +>x : Symbol(x, Decl(dependentReturnType1.ts, 252, 54)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 252, 13)) + +): T extends { a: string } ? number : T extends { b: number } ? string : never { +>T : Symbol(T, Decl(dependentReturnType1.ts, 252, 13)) +>a : Symbol(a, Decl(dependentReturnType1.ts, 254, 14)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 252, 13)) +>b : Symbol(b, Decl(dependentReturnType1.ts, 254, 49)) + + if (q(x)) { +>q : Symbol(q, Decl(dependentReturnType1.ts, 247, 1)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 252, 54)) + + x.b; +>x.b : Symbol(b, Decl(dependentReturnType1.ts, 252, 40)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 252, 54)) +>b : Symbol(b, Decl(dependentReturnType1.ts, 252, 40)) + + return ""; + } + x.a; +>x.a : Symbol(a, Decl(dependentReturnType1.ts, 252, 24)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 252, 54)) +>a : Symbol(a, Decl(dependentReturnType1.ts, 252, 24)) + + return 1; +} + +let y = { a: "", b: 1 } +>y : Symbol(y, Decl(dependentReturnType1.ts, 263, 3)) +>a : Symbol(a, Decl(dependentReturnType1.ts, 263, 9)) +>b : Symbol(b, Decl(dependentReturnType1.ts, 263, 16)) + +const r = foo<{ a: string }>(y); // type says number but actually string +>r : Symbol(r, Decl(dependentReturnType1.ts, 264, 5)) +>foo : Symbol(foo, Decl(dependentReturnType1.ts, 251, 50)) +>a : Symbol(a, Decl(dependentReturnType1.ts, 264, 15)) +>y : Symbol(y, Decl(dependentReturnType1.ts, 263, 3)) + +type HelperCond = T extends A ? R1 : T extends B ? R2 : never; +>HelperCond : Symbol(HelperCond, Decl(dependentReturnType1.ts, 264, 32)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 266, 16)) +>A : Symbol(A, Decl(dependentReturnType1.ts, 266, 18)) +>R1 : Symbol(R1, Decl(dependentReturnType1.ts, 266, 21)) +>B : Symbol(B, Decl(dependentReturnType1.ts, 266, 25)) +>R2 : Symbol(R2, Decl(dependentReturnType1.ts, 266, 28)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 266, 16)) +>A : Symbol(A, Decl(dependentReturnType1.ts, 266, 18)) +>R1 : Symbol(R1, Decl(dependentReturnType1.ts, 266, 21)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 266, 16)) +>B : Symbol(B, Decl(dependentReturnType1.ts, 266, 25)) +>R2 : Symbol(R2, Decl(dependentReturnType1.ts, 266, 28)) + +// We don't narrow the return type because the conditionals are not distributive +function foo2(x: U, y: V): +>foo2 : Symbol(foo2, Decl(dependentReturnType1.ts, 266, 79)) +>U : Symbol(U, Decl(dependentReturnType1.ts, 269, 14)) +>V : Symbol(V, Decl(dependentReturnType1.ts, 269, 40)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 269, 60)) +>U : Symbol(U, Decl(dependentReturnType1.ts, 269, 14)) +>y : Symbol(y, Decl(dependentReturnType1.ts, 269, 65)) +>V : Symbol(V, Decl(dependentReturnType1.ts, 269, 40)) + + HelperCond<{ x: U, y: V }, +>HelperCond : Symbol(HelperCond, Decl(dependentReturnType1.ts, 264, 32)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 270, 16)) +>U : Symbol(U, Decl(dependentReturnType1.ts, 269, 14)) +>y : Symbol(y, Decl(dependentReturnType1.ts, 270, 22)) +>V : Symbol(V, Decl(dependentReturnType1.ts, 269, 40)) + + { x: string, y: true }, 1, +>x : Symbol(x, Decl(dependentReturnType1.ts, 271, 9)) +>y : Symbol(y, Decl(dependentReturnType1.ts, 271, 20)) + + { x: number, y: false }, 2> { +>x : Symbol(x, Decl(dependentReturnType1.ts, 272, 9)) +>y : Symbol(y, Decl(dependentReturnType1.ts, 272, 20)) + + if (typeof x === "string" && y === true) { +>x : Symbol(x, Decl(dependentReturnType1.ts, 269, 60)) +>y : Symbol(y, Decl(dependentReturnType1.ts, 269, 65)) + + return 1; // Error + } + if (typeof x === "number" && y === false) { +>x : Symbol(x, Decl(dependentReturnType1.ts, 269, 60)) +>y : Symbol(y, Decl(dependentReturnType1.ts, 269, 65)) + + return 2; // Error + } + return 0; // Error +} + +// From https://github.com/microsoft/TypeScript/issues/24929#issue-332087943 +declare function isString(s: unknown): s is string; +>isString : Symbol(isString, Decl(dependentReturnType1.ts, 280, 1)) +>s : Symbol(s, Decl(dependentReturnType1.ts, 283, 26)) +>s : Symbol(s, Decl(dependentReturnType1.ts, 283, 26)) + +// capitalize a string or each element of an array of strings +function capitalize( +>capitalize : Symbol(capitalize, Decl(dependentReturnType1.ts, 283, 51)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 285, 20)) + + input: T +>input : Symbol(input, Decl(dependentReturnType1.ts, 285, 49)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 285, 20)) + +): T extends string[] ? string[] : T extends string ? string : never { +>T : Symbol(T, Decl(dependentReturnType1.ts, 285, 20)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 285, 20)) + + if (isString(input)) { +>isString : Symbol(isString, Decl(dependentReturnType1.ts, 280, 1)) +>input : Symbol(input, Decl(dependentReturnType1.ts, 285, 49)) + + return input[0].toUpperCase() + input.slice(1); // Ok +>input[0].toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --)) +>input : Symbol(input, Decl(dependentReturnType1.ts, 285, 49)) +>toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --)) +>input.slice : Symbol(String.slice, Decl(lib.es5.d.ts, --, --)) +>input : Symbol(input, Decl(dependentReturnType1.ts, 285, 49)) +>slice : Symbol(String.slice, Decl(lib.es5.d.ts, --, --)) + + } else { + return input.map(elt => capitalize(elt)); // Ok +>input.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) +>input : Symbol(input, Decl(dependentReturnType1.ts, 285, 49)) +>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) +>elt : Symbol(elt, Decl(dependentReturnType1.ts, 291, 25)) +>capitalize : Symbol(capitalize, Decl(dependentReturnType1.ts, 283, 51)) +>elt : Symbol(elt, Decl(dependentReturnType1.ts, 291, 25)) + } +} + +function badCapitalize( +>badCapitalize : Symbol(badCapitalize, Decl(dependentReturnType1.ts, 293, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 295, 23)) + + input: T +>input : Symbol(input, Decl(dependentReturnType1.ts, 295, 52)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 295, 23)) + +): T extends string[] ? string[] : T extends string ? string : never { +>T : Symbol(T, Decl(dependentReturnType1.ts, 295, 23)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 295, 23)) + + if (isString(input)) { +>isString : Symbol(isString, Decl(dependentReturnType1.ts, 280, 1)) +>input : Symbol(input, Decl(dependentReturnType1.ts, 295, 52)) + + return input[0].toUpperCase() + input.slice(1); // Ok +>input[0].toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --)) +>input : Symbol(input, Decl(dependentReturnType1.ts, 295, 52)) +>toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --)) +>input.slice : Symbol(String.slice, Decl(lib.es5.d.ts, --, --)) +>input : Symbol(input, Decl(dependentReturnType1.ts, 295, 52)) +>slice : Symbol(String.slice, Decl(lib.es5.d.ts, --, --)) + + } else { + return input[0].toUpperCase() + input.slice(1); // Bad, error +>input[0].toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --)) +>input : Symbol(input, Decl(dependentReturnType1.ts, 295, 52)) +>toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --)) +>input.slice : Symbol(Array.slice, Decl(lib.es5.d.ts, --, --)) +>input : Symbol(input, Decl(dependentReturnType1.ts, 295, 52)) +>slice : Symbol(Array.slice, Decl(lib.es5.d.ts, --, --)) + } +} + +// No narrowing because conditional's extends type is different from type parameter constraint types +function voidRet( +>voidRet : Symbol(voidRet, Decl(dependentReturnType1.ts, 303, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 306, 17)) +>a : Symbol(a, Decl(dependentReturnType1.ts, 306, 28)) + + x: T +>x : Symbol(x, Decl(dependentReturnType1.ts, 306, 54)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 306, 17)) + +): T extends {} ? void : T extends undefined ? number : never { +>T : Symbol(T, Decl(dependentReturnType1.ts, 306, 17)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 306, 17)) + + if (x) { +>x : Symbol(x, Decl(dependentReturnType1.ts, 306, 54)) + + return; + } + return 1; +} + +// Multiple type parameters at once +function woo( +>woo : Symbol(woo, Decl(dependentReturnType1.ts, 313, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 316, 13)) +>U : Symbol(U, Decl(dependentReturnType1.ts, 316, 39)) + + x: T, +>x : Symbol(x, Decl(dependentReturnType1.ts, 316, 67)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 316, 13)) + + y: U, +>y : Symbol(y, Decl(dependentReturnType1.ts, 317, 9)) +>U : Symbol(U, Decl(dependentReturnType1.ts, 316, 39)) + +): T extends string +>T : Symbol(T, Decl(dependentReturnType1.ts, 316, 13)) + + ? U extends string +>U : Symbol(U, Decl(dependentReturnType1.ts, 316, 39)) + + ? 1 + : U extends number +>U : Symbol(U, Decl(dependentReturnType1.ts, 316, 39)) + + ? 2 + : never + : T extends number +>T : Symbol(T, Decl(dependentReturnType1.ts, 316, 13)) + + ? U extends number +>U : Symbol(U, Decl(dependentReturnType1.ts, 316, 39)) + + ? 3 + : U extends string +>U : Symbol(U, Decl(dependentReturnType1.ts, 316, 39)) + + ? 4 + : never + : never { + if (typeof x === "number" && typeof y === "string") { +>x : Symbol(x, Decl(dependentReturnType1.ts, 316, 67)) +>y : Symbol(y, Decl(dependentReturnType1.ts, 317, 9)) + + return 1; // Good error + } + return undefined as any; +>undefined : Symbol(undefined) +} + +function ttt( +>ttt : Symbol(ttt, Decl(dependentReturnType1.ts, 336, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 338, 13)) +>U : Symbol(U, Decl(dependentReturnType1.ts, 338, 39)) + + x: T, +>x : Symbol(x, Decl(dependentReturnType1.ts, 338, 67)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 338, 13)) + + y: U, +>y : Symbol(y, Decl(dependentReturnType1.ts, 339, 9)) +>U : Symbol(U, Decl(dependentReturnType1.ts, 338, 39)) + +): T extends string +>T : Symbol(T, Decl(dependentReturnType1.ts, 338, 13)) + + ? U extends string +>U : Symbol(U, Decl(dependentReturnType1.ts, 338, 39)) + + ? 1 + : U extends number +>U : Symbol(U, Decl(dependentReturnType1.ts, 338, 39)) + + ? 2 + : never + : T extends number +>T : Symbol(T, Decl(dependentReturnType1.ts, 338, 13)) + + ? U extends number +>U : Symbol(U, Decl(dependentReturnType1.ts, 338, 39)) + + ? 3 + : U extends string +>U : Symbol(U, Decl(dependentReturnType1.ts, 338, 39)) + + ? 4 + : never + : never { + if (typeof x === "number" && typeof y === "string") { +>x : Symbol(x, Decl(dependentReturnType1.ts, 338, 67)) +>y : Symbol(y, Decl(dependentReturnType1.ts, 339, 9)) + + return 4; // Ok + } + + return undefined as any; +>undefined : Symbol(undefined) +} + +// Shadowing of the narrowed reference +function shadowing(x: T): T extends 1 ? number : T extends 2 ? string : never { +>shadowing : Symbol(shadowing, Decl(dependentReturnType1.ts, 359, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 362, 19)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 362, 36)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 362, 19)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 362, 19)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 362, 19)) + + if (true) { + let x: number = Math.random() ? 1 : 2; +>x : Symbol(x, Decl(dependentReturnType1.ts, 364, 11)) +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2025.float16.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) + + if (x === 1) { +>x : Symbol(x, Decl(dependentReturnType1.ts, 364, 11)) + + return 1; // Error + } + return ""; // Error + } +} + +function noShadowing(x: T): T extends 1 ? number : T extends 2 ? string : never { +>noShadowing : Symbol(noShadowing, Decl(dependentReturnType1.ts, 370, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 372, 21)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 372, 38)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 372, 21)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 372, 21)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 372, 21)) + + if (true) { + if (x === 1) { +>x : Symbol(x, Decl(dependentReturnType1.ts, 372, 38)) + + return 1; // Ok + } + return ""; // Ok + } +} + +// If the narrowing reference is out of scope, we simply won't narrow its type +declare let someX: boolean; +>someX : Symbol(someX, Decl(dependentReturnType1.ts, 382, 11)) + +function scope2(a: T): T extends true ? 1 : T extends false ? 2 : never { +>scope2 : Symbol(scope2, Decl(dependentReturnType1.ts, 382, 27)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 383, 16)) +>a : Symbol(a, Decl(dependentReturnType1.ts, 383, 35)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 383, 16)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 383, 16)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 383, 16)) + + if ((true)) { + const someX = a; +>someX : Symbol(someX, Decl(dependentReturnType1.ts, 385, 13)) +>a : Symbol(a, Decl(dependentReturnType1.ts, 383, 35)) + + if (someX) { // We narrow `someX` and the return type here +>someX : Symbol(someX, Decl(dependentReturnType1.ts, 385, 13)) + + return 1; + } + } + if (!someX) { // This is a different `someX`, so we don't narrow here +>someX : Symbol(someX, Decl(dependentReturnType1.ts, 382, 11)) + + return 2; + } + + return undefined as any; +>undefined : Symbol(undefined) +} + +function moreShadowing(x: T): T extends 1 ? number : T extends 2 ? string : never { +>moreShadowing : Symbol(moreShadowing, Decl(dependentReturnType1.ts, 395, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 397, 23)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 397, 40)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 397, 23)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 397, 23)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 397, 23)) + + if (x === 2) { +>x : Symbol(x, Decl(dependentReturnType1.ts, 397, 40)) + + let x: number = Math.random() ? 1 : 2; +>x : Symbol(x, Decl(dependentReturnType1.ts, 399, 11)) +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2025.float16.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) + + if (x === 1) { +>x : Symbol(x, Decl(dependentReturnType1.ts, 399, 11)) + + return 1; // Error + } + return ""; // Ok + } + return 0; // Ok +} + +// This would be unsafe to narrow due to `infer` type. +function withInfer(x: T): T extends [infer R] ? R : T extends number ? boolean : never { +>withInfer : Symbol(withInfer, Decl(dependentReturnType1.ts, 406, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 409, 19)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 409, 48)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 409, 19)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 409, 19)) +>R : Symbol(R, Decl(dependentReturnType1.ts, 409, 71)) +>R : Symbol(R, Decl(dependentReturnType1.ts, 409, 71)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 409, 19)) + + if (typeof x === "number") { +>x : Symbol(x, Decl(dependentReturnType1.ts, 409, 48)) + + return true; + } + return ""; +} + +const withInferResult = withInfer(["a"] as const); // The type says it returns `"a"`, but the function actually returns `""`. +>withInferResult : Symbol(withInferResult, Decl(dependentReturnType1.ts, 416, 5)) +>withInfer : Symbol(withInfer, Decl(dependentReturnType1.ts, 406, 1)) +>const : Symbol(const) + +// Ok +async function abool(x: T): Promise { +>abool : Symbol(abool, Decl(dependentReturnType1.ts, 416, 50)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 419, 21)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 419, 45)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 419, 21)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 419, 21)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 419, 21)) + + if (x) { +>x : Symbol(x, Decl(dependentReturnType1.ts, 419, 45)) + + return 1; + } + return 2; +} + +// Ok +function* bbool(x: T): Generator { +>bbool : Symbol(bbool, Decl(dependentReturnType1.ts, 424, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 427, 16)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 427, 40)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 427, 16)) +>Generator : Symbol(Generator, Decl(lib.es2015.generator.d.ts, --, --)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 427, 16)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 427, 16)) + + yield 3; + if (x) { +>x : Symbol(x, Decl(dependentReturnType1.ts, 427, 40)) + + return 1; + } + return 2; +} + +// We don't do the same type of narrowing for `yield` statements +function* cbool(x: T): Generator { +>cbool : Symbol(cbool, Decl(dependentReturnType1.ts, 433, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 436, 16)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 436, 40)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 436, 16)) +>Generator : Symbol(Generator, Decl(lib.es2015.generator.d.ts, --, --)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 436, 16)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 436, 16)) + + if (x) { +>x : Symbol(x, Decl(dependentReturnType1.ts, 436, 40)) + + yield 1; + } + yield 2; + return 0; +} + +// From #33912 +abstract class Operation { +>Operation : Symbol(Operation, Decl(dependentReturnType1.ts, 442, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 445, 25)) +>R : Symbol(R, Decl(dependentReturnType1.ts, 445, 27)) + + abstract perform(t: T): R; +>perform : Symbol(Operation.perform, Decl(dependentReturnType1.ts, 445, 32)) +>t : Symbol(t, Decl(dependentReturnType1.ts, 446, 21)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 445, 25)) +>R : Symbol(R, Decl(dependentReturnType1.ts, 445, 27)) +} + +type ConditionalReturnType | undefined> = +>ConditionalReturnType : Symbol(ConditionalReturnType, Decl(dependentReturnType1.ts, 447, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 449, 27)) +>R : Symbol(R, Decl(dependentReturnType1.ts, 449, 29)) +>EOp : Symbol(EOp, Decl(dependentReturnType1.ts, 449, 32)) +>Operation : Symbol(Operation, Decl(dependentReturnType1.ts, 442, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 449, 27)) +>R : Symbol(R, Decl(dependentReturnType1.ts, 449, 29)) + + EOp extends Operation ? R : EOp extends undefined ? T | R : never; +>EOp : Symbol(EOp, Decl(dependentReturnType1.ts, 449, 32)) +>Operation : Symbol(Operation, Decl(dependentReturnType1.ts, 442, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 449, 27)) +>R : Symbol(R, Decl(dependentReturnType1.ts, 449, 29)) +>R : Symbol(R, Decl(dependentReturnType1.ts, 449, 29)) +>EOp : Symbol(EOp, Decl(dependentReturnType1.ts, 449, 32)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 449, 27)) +>R : Symbol(R, Decl(dependentReturnType1.ts, 449, 29)) + + +class ConditionalOperation< +>ConditionalOperation : Symbol(ConditionalOperation, Decl(dependentReturnType1.ts, 450, 76)) + + T, +>T : Symbol(T, Decl(dependentReturnType1.ts, 453, 27)) + + R, +>R : Symbol(R, Decl(dependentReturnType1.ts, 454, 6)) + + EOp extends Operation | undefined, +>EOp : Symbol(EOp, Decl(dependentReturnType1.ts, 455, 6)) +>Operation : Symbol(Operation, Decl(dependentReturnType1.ts, 442, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 453, 27)) +>R : Symbol(R, Decl(dependentReturnType1.ts, 454, 6)) + +> extends Operation> { +>Operation : Symbol(Operation, Decl(dependentReturnType1.ts, 442, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 453, 27)) +>ConditionalReturnType : Symbol(ConditionalReturnType, Decl(dependentReturnType1.ts, 447, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 453, 27)) +>R : Symbol(R, Decl(dependentReturnType1.ts, 454, 6)) +>EOp : Symbol(EOp, Decl(dependentReturnType1.ts, 455, 6)) + + constructor( + private predicate: (value: T) => boolean, +>predicate : Symbol(ConditionalOperation.predicate, Decl(dependentReturnType1.ts, 458, 16)) +>value : Symbol(value, Decl(dependentReturnType1.ts, 459, 28)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 453, 27)) + + private thenOp: Operation, +>thenOp : Symbol(ConditionalOperation.thenOp, Decl(dependentReturnType1.ts, 459, 49)) +>Operation : Symbol(Operation, Decl(dependentReturnType1.ts, 442, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 453, 27)) +>R : Symbol(R, Decl(dependentReturnType1.ts, 454, 6)) + + private elseOp?: EOp, +>elseOp : Symbol(ConditionalOperation.elseOp, Decl(dependentReturnType1.ts, 460, 40)) +>EOp : Symbol(EOp, Decl(dependentReturnType1.ts, 455, 6)) + + ) { + super(); +>super : Symbol(Operation, Decl(dependentReturnType1.ts, 442, 1)) + } + + // We won't try to narrow the return type because `T` is declared on the class and we don't analyze this case. + perform(t: T): ConditionalReturnType { +>perform : Symbol(ConditionalOperation.perform, Decl(dependentReturnType1.ts, 464, 5)) +>t : Symbol(t, Decl(dependentReturnType1.ts, 467, 12)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 453, 27)) +>ConditionalReturnType : Symbol(ConditionalReturnType, Decl(dependentReturnType1.ts, 447, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 453, 27)) +>R : Symbol(R, Decl(dependentReturnType1.ts, 454, 6)) +>EOp : Symbol(EOp, Decl(dependentReturnType1.ts, 455, 6)) + + if (this.predicate(t)) { +>this.predicate : Symbol(ConditionalOperation.predicate, Decl(dependentReturnType1.ts, 458, 16)) +>this : Symbol(ConditionalOperation, Decl(dependentReturnType1.ts, 450, 76)) +>predicate : Symbol(ConditionalOperation.predicate, Decl(dependentReturnType1.ts, 458, 16)) +>t : Symbol(t, Decl(dependentReturnType1.ts, 467, 12)) + + return this.thenOp.perform(t); // Bad: this is assignable to all of the branches of the conditional, but we still can't return it +>this.thenOp.perform : Symbol(Operation.perform, Decl(dependentReturnType1.ts, 445, 32)) +>this.thenOp : Symbol(ConditionalOperation.thenOp, Decl(dependentReturnType1.ts, 459, 49)) +>this : Symbol(ConditionalOperation, Decl(dependentReturnType1.ts, 450, 76)) +>thenOp : Symbol(ConditionalOperation.thenOp, Decl(dependentReturnType1.ts, 459, 49)) +>perform : Symbol(Operation.perform, Decl(dependentReturnType1.ts, 445, 32)) +>t : Symbol(t, Decl(dependentReturnType1.ts, 467, 12)) + + } else if (typeof this.elseOp !== "undefined") { +>this.elseOp : Symbol(ConditionalOperation.elseOp, Decl(dependentReturnType1.ts, 460, 40)) +>this : Symbol(ConditionalOperation, Decl(dependentReturnType1.ts, 450, 76)) +>elseOp : Symbol(ConditionalOperation.elseOp, Decl(dependentReturnType1.ts, 460, 40)) + + return this.elseOp.perform(t); // Would be ok +>this.elseOp.perform : Symbol(Operation.perform, Decl(dependentReturnType1.ts, 445, 32)) +>this.elseOp : Symbol(ConditionalOperation.elseOp, Decl(dependentReturnType1.ts, 460, 40)) +>this : Symbol(ConditionalOperation, Decl(dependentReturnType1.ts, 450, 76)) +>elseOp : Symbol(ConditionalOperation.elseOp, Decl(dependentReturnType1.ts, 460, 40)) +>perform : Symbol(Operation.perform, Decl(dependentReturnType1.ts, 445, 32)) +>t : Symbol(t, Decl(dependentReturnType1.ts, 467, 12)) + + } else { + return t; // Would be ok +>t : Symbol(t, Decl(dependentReturnType1.ts, 467, 12)) + } + } +} + +// Like the version above, we will not attempt to narrow because there's more than one reference to `T`, +// because `T` shows up in the type of `predicate`. +function perform | undefined>( +>perform : Symbol(perform, Decl(dependentReturnType1.ts, 476, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 480, 17)) +>R : Symbol(R, Decl(dependentReturnType1.ts, 480, 19)) +>EOp : Symbol(EOp, Decl(dependentReturnType1.ts, 480, 22)) +>Operation : Symbol(Operation, Decl(dependentReturnType1.ts, 442, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 480, 17)) +>R : Symbol(R, Decl(dependentReturnType1.ts, 480, 19)) + + t: T, +>t : Symbol(t, Decl(dependentReturnType1.ts, 480, 64)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 480, 17)) + + predicate: (value: T) => boolean, +>predicate : Symbol(predicate, Decl(dependentReturnType1.ts, 481, 9)) +>value : Symbol(value, Decl(dependentReturnType1.ts, 482, 16)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 480, 17)) + + thenOp: Operation, +>thenOp : Symbol(thenOp, Decl(dependentReturnType1.ts, 482, 37)) +>Operation : Symbol(Operation, Decl(dependentReturnType1.ts, 442, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 480, 17)) +>R : Symbol(R, Decl(dependentReturnType1.ts, 480, 19)) + + elseOp?: EOp, +>elseOp : Symbol(elseOp, Decl(dependentReturnType1.ts, 483, 28)) +>EOp : Symbol(EOp, Decl(dependentReturnType1.ts, 480, 22)) + + ): ConditionalReturnType { +>ConditionalReturnType : Symbol(ConditionalReturnType, Decl(dependentReturnType1.ts, 447, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 480, 17)) +>R : Symbol(R, Decl(dependentReturnType1.ts, 480, 19)) +>EOp : Symbol(EOp, Decl(dependentReturnType1.ts, 480, 22)) + + if (predicate(t)) { +>predicate : Symbol(predicate, Decl(dependentReturnType1.ts, 481, 9)) +>t : Symbol(t, Decl(dependentReturnType1.ts, 480, 64)) + + return thenOp.perform(t); // Bad: this is assignable to all of the branches of the conditional, but we still can't return it +>thenOp.perform : Symbol(Operation.perform, Decl(dependentReturnType1.ts, 445, 32)) +>thenOp : Symbol(thenOp, Decl(dependentReturnType1.ts, 482, 37)) +>perform : Symbol(Operation.perform, Decl(dependentReturnType1.ts, 445, 32)) +>t : Symbol(t, Decl(dependentReturnType1.ts, 480, 64)) + + } else if (elseOp !== undefined) { +>elseOp : Symbol(elseOp, Decl(dependentReturnType1.ts, 483, 28)) +>undefined : Symbol(undefined) + + return elseOp.perform(t); // Would be ok +>elseOp.perform : Symbol(Operation.perform, Decl(dependentReturnType1.ts, 445, 32)) +>elseOp : Symbol(elseOp, Decl(dependentReturnType1.ts, 483, 28)) +>perform : Symbol(Operation.perform, Decl(dependentReturnType1.ts, 445, 32)) +>t : Symbol(t, Decl(dependentReturnType1.ts, 480, 64)) + + } else { + return t; // Would be ok +>t : Symbol(t, Decl(dependentReturnType1.ts, 480, 64)) + } +} + +// Return conditional expressions with parentheses +function returnStuff1(x: T ): T extends true ? 1 : T extends false ? 2 : never { +>returnStuff1 : Symbol(returnStuff1, Decl(dependentReturnType1.ts, 493, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 496, 22)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 496, 41)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 496, 22)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 496, 22)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 496, 22)) + + return (x ? (1) : 2); +>x : Symbol(x, Decl(dependentReturnType1.ts, 496, 41)) +} + +function returnStuff2(x: T ): +>returnStuff2 : Symbol(returnStuff2, Decl(dependentReturnType1.ts, 498, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 500, 22)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 500, 45)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 500, 22)) + + T extends 1 ? "one" : T extends 2 ? "two" : T extends "a" ? 0 : never { +>T : Symbol(T, Decl(dependentReturnType1.ts, 500, 22)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 500, 22)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 500, 22)) + + return (typeof x === "string" ? 0 : (x === 1 ? ("one") : "two")); +>x : Symbol(x, Decl(dependentReturnType1.ts, 500, 45)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 500, 45)) +} + +// If the conditional type's input is `never`, then it resolves to `never`: +function neverOk(x: T): T extends true ? 1 : T extends false ? 2 : never { +>neverOk : Symbol(neverOk, Decl(dependentReturnType1.ts, 503, 1)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 506, 17)) +>x : Symbol(x, Decl(dependentReturnType1.ts, 506, 36)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 506, 17)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 506, 17)) +>T : Symbol(T, Decl(dependentReturnType1.ts, 506, 17)) + + if (x === true) { +>x : Symbol(x, Decl(dependentReturnType1.ts, 506, 36)) + + return 1; + } + if (x === false) { +>x : Symbol(x, Decl(dependentReturnType1.ts, 506, 36)) + + return 2; + } + return 1; +} diff --git a/tsc/testdata/baselines/reference/compiler/dependentReturnType1.types b/tsc/testdata/baselines/reference/compiler/dependentReturnType1.types new file mode 100644 index 0000000000000..75fe2e73a2cce --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/dependentReturnType1.types @@ -0,0 +1,1344 @@ +//// [tests/cases/compiler/dependentReturnType1.ts] //// + +=== dependentReturnType1.ts === +interface A { + 1: number; +>1 : number + + 2: string; +>2 : string +} + +function f1(x: T): A[T] { +>f1 : (x: T) => A[T] +>x : T + + if (x === 1) { +>x === 1 : boolean +>x : T +>1 : 1 + + return 0; // Ok +>0 : 0 + } + else { + return 1; // Error +>1 : 1 + } +} + +interface C { + 1: number; +>1 : number + + 2: string; +>2 : string + + 3: boolean; +>3 : boolean +} + +function f2(x: T): C[T] { +>f2 : (x: T) => C[T] +>x : T + + if (x === 1) { +>x === 1 : boolean +>x : T +>1 : 1 + + return 0; // Ok +>0 : 0 + } + else { + return ""; // Error, returned expression needs to have type string & boolean (= never) +>"" : "" + } +} + +function f3(x: T): T extends 1 ? number : T extends 2 ? string : T extends 3 ? boolean : never { +>f3 : (x: T) => T extends 1 ? number : T extends 2 ? string : T extends 3 ? boolean : never +>x : T + + if (x === 1) { +>x === 1 : boolean +>x : T +>1 : 1 + + return 0; // Ok +>0 : 0 + } + else { + return ""; // Error, returned expression needs to have type string & boolean (= never) +>"" : "" + } +} + +interface One { + a: "a"; +>a : "a" + + b: "b"; +>b : "b" + + c: "c"; +>c : "c" + + d: "d"; +>d : "d" +} + +interface Two { + a: "a"; +>a : "a" + + b: "b"; +>b : "b" + + e: "e"; +>e : "e" + + f: "f"; +>f : "f" +} + +interface Three { + a: "a"; +>a : "a" + + c: "c"; +>c : "c" + + e: "e"; +>e : "e" + + g: "g"; +>g : "g" +} + +interface Four { + a: "a"; +>a : "a" + + d: "d"; +>d : "d" + + f: "f"; +>f : "f" + + g: "g"; +>g : "g" +} +// Badly written conditional return type, will not trigger narrowing +function f10(x: T): T extends 1 ? One : T extends 2 ? Two : T extends 3 ? Three : Four { +>f10 : (x: T) => T extends 1 ? One : T extends 2 ? Two : T extends 3 ? Three : Four +>x : T + + if (x === 1 || x === 2) { +>x === 1 || x === 2 : boolean +>x === 1 : boolean +>x : T +>1 : 1 +>x === 2 : boolean +>x : T +>2 : 2 + + return { a: "a", b: "b", c: "c", d: "d", e: "e", f: "f" }; // Error +>{ a: "a", b: "b", c: "c", d: "d", e: "e", f: "f" } : { a: "a"; b: "b"; c: "c"; d: "d"; e: "e"; f: "f"; } +>a : "a" +>"a" : "a" +>b : "b" +>"b" : "b" +>c : "c" +>"c" : "c" +>d : "d" +>"d" : "d" +>e : "e" +>"e" : "e" +>f : "f" +>"f" : "f" + } + return { a: "a", b: "b", c: "c", d: "d", e: "e", f: "f", g: "g" }; // Error +>{ a: "a", b: "b", c: "c", d: "d", e: "e", f: "f", g: "g" } : { a: "a"; b: "b"; c: "c"; d: "d"; e: "e"; f: "f"; g: "g"; } +>a : "a" +>"a" : "a" +>b : "b" +>"b" : "b" +>c : "c" +>"c" : "c" +>d : "d" +>"d" : "d" +>e : "e" +>"e" : "e" +>f : "f" +>"f" : "f" +>g : "g" +>"g" : "g" +} +// Well written conditional +function f101(x: T): T extends 1 ? One : T extends 2 ? Two : T extends 3 ? Three : T extends 4 ? Four : never { +>f101 : (x: T) => T extends 1 ? One : T extends 2 ? Two : T extends 3 ? Three : T extends 4 ? Four : never +>x : T + + if (x === 1 || x === 2) { +>x === 1 || x === 2 : boolean +>x === 1 : boolean +>x : T +>1 : 1 +>x === 2 : boolean +>x : T +>2 : 2 + + return { a: "a", b: "b", c: "c", d: "d", e: "e", f: "f" }; // Ok +>{ a: "a", b: "b", c: "c", d: "d", e: "e", f: "f" } : { a: "a"; b: "b"; c: "c"; d: "d"; e: "e"; f: "f"; } +>a : "a" +>"a" : "a" +>b : "b" +>"b" : "b" +>c : "c" +>"c" : "c" +>d : "d" +>"d" : "d" +>e : "e" +>"e" : "e" +>f : "f" +>"f" : "f" + } + // Excess property becomes a problem with the change, + // because we now check assignability to a narrower type... + return { a: "a", b: "b", c: "c", d: "d", e: "e", f: "f", g: "g" }; // EPC Error +>{ a: "a", b: "b", c: "c", d: "d", e: "e", f: "f", g: "g" } : { a: "a"; b: string; c: "c"; d: "d"; e: "e"; f: "f"; g: "g"; } +>a : "a" +>"a" : "a" +>b : string +>"b" : "b" +>c : "c" +>"c" : "c" +>d : "d" +>"d" : "d" +>e : "e" +>"e" : "e" +>f : "f" +>"f" : "f" +>g : "g" +>"g" : "g" +} + +// This will not work for several reasons: +// - first because the constraint of type parameter `Arg` is generic, +// so attempting to narrow the type of `arg` in the `if` would result in type `Arg & LeftIn`, +// which when substituted in the conditional return type, would not further resolve that conditional type +// - second because the `else` branch would never work because we don't narrow the type of `arg` to `Arg & RightIn` +function conditionalProducingIf( +>conditionalProducingIf : (arg: Arg, cond: (arg: LeftIn | RightIn) => arg is LeftIn, produceLeftOut: (arg: LeftIn) => LeftOut, produceRightOut: (arg: RightIn) => RightOut) => Arg extends LeftIn ? LeftOut : Arg extends RightIn ? RightOut : never + + arg: Arg, +>arg : Arg + + cond: (arg: LeftIn | RightIn) => arg is LeftIn, +>cond : (arg: LeftIn | RightIn) => arg is LeftIn +>arg : LeftIn | RightIn + + produceLeftOut: (arg: LeftIn) => LeftOut, +>produceLeftOut : (arg: LeftIn) => LeftOut +>arg : LeftIn + + produceRightOut: (arg: RightIn) => RightOut): +>produceRightOut : (arg: RightIn) => RightOut +>arg : RightIn + + Arg extends LeftIn ? LeftOut : Arg extends RightIn ? RightOut : never +{ + if (cond(arg)) { +>cond(arg) : boolean +>cond : (arg: LeftIn | RightIn) => arg is LeftIn +>arg : Arg + + return produceLeftOut(arg); +>produceLeftOut(arg) : LeftOut +>produceLeftOut : (arg: LeftIn) => LeftOut +>arg : Arg & LeftIn + + } else { + return produceRightOut(arg as RightIn); +>produceRightOut(arg as RightIn) : RightOut +>produceRightOut : (arg: RightIn) => RightOut +>arg as RightIn : RightIn +>arg : Arg + } +} + +interface Animal { + name: string; +>name : string +} + +interface Dog extends Animal { + bark: () => string; +>bark : () => string +} + +// This would be unsafe to narrow. +declare function isDog(x: Animal): x is Dog; +>isDog : (x: Animal) => x is Dog +>x : Animal + +declare function doggy(x: Dog): number; +>doggy : (x: Dog) => number +>x : Dog + +function f12(x: T): T extends Dog ? number : string { +>f12 : (x: T) => T extends Dog ? number : string +>x : T + + if (isDog(x)) { // `x` has type `T & Dog` here +>isDog(x) : boolean +>isDog : (x: Animal) => x is Dog +>x : T + + return doggy(x); +>doggy(x) : number +>doggy : (x: Dog) => number +>x : T & Dog + } + return ""; // Error: Should not work because we can't express "not a Dog" in the type system +>"" : "" +} + +// Cannot narrow `keyof` too eagerly or something like the below breaks +function f(entry: EntryId): Entry[EntryId] { +>f : (entry: EntryId) => Entry[EntryId] +>index : string +>entry : EntryId + + const entries = {} as Entry; +>entries : Entry +>{} as Entry : Entry +>{} : {} + + return entries[entry]; +>entries[entry] : Entry[EntryId] +>entries : Entry +>entry : EntryId +} + +// Works the same as before +declare function takeA(val: 'A'): void; +>takeA : (val: 'A') => void +>val : "A" + +export function bounceAndTakeIfA(value: AB): AB { +>bounceAndTakeIfA : (value: AB) => AB +>value : AB + + if (value === 'A') { +>value === 'A' : boolean +>value : AB +>'A' : "A" + + takeA(value); +>takeA(value) : void +>takeA : (val: 'A') => void +>value : "A" + + takeAB(value); +>takeAB(value) : void +>takeAB : (val: AB) => void +>value : AB + + return value; +>value : AB + } + + return value; +>value : AB + + function takeAB(val: AB): void {} +>takeAB : (val: AB) => void +>val : AB +} + +// Works the same as before +export function bbb(value: AB): "a" { +>bbb : (value: AB) => "a" +>value : AB + + if (value === "a") { +>value === "a" : boolean +>value : AB +>"a" : "a" + + return value; +>value : "a" + } + return "a"; +>"a" : "a" +} + +class Unnamed { +>Unnamed : Unnamed + + root!: { name: string }; +>root : { name: string; } +>name : string + + // Error: No narrowing because parameter is optional but `T` doesn't allow for undefined + name(name?: T): T extends string ? this : T extends undefined ? string : never { +>name : (name?: T) => T extends string ? this : T extends undefined ? string : never +>name : T | undefined + + if (typeof name === 'undefined') { +>typeof name === 'undefined' : boolean +>typeof name : "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" +>name : T | undefined +>'undefined' : "undefined" + + return this.root.name; +>this.root.name : string +>this.root : { name: string; } +>this : this +>root : { name: string; } +>name : string + } + return this; +>this : this + } + + // Good conditional + name2(name?: T): T extends string ? this : T extends undefined ? string : never { +>name2 : (name?: T) => T extends string ? this : T extends undefined ? string : never +>name : T | undefined + + if (typeof name === 'undefined') { +>typeof name === 'undefined' : boolean +>typeof name : "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" +>name : T | undefined +>'undefined' : "undefined" + + return this.root.name; // Ok +>this.root.name : string +>this.root : { name: string; } +>this : this +>root : { name: string; } +>name : string + } + this.root.name = name; +>this.root.name = name : string +>this.root.name : string +>this.root : { name: string; } +>this : this +>root : { name: string; } +>name : string +>name : string + + return this; // Ok +>this : this + } + + // Good conditional, wrong return expressions + name3(name?: T): T extends string ? this : T extends undefined ? string : never { +>name3 : (name?: T) => T extends string ? this : T extends undefined ? string : never +>name : T | undefined + + if (typeof name === 'undefined') { +>typeof name === 'undefined' : boolean +>typeof name : "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" +>name : T | undefined +>'undefined' : "undefined" + + return this; // Error +>this : this + } + this.root.name = name; +>this.root.name = name : string +>this.root.name : string +>this.root : { name: string; } +>this : this +>root : { name: string; } +>name : string +>name : string + + return name; // Error +>name : T & {} + } +} + +// Conditional expressions +interface Aa { + 1: number; +>1 : number + + 2: string; +>2 : string + + 3: boolean; +>3 : boolean +} + +function trivialConditional(x: T): Aa[T] { +>trivialConditional : (x: T) => Aa[T] +>x : T + + if (x !== 1) { +>x !== 1 : boolean +>x : T +>1 : 1 + + return x === 2 ? "" : true; +>x === 2 ? "" : true : "" | true +>x === 2 : boolean +>x : T +>2 : 2 +>"" : "" +>true : true + } + else { + return 0; +>0 : 0 + } +} + +function conditional(x: T): +>conditional : (x: T) => T extends true ? 1 : T extends false ? 2 : never +>x : T + + T extends true ? 1 : T extends false ? 2 : never { +>true : true +>false : false + + return x ? 1 : 2; // Ok +>x ? 1 : 2 : 1 | 2 +>x : T +>1 : 1 +>2 : 2 +} + +function contextualConditional( +>contextualConditional : (x: T) => T extends "a" ? "a" : T extends "b" ? number : never + + x: T +>x : T + +): T extends "a" ? "a" : T extends "b" ? number : never { + return x === "a" ? x : parseInt(x); // Ok +>x === "a" ? x : parseInt(x) : number | "a" +>x === "a" : boolean +>x : T +>"a" : "a" +>x : "a" +>parseInt(x) : number +>parseInt : (string: string, radix?: number) => number +>x : "b" +} + +function conditionalWithError( +>conditionalWithError : (x: T) => T extends "a" ? number : T extends "b" ? string : never + + x: T +>x : T + +): T extends "a" ? number : T extends "b" ? string : never { + return x === "a" ? x : parseInt(x); // Error +>x === "a" ? x : parseInt(x) : number | "a" +>x === "a" : boolean +>x : T +>"a" : "a" +>x : "a" +>parseInt(x) : number +>parseInt : (string: string, radix?: number) => number +>x : "b" +} + +// Multiple indexed type reductions +interface BB { + "a": number; +>"a" : number + + [y: number]: string; +>y : number +} + +interface AA { + "c": BB[T]; +>"c" : BB[T] + + "d": boolean, +>"d" : boolean +} + +function reduction(x: T, y: U): AA[U] { +>reduction : (x: T, y: U) => AA[U] +>x : T +>y : U + + if (y === "c" && x === "a") { +>y === "c" && x === "a" : boolean +>y === "c" : boolean +>y : U +>"c" : "c" +>x === "a" : boolean +>x : T +>"a" : "a" + + // AA[U='c'] -> BB[T] + // BB[T='a'] -> number + return 0; // Ok +>0 : 0 + } + + return undefined as never; +>undefined as never : never +>undefined : undefined +} + +// Substitution types are not narrowed +function subsCond( +>subsCond : (x: T) => T extends 1 | 2 ? T extends 1 ? string : T extends 2 ? boolean : never : T extends 3 ? number : never + + x: T, +>x : T + +): T extends 1 | 2 + ? T extends 1 + ? string + : T extends 2 + ? boolean + : never + : T extends 3 + ? number + : never { + if (x === 1) { +>x === 1 : boolean +>x : T +>1 : 1 + + return ""; +>"" : "" + + } else if (x == 2) { +>x == 2 : boolean +>x : T +>2 : 2 + + return true; +>true : true + } + return 3; +>3 : 3 +} + + +// Unsafe: check types overlap +declare function q(x: object): x is { b: number }; +>q : (x: object) => x is { b: number; } +>x : object +>b : number + +function foo( +>foo : (x: T) => T extends { a: string; } ? number : T extends { b: number; } ? string : never +>a : string +>b : number + + x: T, +>x : T + +): T extends { a: string } ? number : T extends { b: number } ? string : never { +>a : string +>b : number + + if (q(x)) { +>q(x) : boolean +>q : (x: object) => x is { b: number; } +>x : { a: string; } | { b: number; } + + x.b; +>x.b : number +>x : { b: number; } +>b : number + + return ""; +>"" : "" + } + x.a; +>x.a : string +>x : { a: string; } +>a : string + + return 1; +>1 : 1 +} + +let y = { a: "", b: 1 } +>y : { a: string; b: number; } +>{ a: "", b: 1 } : { a: string; b: number; } +>a : string +>"" : "" +>b : number +>1 : 1 + +const r = foo<{ a: string }>(y); // type says number but actually string +>r : number +>foo<{ a: string }>(y) : number +>foo : (x: T) => T extends { a: string; } ? number : T extends { b: number; } ? string : never +>a : string +>y : { a: string; b: number; } + +type HelperCond = T extends A ? R1 : T extends B ? R2 : never; +>HelperCond : HelperCond + +// We don't narrow the return type because the conditionals are not distributive +function foo2(x: U, y: V): +>foo2 : (x: U, y: V) => HelperCond<{ x: U; y: V; }, { x: string; y: true; }, 1, { x: number; y: false; }, 2> +>x : U +>y : V + + HelperCond<{ x: U, y: V }, +>x : U +>y : V + + { x: string, y: true }, 1, +>x : string +>y : true +>true : true + + { x: number, y: false }, 2> { +>x : number +>y : false +>false : false + + if (typeof x === "string" && y === true) { +>typeof x === "string" && y === true : boolean +>typeof x === "string" : boolean +>typeof x : "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" +>x : U +>"string" : "string" +>y === true : boolean +>y : V +>true : true + + return 1; // Error +>1 : 1 + } + if (typeof x === "number" && y === false) { +>typeof x === "number" && y === false : boolean +>typeof x === "number" : boolean +>typeof x : "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" +>x : U +>"number" : "number" +>y === false : boolean +>y : V +>false : false + + return 2; // Error +>2 : 2 + } + return 0; // Error +>0 : 0 +} + +// From https://github.com/microsoft/TypeScript/issues/24929#issue-332087943 +declare function isString(s: unknown): s is string; +>isString : (s: unknown) => s is string +>s : unknown + +// capitalize a string or each element of an array of strings +function capitalize( +>capitalize : (input: T) => T extends string[] ? string[] : T extends string ? string : never + + input: T +>input : T + +): T extends string[] ? string[] : T extends string ? string : never { + if (isString(input)) { +>isString(input) : boolean +>isString : (s: unknown) => s is string +>input : string | string[] + + return input[0].toUpperCase() + input.slice(1); // Ok +>input[0].toUpperCase() + input.slice(1) : string +>input[0].toUpperCase() : string +>input[0].toUpperCase : () => string +>input[0] : string +>input : string +>0 : 0 +>toUpperCase : () => string +>input.slice(1) : string +>input.slice : (start?: number, end?: number) => string +>input : string +>slice : (start?: number, end?: number) => string +>1 : 1 + + } else { + return input.map(elt => capitalize(elt)); // Ok +>input.map(elt => capitalize(elt)) : string[] +>input.map : (callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[] +>input : string[] +>map : (callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[] +>elt => capitalize(elt) : (elt: string) => string +>elt : string +>capitalize(elt) : string +>capitalize : (input: T) => T extends string[] ? string[] : T extends string ? string : never +>elt : string + } +} + +function badCapitalize( +>badCapitalize : (input: T) => T extends string[] ? string[] : T extends string ? string : never + + input: T +>input : T + +): T extends string[] ? string[] : T extends string ? string : never { + if (isString(input)) { +>isString(input) : boolean +>isString : (s: unknown) => s is string +>input : string | string[] + + return input[0].toUpperCase() + input.slice(1); // Ok +>input[0].toUpperCase() + input.slice(1) : string +>input[0].toUpperCase() : string +>input[0].toUpperCase : () => string +>input[0] : string +>input : string +>0 : 0 +>toUpperCase : () => string +>input.slice(1) : string +>input.slice : (start?: number, end?: number) => string +>input : string +>slice : (start?: number, end?: number) => string +>1 : 1 + + } else { + return input[0].toUpperCase() + input.slice(1); // Bad, error +>input[0].toUpperCase() + input.slice(1) : string +>input[0].toUpperCase() : string +>input[0].toUpperCase : () => string +>input[0] : string +>input : string[] +>0 : 0 +>toUpperCase : () => string +>input.slice(1) : string[] +>input.slice : (start?: number, end?: number) => string[] +>input : string[] +>slice : (start?: number, end?: number) => string[] +>1 : 1 + } +} + +// No narrowing because conditional's extends type is different from type parameter constraint types +function voidRet( +>voidRet : (x: T) => T extends {} ? void : T extends undefined ? number : never +>a : string + + x: T +>x : T + +): T extends {} ? void : T extends undefined ? number : never { + if (x) { +>x : T + + return; + } + return 1; +>1 : 1 +} + +// Multiple type parameters at once +function woo( +>woo : (x: T, y: U) => T extends string ? U extends string ? 1 : U extends number ? 2 : never : T extends number ? U extends number ? 3 : U extends string ? 4 : never : never + + x: T, +>x : T + + y: U, +>y : U + +): T extends string + ? U extends string + ? 1 + : U extends number + ? 2 + : never + : T extends number + ? U extends number + ? 3 + : U extends string + ? 4 + : never + : never { + if (typeof x === "number" && typeof y === "string") { +>typeof x === "number" && typeof y === "string" : boolean +>typeof x === "number" : boolean +>typeof x : "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" +>x : T +>"number" : "number" +>typeof y === "string" : boolean +>typeof y : "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" +>y : U +>"string" : "string" + + return 1; // Good error +>1 : 1 + } + return undefined as any; +>undefined as any : any +>undefined : undefined +} + +function ttt( +>ttt : (x: T, y: U) => T extends string ? U extends string ? 1 : U extends number ? 2 : never : T extends number ? U extends number ? 3 : U extends string ? 4 : never : never + + x: T, +>x : T + + y: U, +>y : U + +): T extends string + ? U extends string + ? 1 + : U extends number + ? 2 + : never + : T extends number + ? U extends number + ? 3 + : U extends string + ? 4 + : never + : never { + if (typeof x === "number" && typeof y === "string") { +>typeof x === "number" && typeof y === "string" : boolean +>typeof x === "number" : boolean +>typeof x : "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" +>x : T +>"number" : "number" +>typeof y === "string" : boolean +>typeof y : "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" +>y : U +>"string" : "string" + + return 4; // Ok +>4 : 4 + } + + return undefined as any; +>undefined as any : any +>undefined : undefined +} + +// Shadowing of the narrowed reference +function shadowing(x: T): T extends 1 ? number : T extends 2 ? string : never { +>shadowing : (x: T) => T extends 1 ? number : T extends 2 ? string : never +>x : T + + if (true) { +>true : true + + let x: number = Math.random() ? 1 : 2; +>x : number +>Math.random() ? 1 : 2 : 1 | 2 +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number +>1 : 1 +>2 : 2 + + if (x === 1) { +>x === 1 : boolean +>x : number +>1 : 1 + + return 1; // Error +>1 : 1 + } + return ""; // Error +>"" : "" + } +} + +function noShadowing(x: T): T extends 1 ? number : T extends 2 ? string : never { +>noShadowing : (x: T) => T extends 1 ? number : T extends 2 ? string : never +>x : T + + if (true) { +>true : true + + if (x === 1) { +>x === 1 : boolean +>x : T +>1 : 1 + + return 1; // Ok +>1 : 1 + } + return ""; // Ok +>"" : "" + } +} + +// If the narrowing reference is out of scope, we simply won't narrow its type +declare let someX: boolean; +>someX : boolean + +function scope2(a: T): T extends true ? 1 : T extends false ? 2 : never { +>scope2 : (a: T) => T extends true ? 1 : T extends false ? 2 : never +>a : T +>true : true +>false : false + + if ((true)) { +>(true) : true +>true : true + + const someX = a; +>someX : T +>a : T + + if (someX) { // We narrow `someX` and the return type here +>someX : T + + return 1; +>1 : 1 + } + } + if (!someX) { // This is a different `someX`, so we don't narrow here +>!someX : boolean +>someX : boolean + + return 2; +>2 : 2 + } + + return undefined as any; +>undefined as any : any +>undefined : undefined +} + +function moreShadowing(x: T): T extends 1 ? number : T extends 2 ? string : never { +>moreShadowing : (x: T) => T extends 1 ? number : T extends 2 ? string : never +>x : T + + if (x === 2) { +>x === 2 : boolean +>x : T +>2 : 2 + + let x: number = Math.random() ? 1 : 2; +>x : number +>Math.random() ? 1 : 2 : 1 | 2 +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number +>1 : 1 +>2 : 2 + + if (x === 1) { +>x === 1 : boolean +>x : number +>1 : 1 + + return 1; // Error +>1 : 1 + } + return ""; // Ok +>"" : "" + } + return 0; // Ok +>0 : 0 +} + +// This would be unsafe to narrow due to `infer` type. +function withInfer(x: T): T extends [infer R] ? R : T extends number ? boolean : never { +>withInfer : (x: T) => T extends [infer R] ? R : T extends number ? boolean : never +>x : T + + if (typeof x === "number") { +>typeof x === "number" : boolean +>typeof x : "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" +>x : T +>"number" : "number" + + return true; +>true : true + } + return ""; +>"" : "" +} + +const withInferResult = withInfer(["a"] as const); // The type says it returns `"a"`, but the function actually returns `""`. +>withInferResult : "a" +>withInfer(["a"] as const) : "a" +>withInfer : (x: T) => T extends [infer R] ? R : T extends number ? boolean : never +>["a"] as const : ["a"] +>["a"] : ["a"] +>"a" : "a" + +// Ok +async function abool(x: T): Promise { +>abool : (x: T) => Promise +>true : true +>false : false +>x : T +>true : true +>false : false + + if (x) { +>x : T + + return 1; +>1 : 1 + } + return 2; +>2 : 2 +} + +// Ok +function* bbool(x: T): Generator { +>bbool : (x: T) => Generator +>true : true +>false : false +>x : T +>true : true +>false : false + + yield 3; +>yield 3 : unknown +>3 : 3 + + if (x) { +>x : T + + return 1; +>1 : 1 + } + return 2; +>2 : 2 +} + +// We don't do the same type of narrowing for `yield` statements +function* cbool(x: T): Generator { +>cbool : (x: T) => Generator +>true : true +>false : false +>x : T +>true : true +>false : false + + if (x) { +>x : T + + yield 1; +>yield 1 : unknown +>1 : 1 + } + yield 2; +>yield 2 : unknown +>2 : 2 + + return 0; +>0 : 0 +} + +// From #33912 +abstract class Operation { +>Operation : Operation + + abstract perform(t: T): R; +>perform : (t: T) => R +>t : T +} + +type ConditionalReturnType | undefined> = +>ConditionalReturnType : ConditionalReturnType + + EOp extends Operation ? R : EOp extends undefined ? T | R : never; + + +class ConditionalOperation< +>ConditionalOperation : ConditionalOperation + + T, + R, + EOp extends Operation | undefined, +> extends Operation> { +>Operation : Operation> + + constructor( + private predicate: (value: T) => boolean, +>predicate : (value: T) => boolean +>value : T + + private thenOp: Operation, +>thenOp : Operation + + private elseOp?: EOp, +>elseOp : EOp | undefined + + ) { + super(); +>super() : void +>super : typeof Operation + } + + // We won't try to narrow the return type because `T` is declared on the class and we don't analyze this case. + perform(t: T): ConditionalReturnType { +>perform : (t: T) => ConditionalReturnType +>t : T + + if (this.predicate(t)) { +>this.predicate(t) : boolean +>this.predicate : (value: T) => boolean +>this : this +>predicate : (value: T) => boolean +>t : T + + return this.thenOp.perform(t); // Bad: this is assignable to all of the branches of the conditional, but we still can't return it +>this.thenOp.perform(t) : R +>this.thenOp.perform : (t: T) => R +>this.thenOp : Operation +>this : this +>thenOp : Operation +>perform : (t: T) => R +>t : T + + } else if (typeof this.elseOp !== "undefined") { +>typeof this.elseOp !== "undefined" : boolean +>typeof this.elseOp : "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" +>this.elseOp : EOp | undefined +>this : this +>elseOp : EOp | undefined +>"undefined" : "undefined" + + return this.elseOp.perform(t); // Would be ok +>this.elseOp.perform(t) : R +>this.elseOp.perform : (t: T) => R +>this.elseOp : Operation +>this : this +>elseOp : Operation +>perform : (t: T) => R +>t : T + + } else { + return t; // Would be ok +>t : T + } + } +} + +// Like the version above, we will not attempt to narrow because there's more than one reference to `T`, +// because `T` shows up in the type of `predicate`. +function perform | undefined>( +>perform : | undefined>(t: T, predicate: (value: T) => boolean, thenOp: Operation, elseOp?: EOp) => ConditionalReturnType + + t: T, +>t : T + + predicate: (value: T) => boolean, +>predicate : (value: T) => boolean +>value : T + + thenOp: Operation, +>thenOp : Operation + + elseOp?: EOp, +>elseOp : EOp | undefined + + ): ConditionalReturnType { + if (predicate(t)) { +>predicate(t) : boolean +>predicate : (value: T) => boolean +>t : T + + return thenOp.perform(t); // Bad: this is assignable to all of the branches of the conditional, but we still can't return it +>thenOp.perform(t) : R +>thenOp.perform : (t: T) => R +>thenOp : Operation +>perform : (t: T) => R +>t : T + + } else if (elseOp !== undefined) { +>elseOp !== undefined : boolean +>elseOp : EOp | undefined +>undefined : undefined + + return elseOp.perform(t); // Would be ok +>elseOp.perform(t) : R +>elseOp.perform : (t: T) => R +>elseOp : Operation +>perform : (t: T) => R +>t : T + + } else { + return t; // Would be ok +>t : T + } +} + +// Return conditional expressions with parentheses +function returnStuff1(x: T ): T extends true ? 1 : T extends false ? 2 : never { +>returnStuff1 : (x: T) => T extends true ? 1 : T extends false ? 2 : never +>x : T +>true : true +>false : false + + return (x ? (1) : 2); +>(x ? (1) : 2) : 1 | 2 +>x ? (1) : 2 : 1 | 2 +>x : T +>(1) : 1 +>1 : 1 +>2 : 2 +} + +function returnStuff2(x: T ): +>returnStuff2 : (x: T) => T extends 1 ? "one" : T extends 2 ? "two" : T extends "a" ? 0 : never +>x : T + + T extends 1 ? "one" : T extends 2 ? "two" : T extends "a" ? 0 : never { + return (typeof x === "string" ? 0 : (x === 1 ? ("one") : "two")); +>(typeof x === "string" ? 0 : (x === 1 ? ("one") : "two")) : "one" | "two" | 0 +>typeof x === "string" ? 0 : (x === 1 ? ("one") : "two") : "one" | "two" | 0 +>typeof x === "string" : boolean +>typeof x : "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" +>x : T +>"string" : "string" +>0 : 0 +>(x === 1 ? ("one") : "two") : "one" | "two" +>x === 1 ? ("one") : "two" : "one" | "two" +>x === 1 : boolean +>x : T +>1 : 1 +>("one") : "one" +>"one" : "one" +>"two" : "two" +} + +// If the conditional type's input is `never`, then it resolves to `never`: +function neverOk(x: T): T extends true ? 1 : T extends false ? 2 : never { +>neverOk : (x: T) => T extends true ? 1 : T extends false ? 2 : never +>x : T +>true : true +>false : false + + if (x === true) { +>x === true : boolean +>x : T +>true : true + + return 1; +>1 : 1 + } + if (x === false) { +>x === false : boolean +>x : T +>false : false + + return 2; +>2 : 2 + } + return 1; +>1 : 1 +} diff --git a/tsc/testdata/baselines/reference/compiler/dependentReturnType10.symbols b/tsc/testdata/baselines/reference/compiler/dependentReturnType10.symbols new file mode 100644 index 0000000000000..d8a400718ae15 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/dependentReturnType10.symbols @@ -0,0 +1,61 @@ +//// [tests/cases/compiler/dependentReturnType10.ts] //// + +=== dependentReturnType10.ts === +interface Animal { +>Animal : Symbol(Animal, Decl(dependentReturnType10.ts, 0, 0)) + + name: string; +>name : Symbol(Animal.name, Decl(dependentReturnType10.ts, 0, 18)) + + species: string; +>species : Symbol(Animal.species, Decl(dependentReturnType10.ts, 1, 17)) +} + +interface Dog extends Animal { +>Dog : Symbol(Dog, Decl(dependentReturnType10.ts, 3, 1)) +>Animal : Symbol(Animal, Decl(dependentReturnType10.ts, 0, 0)) + + breed: string; +>breed : Symbol(Dog.breed, Decl(dependentReturnType10.ts, 5, 30)) +} + +type GreetRet = +>GreetRet : Symbol(GreetRet, Decl(dependentReturnType10.ts, 7, 1)) +>T : Symbol(T, Decl(dependentReturnType10.ts, 9, 14)) + + T extends string ? string : +>T : Symbol(T, Decl(dependentReturnType10.ts, 9, 14)) + + T extends { name: string } ? { greeting: string, breed: string } : +>T : Symbol(T, Decl(dependentReturnType10.ts, 9, 14)) +>name : Symbol(name, Decl(dependentReturnType10.ts, 11, 15)) +>greeting : Symbol(greeting, Decl(dependentReturnType10.ts, 11, 34)) +>breed : Symbol(breed, Decl(dependentReturnType10.ts, 11, 52)) + + never; + +function greet(animal: T): GreetRet { +>greet : Symbol(greet, Decl(dependentReturnType10.ts, 12, 10)) +>T : Symbol(T, Decl(dependentReturnType10.ts, 14, 15)) +>Dog : Symbol(Dog, Decl(dependentReturnType10.ts, 3, 1)) +>animal : Symbol(animal, Decl(dependentReturnType10.ts, 14, 39)) +>T : Symbol(T, Decl(dependentReturnType10.ts, 14, 15)) +>GreetRet : Symbol(GreetRet, Decl(dependentReturnType10.ts, 7, 1)) +>T : Symbol(T, Decl(dependentReturnType10.ts, 14, 15)) + + if (typeof animal === "string") { +>animal : Symbol(animal, Decl(dependentReturnType10.ts, 14, 39)) + + return `hello, ${animal}` +>animal : Symbol(animal, Decl(dependentReturnType10.ts, 14, 39)) + } + return { greeting: `woof, ${animal.name}`, breed: animal.breed } +>greeting : Symbol(greeting, Decl(dependentReturnType10.ts, 18, 12)) +>animal.name : Symbol(Animal.name, Decl(dependentReturnType10.ts, 0, 18)) +>animal : Symbol(animal, Decl(dependentReturnType10.ts, 14, 39)) +>name : Symbol(Animal.name, Decl(dependentReturnType10.ts, 0, 18)) +>breed : Symbol(breed, Decl(dependentReturnType10.ts, 18, 46)) +>animal.breed : Symbol(Dog.breed, Decl(dependentReturnType10.ts, 5, 30)) +>animal : Symbol(animal, Decl(dependentReturnType10.ts, 14, 39)) +>breed : Symbol(Dog.breed, Decl(dependentReturnType10.ts, 5, 30)) +} diff --git a/tsc/testdata/baselines/reference/compiler/dependentReturnType10.types b/tsc/testdata/baselines/reference/compiler/dependentReturnType10.types new file mode 100644 index 0000000000000..3965937b9bb2f --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/dependentReturnType10.types @@ -0,0 +1,53 @@ +//// [tests/cases/compiler/dependentReturnType10.ts] //// + +=== dependentReturnType10.ts === +interface Animal { + name: string; +>name : string + + species: string; +>species : string +} + +interface Dog extends Animal { + breed: string; +>breed : string +} + +type GreetRet = +>GreetRet : GreetRet + + T extends string ? string : + T extends { name: string } ? { greeting: string, breed: string } : +>name : string +>greeting : string +>breed : string + + never; + +function greet(animal: T): GreetRet { +>greet : (animal: T) => GreetRet +>animal : T + + if (typeof animal === "string") { +>typeof animal === "string" : boolean +>typeof animal : "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" +>animal : T +>"string" : "string" + + return `hello, ${animal}` +>`hello, ${animal}` : string +>animal : T & string + } + return { greeting: `woof, ${animal.name}`, breed: animal.breed } +>{ greeting: `woof, ${animal.name}`, breed: animal.breed } : { greeting: string; breed: string; } +>greeting : string +>`woof, ${animal.name}` : string +>animal.name : string +>animal : Dog +>name : string +>breed : string +>animal.breed : string +>animal : Dog +>breed : string +} diff --git a/tsc/testdata/baselines/reference/compiler/dependentReturnType11.errors.txt b/tsc/testdata/baselines/reference/compiler/dependentReturnType11.errors.txt new file mode 100644 index 0000000000000..37e3cb2bf66b5 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/dependentReturnType11.errors.txt @@ -0,0 +1,394 @@ +dependentReturnType11.ts(36,34): error TS2339: Property 'prop1' does not exist on type 'Boolean'. +dependentReturnType11.ts(37,20): error TS2322: Type '1' is not assignable to type 'Ret'. +dependentReturnType11.ts(37,24): error TS2322: Type '2' is not assignable to type 'Ret'. +dependentReturnType11.ts(72,25): error TS2322: Type '1' is not assignable to type 'Ret'. +dependentReturnType11.ts(72,29): error TS2322: Type '2' is not assignable to type 'Ret'. +dependentReturnType11.ts(93,25): error TS2322: Type '1' is not assignable to type 'Ret'. +dependentReturnType11.ts(93,29): error TS2322: Type '2' is not assignable to type 'Ret'. +dependentReturnType11.ts(110,19): error TS2322: Type '1' is not assignable to type 'Ret'. +dependentReturnType11.ts(110,23): error TS2322: Type '2' is not assignable to type 'Ret'. +dependentReturnType11.ts(114,23): error TS2322: Type '1' is not assignable to type 'Ret'. +dependentReturnType11.ts(114,27): error TS2322: Type '2' is not assignable to type 'Ret'. +dependentReturnType11.ts(118,23): error TS2322: Type '1' is not assignable to type 'Ret'. +dependentReturnType11.ts(118,27): error TS2322: Type '2' is not assignable to type 'Ret'. +dependentReturnType11.ts(136,25): error TS2322: Type '1' is not assignable to type 'Ret'. +dependentReturnType11.ts(136,29): error TS2322: Type '2' is not assignable to type 'Ret'. +dependentReturnType11.ts(144,25): error TS2322: Type '1' is not assignable to type 'Ret'. +dependentReturnType11.ts(144,29): error TS2322: Type '2' is not assignable to type 'Ret'. +dependentReturnType11.ts(164,25): error TS2322: Type '1' is not assignable to type 'Ret'. +dependentReturnType11.ts(164,29): error TS2322: Type '2' is not assignable to type 'Ret'. +dependentReturnType11.ts(205,13): error TS2322: Type '1' is not assignable to type 'Ret'. +dependentReturnType11.ts(207,9): error TS2322: Type '2' is not assignable to type 'Ret'. +dependentReturnType11.ts(221,9): error TS2322: Type '3' is not assignable to type 'RetU'. +dependentReturnType11.ts(224,9): error TS2322: Type '1' is not assignable to type 'RetU'. +dependentReturnType11.ts(226,5): error TS2322: Type '2' is not assignable to type 'RetU'. +dependentReturnType11.ts(260,9): error TS18048: 'param' is possibly 'undefined'. +dependentReturnType11.ts(261,9): error TS2322: Type '3' is not assignable to type 'RetU'. +dependentReturnType11.ts(263,9): error TS18048: 'param' is possibly 'undefined'. +dependentReturnType11.ts(264,9): error TS2322: Type '1' is not assignable to type 'RetU'. +dependentReturnType11.ts(266,5): error TS2322: Type '2' is not assignable to type 'RetU'. +dependentReturnType11.ts(270,9): error TS18048: 'param.prop1' is possibly 'undefined'. +dependentReturnType11.ts(271,9): error TS2322: Type '3' is not assignable to type 'RetU'. +dependentReturnType11.ts(273,9): error TS18048: 'param.prop1' is possibly 'undefined'. +dependentReturnType11.ts(274,9): error TS2322: Type '1' is not assignable to type 'RetU'. +dependentReturnType11.ts(276,5): error TS2322: Type '2' is not assignable to type 'RetU'. +dependentReturnType11.ts(282,25): error TS2322: Type '1' is not assignable to type 'Ret'. +dependentReturnType11.ts(282,29): error TS2322: Type '2' is not assignable to type 'Ret'. + + +==== dependentReturnType11.ts (36 errors) ==== + type Ret = + T extends true ? 1 : + T extends false ? 2 : + never; + + // Tests for constructing narrowable reference. + + function f1(param: T): Ret { + return param ? 1 : 2; + } + + function f2(param: { prop: T }): Ret { + return param.prop ? 1 : 2; + } + + function f3({ prop }: { prop: T }): Ret { + return prop ? 1 : 2; + } + + function f4({ prop1 }: { prop1: { prop2: T } }): Ret { + return prop1.prop2 ? 1 : 2; + } + + function f5(param: { prop1: { prop2: T } }): Ret { + return param.prop1.prop2 ? 1 : 2; + } + + function f6({ prop1: { prop2: a } }: { prop1: { prop2: T } }): Ret { + return a ? 1 : 2; + } + + function f7({ prop1: { prop2 } }: { prop1: { prop2: T } }): Ret { + return prop2 ? 1 : 2; + } + + function f8({ prop1 }: T): Ret { // Bad. + ~~~~~ +!!! error TS2339: Property 'prop1' does not exist on type 'Boolean'. + return prop1 ? 1 : 2; + ~ +!!! error TS2322: Type '1' is not assignable to type 'Ret'. + ~ +!!! error TS2322: Type '2' is not assignable to type 'Ret'. + } + + function f9(param: { "some prop": T }): Ret { + return param["some prop"] ? 1 : 2; + } + + // Tests for detection of valid narrowable type parameter references. + + function g1(param: T): Ret { + return param ? 1 : 2; + } + + function g2(param: { prop: T }): Ret { + return param.prop ? 1 : 2; + } + + class Dog { + bark(): void {} + } + + class Cat { + meow(): void {} + + } + + type Type1 = { prop: T, prop2: Cat | Dog } + + function g3(param: Type1): Ret { + return param.prop ? 1 : 2; + } + + type TypeUnused = string; + + function g4(param: Type1, other: TypeUnused): Ret { // Bad. + return param.prop ? 1 : 2; + ~ +!!! error TS2322: Type '1' is not assignable to type 'Ret'. + ~ +!!! error TS2322: Type '2' is not assignable to type 'Ret'. + } + + interface Type2 { + prop: T, + [s: string]: boolean | undefined, + } + + function g5(param: Type2): Ret { + return param.prop ? 1 : 2; + } + + interface Type3 { + prop: T, + } + + interface Type3 { + prop2: Cat | Dog, + } + + function g6(param: Type3): Ret { // Unsupported for now: interface merging. + return param.prop ? 1 : 2; + ~ +!!! error TS2322: Type '1' is not assignable to type 'Ret'. + ~ +!!! error TS2322: Type '2' is not assignable to type 'Ret'. + } + + type Type4 = { + prop: T, + prop2: S[], + } + + function g7(param: Type4): Ret { + return param.prop ? 1 : 2; + } + + function g8(param: Type1 & { prop2: Cat | Dog }): Ret { + return param.prop ? 1 : 2; + } + + function g9([ prop ]: [T]): Ret { // Unsupported. + return prop ? 1 : 2; + ~ +!!! error TS2322: Type '1' is not assignable to type 'Ret'. + ~ +!!! error TS2322: Type '2' is not assignable to type 'Ret'. + } + + function g10(param: [T]): Ret { // Unsupported. + return param[0] ? 1 : 2; + ~ +!!! error TS2322: Type '1' is not assignable to type 'Ret'. + ~ +!!! error TS2322: Type '2' is not assignable to type 'Ret'. + } + + function g11(param: T[]): Ret { // Bad. + return param[0] ? 1 : 2; + ~ +!!! error TS2322: Type '1' is not assignable to type 'Ret'. + ~ +!!! error TS2322: Type '2' is not assignable to type 'Ret'. + } + + type Type5 = { + prop: S, + prop2: string[], + } + + function g12(param: Type5): Ret { + return param.prop ? 1 : 2; + } + + type Type6 = { + prop: S, + prop2: S[], + } + + function g13(param: Type6): Ret { // Bad. + return param.prop ? 1 : 2; + ~ +!!! error TS2322: Type '1' is not assignable to type 'Ret'. + ~ +!!! error TS2322: Type '2' is not assignable to type 'Ret'. + } + + type Type7 = { + [Key in "prop"]: T + } + + function g14(param: Type7): Ret { // Unsupported. + return param.prop ? 1 : 2; + ~ +!!! error TS2322: Type '1' is not assignable to type 'Ret'. + ~ +!!! error TS2322: Type '2' is not assignable to type 'Ret'. + } + + class Class1 { + prop!: T; + } + + function g15(param: Class1): Ret { + return param.prop ? 1 : 2; + } + + type Type8 = { + prop: T, + } + + type Type9 = { + prop2: T, + } + + function g16(param: Type8["prop2"]>): Ret { // Unsupported. + return param.prop ? 1 : 2; + ~ +!!! error TS2322: Type '1' is not assignable to type 'Ret'. + ~ +!!! error TS2322: Type '2' is not assignable to type 'Ret'. + } + + // Tests for shadowing and resolving the constructed narrowable reference. + + function h1(param: T): Ret { + if (param) { + const param = false; + return 1; + } + return 2; + } + + function h2({ prop }: { prop: T}): Ret { + if (prop) { + const prop = false; + return 1; + } + return 2; + } + + function h3(param: { prop: T}): Ret { + if (param.prop) { + const param = { prop: false }; + return 1; + } + return 2; + } + + function h4(param: { "some prop": T }): Ret { + if (param["some prop"]) { + const param = { "some prop": false }; + return 1; + } + return 2; + } + + function h5(param: T): Ret { + { + const param = true; + if (param) { + return 1; // Bad. + ~~~~~~ +!!! error TS2322: Type '1' is not assignable to type 'Ret'. + } + return 2; + ~~~~~~ +!!! error TS2322: Type '2' is not assignable to type 'Ret'. + } + } + + // Tests for optionality of parameters and properties. + + type RetU = + T extends true ? 1 : + T extends false ? 2 : + T extends undefined ? 3 : + never; + + function fn1(param?: T): RetU { // Bad. + if (param == undefined) { + return 3; + ~~~~~~ +!!! error TS2322: Type '3' is not assignable to type 'RetU'. + } + if (param) { + return 1; + ~~~~~~ +!!! error TS2322: Type '1' is not assignable to type 'RetU'. + } + return 2; + ~~~~~~ +!!! error TS2322: Type '2' is not assignable to type 'RetU'. + } + + function fn2(param?: T): RetU { + if (param == undefined) { + return 3; + } + if (param) { + return 1; + } + return 2; + } + + function fn3(param: { prop?: T }): RetU { + if (param.prop == undefined) { + return 3; + } + if (param.prop) { + return 1; + } + return 2; + } + + function fn4({ prop }: { prop?: T }): RetU { + if (prop == undefined) { + return 3; + } + if (prop) { + return 1; + } + return 2; + } + + function fn5(param?: { prop: T }): RetU { // Bad. + if (param.prop == undefined) { + ~~~~~ +!!! error TS18048: 'param' is possibly 'undefined'. + return 3; + ~~~~~~ +!!! error TS2322: Type '3' is not assignable to type 'RetU'. + } + if (param.prop) { + ~~~~~ +!!! error TS18048: 'param' is possibly 'undefined'. + return 1; + ~~~~~~ +!!! error TS2322: Type '1' is not assignable to type 'RetU'. + } + return 2; + ~~~~~~ +!!! error TS2322: Type '2' is not assignable to type 'RetU'. + } + + function fn6(param: { prop1?: { prop?: T } }): RetU { // Bad. + if (param.prop1.prop == undefined) { + ~~~~~~~~~~~ +!!! error TS18048: 'param.prop1' is possibly 'undefined'. + return 3; + ~~~~~~ +!!! error TS2322: Type '3' is not assignable to type 'RetU'. + } + if (param.prop1.prop) { + ~~~~~~~~~~~ +!!! error TS18048: 'param.prop1' is possibly 'undefined'. + return 1; + ~~~~~~ +!!! error TS2322: Type '1' is not assignable to type 'RetU'. + } + return 2; + ~~~~~~ +!!! error TS2322: Type '2' is not assignable to type 'RetU'. + } + + type Not = T extends true ? false : true; + + function transformedTypeArgument(param: Type5>): Ret { // Bad. + return param.prop ? 1 : 2; + ~ +!!! error TS2322: Type '1' is not assignable to type 'Ret'. + ~ +!!! error TS2322: Type '2' is not assignable to type 'Ret'. + } \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/dependentReturnType11.symbols b/tsc/testdata/baselines/reference/compiler/dependentReturnType11.symbols new file mode 100644 index 0000000000000..289201d80ee49 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/dependentReturnType11.symbols @@ -0,0 +1,818 @@ +//// [tests/cases/compiler/dependentReturnType11.ts] //// + +=== dependentReturnType11.ts === +type Ret = +>Ret : Symbol(Ret, Decl(dependentReturnType11.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 0, 9)) + + T extends true ? 1 : +>T : Symbol(T, Decl(dependentReturnType11.ts, 0, 9)) + + T extends false ? 2 : +>T : Symbol(T, Decl(dependentReturnType11.ts, 0, 9)) + + never; + +// Tests for constructing narrowable reference. + +function f1(param: T): Ret { +>f1 : Symbol(f1, Decl(dependentReturnType11.ts, 3, 10)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 7, 12)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 7, 31)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 7, 12)) +>Ret : Symbol(Ret, Decl(dependentReturnType11.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 7, 12)) + + return param ? 1 : 2; +>param : Symbol(param, Decl(dependentReturnType11.ts, 7, 31)) +} + +function f2(param: { prop: T }): Ret { +>f2 : Symbol(f2, Decl(dependentReturnType11.ts, 9, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 11, 12)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 11, 31)) +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 11, 39)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 11, 12)) +>Ret : Symbol(Ret, Decl(dependentReturnType11.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 11, 12)) + + return param.prop ? 1 : 2; +>param.prop : Symbol(prop, Decl(dependentReturnType11.ts, 11, 39)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 11, 31)) +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 11, 39)) +} + +function f3({ prop }: { prop: T }): Ret { +>f3 : Symbol(f3, Decl(dependentReturnType11.ts, 13, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 15, 12)) +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 15, 32)) +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 15, 42)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 15, 12)) +>Ret : Symbol(Ret, Decl(dependentReturnType11.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 15, 12)) + + return prop ? 1 : 2; +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 15, 32)) +} + +function f4({ prop1 }: { prop1: { prop2: T } }): Ret { +>f4 : Symbol(f4, Decl(dependentReturnType11.ts, 17, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 19, 12)) +>prop1 : Symbol(prop1, Decl(dependentReturnType11.ts, 19, 32)) +>prop1 : Symbol(prop1, Decl(dependentReturnType11.ts, 19, 43)) +>prop2 : Symbol(prop2, Decl(dependentReturnType11.ts, 19, 52)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 19, 12)) +>Ret : Symbol(Ret, Decl(dependentReturnType11.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 19, 12)) + + return prop1.prop2 ? 1 : 2; +>prop1.prop2 : Symbol(prop2, Decl(dependentReturnType11.ts, 19, 52)) +>prop1 : Symbol(prop1, Decl(dependentReturnType11.ts, 19, 32)) +>prop2 : Symbol(prop2, Decl(dependentReturnType11.ts, 19, 52)) +} + +function f5(param: { prop1: { prop2: T } }): Ret { +>f5 : Symbol(f5, Decl(dependentReturnType11.ts, 21, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 23, 12)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 23, 31)) +>prop1 : Symbol(prop1, Decl(dependentReturnType11.ts, 23, 39)) +>prop2 : Symbol(prop2, Decl(dependentReturnType11.ts, 23, 48)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 23, 12)) +>Ret : Symbol(Ret, Decl(dependentReturnType11.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 23, 12)) + + return param.prop1.prop2 ? 1 : 2; +>param.prop1.prop2 : Symbol(prop2, Decl(dependentReturnType11.ts, 23, 48)) +>param.prop1 : Symbol(prop1, Decl(dependentReturnType11.ts, 23, 39)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 23, 31)) +>prop1 : Symbol(prop1, Decl(dependentReturnType11.ts, 23, 39)) +>prop2 : Symbol(prop2, Decl(dependentReturnType11.ts, 23, 48)) +} + +function f6({ prop1: { prop2: a } }: { prop1: { prop2: T } }): Ret { +>f6 : Symbol(f6, Decl(dependentReturnType11.ts, 25, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 27, 12)) +>prop1 : Symbol(prop1, Decl(dependentReturnType11.ts, 27, 57)) +>prop2 : Symbol(prop2, Decl(dependentReturnType11.ts, 27, 66)) +>a : Symbol(a, Decl(dependentReturnType11.ts, 27, 41)) +>prop1 : Symbol(prop1, Decl(dependentReturnType11.ts, 27, 57)) +>prop2 : Symbol(prop2, Decl(dependentReturnType11.ts, 27, 66)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 27, 12)) +>Ret : Symbol(Ret, Decl(dependentReturnType11.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 27, 12)) + + return a ? 1 : 2; +>a : Symbol(a, Decl(dependentReturnType11.ts, 27, 41)) +} + +function f7({ prop1: { prop2 } }: { prop1: { prop2: T } }): Ret { +>f7 : Symbol(f7, Decl(dependentReturnType11.ts, 29, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 31, 12)) +>prop1 : Symbol(prop1, Decl(dependentReturnType11.ts, 31, 54)) +>prop2 : Symbol(prop2, Decl(dependentReturnType11.ts, 31, 41)) +>prop1 : Symbol(prop1, Decl(dependentReturnType11.ts, 31, 54)) +>prop2 : Symbol(prop2, Decl(dependentReturnType11.ts, 31, 63)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 31, 12)) +>Ret : Symbol(Ret, Decl(dependentReturnType11.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 31, 12)) + + return prop2 ? 1 : 2; +>prop2 : Symbol(prop2, Decl(dependentReturnType11.ts, 31, 41)) +} + +function f8({ prop1 }: T): Ret { // Bad. +>f8 : Symbol(f8, Decl(dependentReturnType11.ts, 33, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 35, 12)) +>prop1 : Symbol(prop1, Decl(dependentReturnType11.ts, 35, 32)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 35, 12)) +>Ret : Symbol(Ret, Decl(dependentReturnType11.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 35, 12)) + + return prop1 ? 1 : 2; +>prop1 : Symbol(prop1, Decl(dependentReturnType11.ts, 35, 32)) +} + +function f9(param: { "some prop": T }): Ret { +>f9 : Symbol(f9, Decl(dependentReturnType11.ts, 37, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 39, 12)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 39, 31)) +>"some prop" : Symbol("some prop", Decl(dependentReturnType11.ts, 39, 39)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 39, 12)) +>Ret : Symbol(Ret, Decl(dependentReturnType11.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 39, 12)) + + return param["some prop"] ? 1 : 2; +>param : Symbol(param, Decl(dependentReturnType11.ts, 39, 31)) +>"some prop" : Symbol("some prop", Decl(dependentReturnType11.ts, 39, 39)) +} + +// Tests for detection of valid narrowable type parameter references. + +function g1(param: T): Ret { +>g1 : Symbol(g1, Decl(dependentReturnType11.ts, 41, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 45, 12)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 45, 31)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 45, 12)) +>Ret : Symbol(Ret, Decl(dependentReturnType11.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 45, 12)) + + return param ? 1 : 2; +>param : Symbol(param, Decl(dependentReturnType11.ts, 45, 31)) +} + +function g2(param: { prop: T }): Ret { +>g2 : Symbol(g2, Decl(dependentReturnType11.ts, 47, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 49, 12)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 49, 31)) +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 49, 39)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 49, 12)) +>Ret : Symbol(Ret, Decl(dependentReturnType11.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 49, 12)) + + return param.prop ? 1 : 2; +>param.prop : Symbol(prop, Decl(dependentReturnType11.ts, 49, 39)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 49, 31)) +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 49, 39)) +} + +class Dog { +>Dog : Symbol(Dog, Decl(dependentReturnType11.ts, 51, 1)) + + bark(): void {} +>bark : Symbol(Dog.bark, Decl(dependentReturnType11.ts, 53, 11)) +} + +class Cat { +>Cat : Symbol(Cat, Decl(dependentReturnType11.ts, 55, 1)) + + meow(): void {} +>meow : Symbol(Cat.meow, Decl(dependentReturnType11.ts, 57, 11)) + +} + +type Type1 = { prop: T, prop2: Cat | Dog } +>Type1 : Symbol(Type1, Decl(dependentReturnType11.ts, 60, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 62, 11)) +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 62, 17)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 62, 11)) +>prop2 : Symbol(prop2, Decl(dependentReturnType11.ts, 62, 26)) +>Cat : Symbol(Cat, Decl(dependentReturnType11.ts, 55, 1)) +>Dog : Symbol(Dog, Decl(dependentReturnType11.ts, 51, 1)) + +function g3(param: Type1): Ret { +>g3 : Symbol(g3, Decl(dependentReturnType11.ts, 62, 45)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 64, 12)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 64, 31)) +>Type1 : Symbol(Type1, Decl(dependentReturnType11.ts, 60, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 64, 12)) +>Ret : Symbol(Ret, Decl(dependentReturnType11.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 64, 12)) + + return param.prop ? 1 : 2; +>param.prop : Symbol(prop, Decl(dependentReturnType11.ts, 62, 17)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 64, 31)) +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 62, 17)) +} + +type TypeUnused = string; +>TypeUnused : Symbol(TypeUnused, Decl(dependentReturnType11.ts, 66, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 68, 16)) + +function g4(param: Type1, other: TypeUnused): Ret { // Bad. +>g4 : Symbol(g4, Decl(dependentReturnType11.ts, 68, 28)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 70, 12)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 70, 31)) +>Type1 : Symbol(Type1, Decl(dependentReturnType11.ts, 60, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 70, 12)) +>other : Symbol(other, Decl(dependentReturnType11.ts, 70, 47)) +>TypeUnused : Symbol(TypeUnused, Decl(dependentReturnType11.ts, 66, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 70, 12)) +>Ret : Symbol(Ret, Decl(dependentReturnType11.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 70, 12)) + + return param.prop ? 1 : 2; +>param.prop : Symbol(prop, Decl(dependentReturnType11.ts, 62, 17)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 70, 31)) +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 62, 17)) +} + +interface Type2 { +>Type2 : Symbol(Type2, Decl(dependentReturnType11.ts, 72, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 74, 16)) + + prop: T, +>prop : Symbol(Type2.prop, Decl(dependentReturnType11.ts, 74, 36)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 74, 16)) + + [s: string]: boolean | undefined, +>s : Symbol(s, Decl(dependentReturnType11.ts, 76, 5)) +} + +function g5(param: Type2): Ret { +>g5 : Symbol(g5, Decl(dependentReturnType11.ts, 77, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 79, 12)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 79, 31)) +>Type2 : Symbol(Type2, Decl(dependentReturnType11.ts, 72, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 79, 12)) +>Ret : Symbol(Ret, Decl(dependentReturnType11.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 79, 12)) + + return param.prop ? 1 : 2; +>param.prop : Symbol(Type2.prop, Decl(dependentReturnType11.ts, 74, 36)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 79, 31)) +>prop : Symbol(Type2.prop, Decl(dependentReturnType11.ts, 74, 36)) +} + +interface Type3 { +>Type3 : Symbol(Type3, Decl(dependentReturnType11.ts, 81, 1), Decl(dependentReturnType11.ts, 85, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 83, 16), Decl(dependentReturnType11.ts, 87, 16)) + + prop: T, +>prop : Symbol(Type3.prop, Decl(dependentReturnType11.ts, 83, 36)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 83, 16), Decl(dependentReturnType11.ts, 87, 16)) +} + +interface Type3 { +>Type3 : Symbol(Type3, Decl(dependentReturnType11.ts, 81, 1), Decl(dependentReturnType11.ts, 85, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 83, 16), Decl(dependentReturnType11.ts, 87, 16)) + + prop2: Cat | Dog, +>prop2 : Symbol(Type3.prop2, Decl(dependentReturnType11.ts, 87, 36)) +>Cat : Symbol(Cat, Decl(dependentReturnType11.ts, 55, 1)) +>Dog : Symbol(Dog, Decl(dependentReturnType11.ts, 51, 1)) +} + +function g6(param: Type3): Ret { // Unsupported for now: interface merging. +>g6 : Symbol(g6, Decl(dependentReturnType11.ts, 89, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 91, 12)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 91, 31)) +>Type3 : Symbol(Type3, Decl(dependentReturnType11.ts, 81, 1), Decl(dependentReturnType11.ts, 85, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 91, 12)) +>Ret : Symbol(Ret, Decl(dependentReturnType11.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 91, 12)) + + return param.prop ? 1 : 2; +>param.prop : Symbol(Type3.prop, Decl(dependentReturnType11.ts, 83, 36)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 91, 31)) +>prop : Symbol(Type3.prop, Decl(dependentReturnType11.ts, 83, 36)) +} + +type Type4 = { +>Type4 : Symbol(Type4, Decl(dependentReturnType11.ts, 93, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 95, 11)) +>S : Symbol(S, Decl(dependentReturnType11.ts, 95, 13)) + + prop: T, +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 95, 20)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 95, 11)) + + prop2: S[], +>prop2 : Symbol(prop2, Decl(dependentReturnType11.ts, 96, 12)) +>S : Symbol(S, Decl(dependentReturnType11.ts, 95, 13)) +} + +function g7(param: Type4): Ret { +>g7 : Symbol(g7, Decl(dependentReturnType11.ts, 98, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 100, 12)) +>S : Symbol(S, Decl(dependentReturnType11.ts, 100, 30)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 100, 34)) +>Type4 : Symbol(Type4, Decl(dependentReturnType11.ts, 93, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 100, 12)) +>S : Symbol(S, Decl(dependentReturnType11.ts, 100, 30)) +>Ret : Symbol(Ret, Decl(dependentReturnType11.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 100, 12)) + + return param.prop ? 1 : 2; +>param.prop : Symbol(prop, Decl(dependentReturnType11.ts, 95, 20)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 100, 34)) +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 95, 20)) +} + +function g8(param: Type1 & { prop2: Cat | Dog }): Ret { +>g8 : Symbol(g8, Decl(dependentReturnType11.ts, 102, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 104, 12)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 104, 31)) +>Type1 : Symbol(Type1, Decl(dependentReturnType11.ts, 60, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 104, 12)) +>prop2 : Symbol(prop2, Decl(dependentReturnType11.ts, 104, 50)) +>Cat : Symbol(Cat, Decl(dependentReturnType11.ts, 55, 1)) +>Dog : Symbol(Dog, Decl(dependentReturnType11.ts, 51, 1)) +>Ret : Symbol(Ret, Decl(dependentReturnType11.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 104, 12)) + + return param.prop ? 1 : 2; +>param.prop : Symbol(prop, Decl(dependentReturnType11.ts, 62, 17)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 104, 31)) +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 62, 17)) +} + +function g9([ prop ]: [T]): Ret { // Unsupported. +>g9 : Symbol(g9, Decl(dependentReturnType11.ts, 106, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 108, 12)) +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 108, 32)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 108, 12)) +>Ret : Symbol(Ret, Decl(dependentReturnType11.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 108, 12)) + + return prop ? 1 : 2; +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 108, 32)) +} + +function g10(param: [T]): Ret { // Unsupported. +>g10 : Symbol(g10, Decl(dependentReturnType11.ts, 110, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 112, 13)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 112, 32)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 112, 13)) +>Ret : Symbol(Ret, Decl(dependentReturnType11.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 112, 13)) + + return param[0] ? 1 : 2; +>param : Symbol(param, Decl(dependentReturnType11.ts, 112, 32)) +>0 : Symbol(0) +} + +function g11(param: T[]): Ret { // Bad. +>g11 : Symbol(g11, Decl(dependentReturnType11.ts, 114, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 116, 13)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 116, 32)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 116, 13)) +>Ret : Symbol(Ret, Decl(dependentReturnType11.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 116, 13)) + + return param[0] ? 1 : 2; +>param : Symbol(param, Decl(dependentReturnType11.ts, 116, 32)) +} + +type Type5 = { +>Type5 : Symbol(Type5, Decl(dependentReturnType11.ts, 118, 1)) +>S : Symbol(S, Decl(dependentReturnType11.ts, 120, 11)) + + prop: S, +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 120, 17)) +>S : Symbol(S, Decl(dependentReturnType11.ts, 120, 11)) + + prop2: string[], +>prop2 : Symbol(prop2, Decl(dependentReturnType11.ts, 121, 12)) +} + +function g12(param: Type5): Ret { +>g12 : Symbol(g12, Decl(dependentReturnType11.ts, 123, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 125, 13)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 125, 32)) +>Type5 : Symbol(Type5, Decl(dependentReturnType11.ts, 118, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 125, 13)) +>Ret : Symbol(Ret, Decl(dependentReturnType11.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 125, 13)) + + return param.prop ? 1 : 2; +>param.prop : Symbol(prop, Decl(dependentReturnType11.ts, 120, 17)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 125, 32)) +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 120, 17)) +} + +type Type6 = { +>Type6 : Symbol(Type6, Decl(dependentReturnType11.ts, 127, 1)) +>S : Symbol(S, Decl(dependentReturnType11.ts, 129, 11)) + + prop: S, +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 129, 17)) +>S : Symbol(S, Decl(dependentReturnType11.ts, 129, 11)) + + prop2: S[], +>prop2 : Symbol(prop2, Decl(dependentReturnType11.ts, 130, 12)) +>S : Symbol(S, Decl(dependentReturnType11.ts, 129, 11)) +} + +function g13(param: Type6): Ret { // Bad. +>g13 : Symbol(g13, Decl(dependentReturnType11.ts, 132, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 134, 13)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 134, 32)) +>Type6 : Symbol(Type6, Decl(dependentReturnType11.ts, 127, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 134, 13)) +>Ret : Symbol(Ret, Decl(dependentReturnType11.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 134, 13)) + + return param.prop ? 1 : 2; +>param.prop : Symbol(prop, Decl(dependentReturnType11.ts, 129, 17)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 134, 32)) +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 129, 17)) +} + +type Type7 = { +>Type7 : Symbol(Type7, Decl(dependentReturnType11.ts, 136, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 138, 11)) + + [Key in "prop"]: T +>Key : Symbol(Key, Decl(dependentReturnType11.ts, 139, 5)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 138, 11)) +} + +function g14(param: Type7): Ret { // Unsupported. +>g14 : Symbol(g14, Decl(dependentReturnType11.ts, 140, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 142, 13)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 142, 32)) +>Type7 : Symbol(Type7, Decl(dependentReturnType11.ts, 136, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 142, 13)) +>Ret : Symbol(Ret, Decl(dependentReturnType11.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 142, 13)) + + return param.prop ? 1 : 2; +>param.prop : Symbol(prop) +>param : Symbol(param, Decl(dependentReturnType11.ts, 142, 32)) +>prop : Symbol(prop) +} + +class Class1 { +>Class1 : Symbol(Class1, Decl(dependentReturnType11.ts, 144, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 146, 13)) + + prop!: T; +>prop : Symbol(Class1.prop, Decl(dependentReturnType11.ts, 146, 17)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 146, 13)) +} + +function g15(param: Class1): Ret { +>g15 : Symbol(g15, Decl(dependentReturnType11.ts, 148, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 150, 13)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 150, 32)) +>Class1 : Symbol(Class1, Decl(dependentReturnType11.ts, 144, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 150, 13)) +>Ret : Symbol(Ret, Decl(dependentReturnType11.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 150, 13)) + + return param.prop ? 1 : 2; +>param.prop : Symbol(Class1.prop, Decl(dependentReturnType11.ts, 146, 17)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 150, 32)) +>prop : Symbol(Class1.prop, Decl(dependentReturnType11.ts, 146, 17)) +} + +type Type8 = { +>Type8 : Symbol(Type8, Decl(dependentReturnType11.ts, 152, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 154, 11)) + + prop: T, +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 154, 17)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 154, 11)) +} + +type Type9 = { +>Type9 : Symbol(Type9, Decl(dependentReturnType11.ts, 156, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 158, 11)) + + prop2: T, +>prop2 : Symbol(prop2, Decl(dependentReturnType11.ts, 158, 17)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 158, 11)) +} + +function g16(param: Type8["prop2"]>): Ret { // Unsupported. +>g16 : Symbol(g16, Decl(dependentReturnType11.ts, 160, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 162, 13)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 162, 32)) +>Type8 : Symbol(Type8, Decl(dependentReturnType11.ts, 152, 1)) +>Type9 : Symbol(Type9, Decl(dependentReturnType11.ts, 156, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 162, 13)) +>Ret : Symbol(Ret, Decl(dependentReturnType11.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 162, 13)) + + return param.prop ? 1 : 2; +>param.prop : Symbol(prop, Decl(dependentReturnType11.ts, 154, 17)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 162, 32)) +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 154, 17)) +} + +// Tests for shadowing and resolving the constructed narrowable reference. + +function h1(param: T): Ret { +>h1 : Symbol(h1, Decl(dependentReturnType11.ts, 164, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 168, 12)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 168, 31)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 168, 12)) +>Ret : Symbol(Ret, Decl(dependentReturnType11.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 168, 12)) + + if (param) { +>param : Symbol(param, Decl(dependentReturnType11.ts, 168, 31)) + + const param = false; +>param : Symbol(param, Decl(dependentReturnType11.ts, 170, 13)) + + return 1; + } + return 2; +} + +function h2({ prop }: { prop: T}): Ret { +>h2 : Symbol(h2, Decl(dependentReturnType11.ts, 174, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 176, 12)) +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 176, 32)) +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 176, 42)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 176, 12)) +>Ret : Symbol(Ret, Decl(dependentReturnType11.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 176, 12)) + + if (prop) { +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 176, 32)) + + const prop = false; +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 178, 13)) + + return 1; + } + return 2; +} + +function h3(param: { prop: T}): Ret { +>h3 : Symbol(h3, Decl(dependentReturnType11.ts, 182, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 184, 12)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 184, 31)) +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 184, 39)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 184, 12)) +>Ret : Symbol(Ret, Decl(dependentReturnType11.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 184, 12)) + + if (param.prop) { +>param.prop : Symbol(prop, Decl(dependentReturnType11.ts, 184, 39)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 184, 31)) +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 184, 39)) + + const param = { prop: false }; +>param : Symbol(param, Decl(dependentReturnType11.ts, 186, 13)) +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 186, 23)) + + return 1; + } + return 2; +} + +function h4(param: { "some prop": T }): Ret { +>h4 : Symbol(h4, Decl(dependentReturnType11.ts, 190, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 192, 12)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 192, 31)) +>"some prop" : Symbol("some prop", Decl(dependentReturnType11.ts, 192, 39)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 192, 12)) +>Ret : Symbol(Ret, Decl(dependentReturnType11.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 192, 12)) + + if (param["some prop"]) { +>param : Symbol(param, Decl(dependentReturnType11.ts, 192, 31)) +>"some prop" : Symbol("some prop", Decl(dependentReturnType11.ts, 192, 39)) + + const param = { "some prop": false }; +>param : Symbol(param, Decl(dependentReturnType11.ts, 194, 13)) +>"some prop" : Symbol("some prop", Decl(dependentReturnType11.ts, 194, 23)) + + return 1; + } + return 2; +} + +function h5(param: T): Ret { +>h5 : Symbol(h5, Decl(dependentReturnType11.ts, 198, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 200, 12)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 200, 31)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 200, 12)) +>Ret : Symbol(Ret, Decl(dependentReturnType11.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 200, 12)) + { + const param = true; +>param : Symbol(param, Decl(dependentReturnType11.ts, 202, 13)) + + if (param) { +>param : Symbol(param, Decl(dependentReturnType11.ts, 202, 13)) + + return 1; // Bad. + } + return 2; + } +} + +// Tests for optionality of parameters and properties. + +type RetU = +>RetU : Symbol(RetU, Decl(dependentReturnType11.ts, 208, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 212, 10)) + + T extends true ? 1 : +>T : Symbol(T, Decl(dependentReturnType11.ts, 212, 10)) + + T extends false ? 2 : +>T : Symbol(T, Decl(dependentReturnType11.ts, 212, 10)) + + T extends undefined ? 3 : +>T : Symbol(T, Decl(dependentReturnType11.ts, 212, 10)) + + never; + +function fn1(param?: T): RetU { // Bad. +>fn1 : Symbol(fn1, Decl(dependentReturnType11.ts, 216, 10)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 218, 13)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 218, 32)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 218, 13)) +>RetU : Symbol(RetU, Decl(dependentReturnType11.ts, 208, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 218, 13)) + + if (param == undefined) { +>param : Symbol(param, Decl(dependentReturnType11.ts, 218, 32)) +>undefined : Symbol(undefined) + + return 3; + } + if (param) { +>param : Symbol(param, Decl(dependentReturnType11.ts, 218, 32)) + + return 1; + } + return 2; +} + +function fn2(param?: T): RetU { +>fn2 : Symbol(fn2, Decl(dependentReturnType11.ts, 226, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 228, 13)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 228, 44)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 228, 13)) +>RetU : Symbol(RetU, Decl(dependentReturnType11.ts, 208, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 228, 13)) + + if (param == undefined) { +>param : Symbol(param, Decl(dependentReturnType11.ts, 228, 44)) +>undefined : Symbol(undefined) + + return 3; + } + if (param) { +>param : Symbol(param, Decl(dependentReturnType11.ts, 228, 44)) + + return 1; + } + return 2; +} + +function fn3(param: { prop?: T }): RetU { +>fn3 : Symbol(fn3, Decl(dependentReturnType11.ts, 236, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 238, 13)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 238, 44)) +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 238, 52)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 238, 13)) +>RetU : Symbol(RetU, Decl(dependentReturnType11.ts, 208, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 238, 13)) + + if (param.prop == undefined) { +>param.prop : Symbol(prop, Decl(dependentReturnType11.ts, 238, 52)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 238, 44)) +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 238, 52)) +>undefined : Symbol(undefined) + + return 3; + } + if (param.prop) { +>param.prop : Symbol(prop, Decl(dependentReturnType11.ts, 238, 52)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 238, 44)) +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 238, 52)) + + return 1; + } + return 2; +} + +function fn4({ prop }: { prop?: T }): RetU { +>fn4 : Symbol(fn4, Decl(dependentReturnType11.ts, 246, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 248, 13)) +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 248, 45)) +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 248, 55)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 248, 13)) +>RetU : Symbol(RetU, Decl(dependentReturnType11.ts, 208, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 248, 13)) + + if (prop == undefined) { +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 248, 45)) +>undefined : Symbol(undefined) + + return 3; + } + if (prop) { +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 248, 45)) + + return 1; + } + return 2; +} + +function fn5(param?: { prop: T }): RetU { // Bad. +>fn5 : Symbol(fn5, Decl(dependentReturnType11.ts, 256, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 258, 13)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 258, 44)) +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 258, 53)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 258, 13)) +>RetU : Symbol(RetU, Decl(dependentReturnType11.ts, 208, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 258, 13)) + + if (param.prop == undefined) { +>param.prop : Symbol(prop, Decl(dependentReturnType11.ts, 258, 53)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 258, 44)) +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 258, 53)) +>undefined : Symbol(undefined) + + return 3; + } + if (param.prop) { +>param.prop : Symbol(prop, Decl(dependentReturnType11.ts, 258, 53)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 258, 44)) +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 258, 53)) + + return 1; + } + return 2; +} + +function fn6(param: { prop1?: { prop?: T } }): RetU { // Bad. +>fn6 : Symbol(fn6, Decl(dependentReturnType11.ts, 266, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 268, 13)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 268, 44)) +>prop1 : Symbol(prop1, Decl(dependentReturnType11.ts, 268, 52)) +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 268, 62)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 268, 13)) +>RetU : Symbol(RetU, Decl(dependentReturnType11.ts, 208, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 268, 13)) + + if (param.prop1.prop == undefined) { +>param.prop1.prop : Symbol(prop, Decl(dependentReturnType11.ts, 268, 62)) +>param.prop1 : Symbol(prop1, Decl(dependentReturnType11.ts, 268, 52)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 268, 44)) +>prop1 : Symbol(prop1, Decl(dependentReturnType11.ts, 268, 52)) +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 268, 62)) +>undefined : Symbol(undefined) + + return 3; + } + if (param.prop1.prop) { +>param.prop1.prop : Symbol(prop, Decl(dependentReturnType11.ts, 268, 62)) +>param.prop1 : Symbol(prop1, Decl(dependentReturnType11.ts, 268, 52)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 268, 44)) +>prop1 : Symbol(prop1, Decl(dependentReturnType11.ts, 268, 52)) +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 268, 62)) + + return 1; + } + return 2; +} + +type Not = T extends true ? false : true; +>Not : Symbol(Not, Decl(dependentReturnType11.ts, 276, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 278, 9)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 278, 9)) + +function transformedTypeArgument(param: Type5>): Ret { // Bad. +>transformedTypeArgument : Symbol(transformedTypeArgument, Decl(dependentReturnType11.ts, 278, 60)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 280, 33)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 280, 52)) +>Type5 : Symbol(Type5, Decl(dependentReturnType11.ts, 118, 1)) +>Not : Symbol(Not, Decl(dependentReturnType11.ts, 276, 1)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 280, 33)) +>Ret : Symbol(Ret, Decl(dependentReturnType11.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType11.ts, 280, 33)) + + return param.prop ? 1 : 2; +>param.prop : Symbol(prop, Decl(dependentReturnType11.ts, 120, 17)) +>param : Symbol(param, Decl(dependentReturnType11.ts, 280, 52)) +>prop : Symbol(prop, Decl(dependentReturnType11.ts, 120, 17)) +} diff --git a/tsc/testdata/baselines/reference/compiler/dependentReturnType11.types b/tsc/testdata/baselines/reference/compiler/dependentReturnType11.types new file mode 100644 index 0000000000000..8b9ac870dc0bb --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/dependentReturnType11.types @@ -0,0 +1,735 @@ +//// [tests/cases/compiler/dependentReturnType11.ts] //// + +=== dependentReturnType11.ts === +type Ret = +>Ret : Ret + + T extends true ? 1 : +>true : true + + T extends false ? 2 : +>false : false + + never; + +// Tests for constructing narrowable reference. + +function f1(param: T): Ret { +>f1 : (param: T) => Ret +>param : T + + return param ? 1 : 2; +>param ? 1 : 2 : 1 | 2 +>param : T +>1 : 1 +>2 : 2 +} + +function f2(param: { prop: T }): Ret { +>f2 : (param: { prop: T; }) => Ret +>param : { prop: T; } +>prop : T + + return param.prop ? 1 : 2; +>param.prop ? 1 : 2 : 1 | 2 +>param.prop : T +>param : { prop: T; } +>prop : T +>1 : 1 +>2 : 2 +} + +function f3({ prop }: { prop: T }): Ret { +>f3 : ({ prop }: { prop: T; }) => Ret +>prop : T +>prop : T + + return prop ? 1 : 2; +>prop ? 1 : 2 : 1 | 2 +>prop : T +>1 : 1 +>2 : 2 +} + +function f4({ prop1 }: { prop1: { prop2: T } }): Ret { +>f4 : ({ prop1 }: { prop1: { prop2: T; }; }) => Ret +>prop1 : { prop2: T; } +>prop1 : { prop2: T; } +>prop2 : T + + return prop1.prop2 ? 1 : 2; +>prop1.prop2 ? 1 : 2 : 1 | 2 +>prop1.prop2 : T +>prop1 : { prop2: T; } +>prop2 : T +>1 : 1 +>2 : 2 +} + +function f5(param: { prop1: { prop2: T } }): Ret { +>f5 : (param: { prop1: { prop2: T; }; }) => Ret +>param : { prop1: { prop2: T; }; } +>prop1 : { prop2: T; } +>prop2 : T + + return param.prop1.prop2 ? 1 : 2; +>param.prop1.prop2 ? 1 : 2 : 1 | 2 +>param.prop1.prop2 : T +>param.prop1 : { prop2: T; } +>param : { prop1: { prop2: T; }; } +>prop1 : { prop2: T; } +>prop2 : T +>1 : 1 +>2 : 2 +} + +function f6({ prop1: { prop2: a } }: { prop1: { prop2: T } }): Ret { +>f6 : ({ prop1: { prop2: a } }: { prop1: { prop2: T; }; }) => Ret +>prop1 : any +>prop2 : any +>a : T +>prop1 : { prop2: T; } +>prop2 : T + + return a ? 1 : 2; +>a ? 1 : 2 : 1 | 2 +>a : T +>1 : 1 +>2 : 2 +} + +function f7({ prop1: { prop2 } }: { prop1: { prop2: T } }): Ret { +>f7 : ({ prop1: { prop2 } }: { prop1: { prop2: T; }; }) => Ret +>prop1 : any +>prop2 : T +>prop1 : { prop2: T; } +>prop2 : T + + return prop2 ? 1 : 2; +>prop2 ? 1 : 2 : 1 | 2 +>prop2 : T +>1 : 1 +>2 : 2 +} + +function f8({ prop1 }: T): Ret { // Bad. +>f8 : ({ prop1 }: T) => Ret +>prop1 : any + + return prop1 ? 1 : 2; +>prop1 ? 1 : 2 : 1 | 2 +>prop1 : any +>1 : 1 +>2 : 2 +} + +function f9(param: { "some prop": T }): Ret { +>f9 : (param: { "some prop": T; }) => Ret +>param : { "some prop": T; } +>"some prop" : T + + return param["some prop"] ? 1 : 2; +>param["some prop"] ? 1 : 2 : 1 | 2 +>param["some prop"] : T +>param : { "some prop": T; } +>"some prop" : "some prop" +>1 : 1 +>2 : 2 +} + +// Tests for detection of valid narrowable type parameter references. + +function g1(param: T): Ret { +>g1 : (param: T) => Ret +>param : T + + return param ? 1 : 2; +>param ? 1 : 2 : 1 | 2 +>param : T +>1 : 1 +>2 : 2 +} + +function g2(param: { prop: T }): Ret { +>g2 : (param: { prop: T; }) => Ret +>param : { prop: T; } +>prop : T + + return param.prop ? 1 : 2; +>param.prop ? 1 : 2 : 1 | 2 +>param.prop : T +>param : { prop: T; } +>prop : T +>1 : 1 +>2 : 2 +} + +class Dog { +>Dog : Dog + + bark(): void {} +>bark : () => void +} + +class Cat { +>Cat : Cat + + meow(): void {} +>meow : () => void + +} + +type Type1 = { prop: T, prop2: Cat | Dog } +>Type1 : Type1 +>prop : T +>prop2 : Cat | Dog + +function g3(param: Type1): Ret { +>g3 : (param: Type1) => Ret +>param : Type1 + + return param.prop ? 1 : 2; +>param.prop ? 1 : 2 : 1 | 2 +>param.prop : T +>param : Type1 +>prop : T +>1 : 1 +>2 : 2 +} + +type TypeUnused = string; +>TypeUnused : string + +function g4(param: Type1, other: TypeUnused): Ret { // Bad. +>g4 : (param: Type1, other: TypeUnused) => Ret +>param : Type1 +>other : string + + return param.prop ? 1 : 2; +>param.prop ? 1 : 2 : 1 | 2 +>param.prop : T +>param : Type1 +>prop : T +>1 : 1 +>2 : 2 +} + +interface Type2 { + prop: T, +>prop : T + + [s: string]: boolean | undefined, +>s : string +} + +function g5(param: Type2): Ret { +>g5 : (param: Type2) => Ret +>param : Type2 + + return param.prop ? 1 : 2; +>param.prop ? 1 : 2 : 1 | 2 +>param.prop : T +>param : Type2 +>prop : T +>1 : 1 +>2 : 2 +} + +interface Type3 { + prop: T, +>prop : T +} + +interface Type3 { + prop2: Cat | Dog, +>prop2 : Cat | Dog +} + +function g6(param: Type3): Ret { // Unsupported for now: interface merging. +>g6 : (param: Type3) => Ret +>param : Type3 + + return param.prop ? 1 : 2; +>param.prop ? 1 : 2 : 1 | 2 +>param.prop : T +>param : Type3 +>prop : T +>1 : 1 +>2 : 2 +} + +type Type4 = { +>Type4 : Type4 + + prop: T, +>prop : T + + prop2: S[], +>prop2 : S[] +} + +function g7(param: Type4): Ret { +>g7 : (param: Type4) => Ret +>param : Type4 + + return param.prop ? 1 : 2; +>param.prop ? 1 : 2 : 1 | 2 +>param.prop : T +>param : Type4 +>prop : T +>1 : 1 +>2 : 2 +} + +function g8(param: Type1 & { prop2: Cat | Dog }): Ret { +>g8 : (param: Type1 & { prop2: Cat | Dog; }) => Ret +>param : Type1 & { prop2: Cat | Dog; } +>prop2 : Cat | Dog + + return param.prop ? 1 : 2; +>param.prop ? 1 : 2 : 1 | 2 +>param.prop : T +>param : Type1 & { prop2: Cat | Dog; } +>prop : T +>1 : 1 +>2 : 2 +} + +function g9([ prop ]: [T]): Ret { // Unsupported. +>g9 : ([prop]: [T]) => Ret +>prop : T + + return prop ? 1 : 2; +>prop ? 1 : 2 : 1 | 2 +>prop : T +>1 : 1 +>2 : 2 +} + +function g10(param: [T]): Ret { // Unsupported. +>g10 : (param: [T]) => Ret +>param : [T] + + return param[0] ? 1 : 2; +>param[0] ? 1 : 2 : 1 | 2 +>param[0] : T +>param : [T] +>0 : 0 +>1 : 1 +>2 : 2 +} + +function g11(param: T[]): Ret { // Bad. +>g11 : (param: T[]) => Ret +>param : T[] + + return param[0] ? 1 : 2; +>param[0] ? 1 : 2 : 1 | 2 +>param[0] : T +>param : T[] +>0 : 0 +>1 : 1 +>2 : 2 +} + +type Type5 = { +>Type5 : Type5 + + prop: S, +>prop : S + + prop2: string[], +>prop2 : string[] +} + +function g12(param: Type5): Ret { +>g12 : (param: Type5) => Ret +>param : Type5 + + return param.prop ? 1 : 2; +>param.prop ? 1 : 2 : 1 | 2 +>param.prop : T +>param : Type5 +>prop : T +>1 : 1 +>2 : 2 +} + +type Type6 = { +>Type6 : Type6 + + prop: S, +>prop : S + + prop2: S[], +>prop2 : S[] +} + +function g13(param: Type6): Ret { // Bad. +>g13 : (param: Type6) => Ret +>param : Type6 + + return param.prop ? 1 : 2; +>param.prop ? 1 : 2 : 1 | 2 +>param.prop : T +>param : Type6 +>prop : T +>1 : 1 +>2 : 2 +} + +type Type7 = { +>Type7 : Type7 + + [Key in "prop"]: T +} + +function g14(param: Type7): Ret { // Unsupported. +>g14 : (param: Type7) => Ret +>param : Type7 + + return param.prop ? 1 : 2; +>param.prop ? 1 : 2 : 1 | 2 +>param.prop : T +>param : Type7 +>prop : T +>1 : 1 +>2 : 2 +} + +class Class1 { +>Class1 : Class1 + + prop!: T; +>prop : T +} + +function g15(param: Class1): Ret { +>g15 : (param: Class1) => Ret +>param : Class1 + + return param.prop ? 1 : 2; +>param.prop ? 1 : 2 : 1 | 2 +>param.prop : T +>param : Class1 +>prop : T +>1 : 1 +>2 : 2 +} + +type Type8 = { +>Type8 : Type8 + + prop: T, +>prop : T +} + +type Type9 = { +>Type9 : Type9 + + prop2: T, +>prop2 : T +} + +function g16(param: Type8["prop2"]>): Ret { // Unsupported. +>g16 : (param: Type8["prop2"]>) => Ret +>param : Type8 + + return param.prop ? 1 : 2; +>param.prop ? 1 : 2 : 1 | 2 +>param.prop : T +>param : Type8 +>prop : T +>1 : 1 +>2 : 2 +} + +// Tests for shadowing and resolving the constructed narrowable reference. + +function h1(param: T): Ret { +>h1 : (param: T) => Ret +>param : T + + if (param) { +>param : T + + const param = false; +>param : false +>false : false + + return 1; +>1 : 1 + } + return 2; +>2 : 2 +} + +function h2({ prop }: { prop: T}): Ret { +>h2 : ({ prop }: { prop: T; }) => Ret +>prop : T +>prop : T + + if (prop) { +>prop : T + + const prop = false; +>prop : false +>false : false + + return 1; +>1 : 1 + } + return 2; +>2 : 2 +} + +function h3(param: { prop: T}): Ret { +>h3 : (param: { prop: T; }) => Ret +>param : { prop: T; } +>prop : T + + if (param.prop) { +>param.prop : T +>param : { prop: T; } +>prop : T + + const param = { prop: false }; +>param : { prop: boolean; } +>{ prop: false } : { prop: boolean; } +>prop : boolean +>false : false + + return 1; +>1 : 1 + } + return 2; +>2 : 2 +} + +function h4(param: { "some prop": T }): Ret { +>h4 : (param: { "some prop": T; }) => Ret +>param : { "some prop": T; } +>"some prop" : T + + if (param["some prop"]) { +>param["some prop"] : T +>param : { "some prop": T; } +>"some prop" : "some prop" + + const param = { "some prop": false }; +>param : { "some prop": boolean; } +>{ "some prop": false } : { "some prop": boolean; } +>"some prop" : boolean +>false : false + + return 1; +>1 : 1 + } + return 2; +>2 : 2 +} + +function h5(param: T): Ret { +>h5 : (param: T) => Ret +>param : T + { + const param = true; +>param : true +>true : true + + if (param) { +>param : true + + return 1; // Bad. +>1 : 1 + } + return 2; +>2 : 2 + } +} + +// Tests for optionality of parameters and properties. + +type RetU = +>RetU : RetU + + T extends true ? 1 : +>true : true + + T extends false ? 2 : +>false : false + + T extends undefined ? 3 : + never; + +function fn1(param?: T): RetU { // Bad. +>fn1 : (param?: T) => RetU +>param : T | undefined + + if (param == undefined) { +>param == undefined : boolean +>param : T | undefined +>undefined : undefined + + return 3; +>3 : 3 + } + if (param) { +>param : T + + return 1; +>1 : 1 + } + return 2; +>2 : 2 +} + +function fn2(param?: T): RetU { +>fn2 : (param?: T) => RetU +>param : T | undefined + + if (param == undefined) { +>param == undefined : boolean +>param : T | undefined +>undefined : undefined + + return 3; +>3 : 3 + } + if (param) { +>param : NonNullable + + return 1; +>1 : 1 + } + return 2; +>2 : 2 +} + +function fn3(param: { prop?: T }): RetU { +>fn3 : (param: { prop?: T; }) => RetU +>param : { prop?: T; } +>prop : T | undefined + + if (param.prop == undefined) { +>param.prop == undefined : boolean +>param.prop : T | undefined +>param : { prop?: T; } +>prop : T | undefined +>undefined : undefined + + return 3; +>3 : 3 + } + if (param.prop) { +>param.prop : NonNullable +>param : { prop?: T; } +>prop : NonNullable + + return 1; +>1 : 1 + } + return 2; +>2 : 2 +} + +function fn4({ prop }: { prop?: T }): RetU { +>fn4 : ({ prop }: { prop?: T; }) => RetU +>prop : T | undefined +>prop : T | undefined + + if (prop == undefined) { +>prop == undefined : boolean +>prop : T | undefined +>undefined : undefined + + return 3; +>3 : 3 + } + if (prop) { +>prop : NonNullable + + return 1; +>1 : 1 + } + return 2; +>2 : 2 +} + +function fn5(param?: { prop: T }): RetU { // Bad. +>fn5 : (param?: { prop: T; }) => RetU +>param : { prop: T; } | undefined +>prop : T + + if (param.prop == undefined) { +>param.prop == undefined : boolean +>param.prop : T +>param : { prop: T; } | undefined +>prop : T +>undefined : undefined + + return 3; +>3 : 3 + } + if (param.prop) { +>param.prop : NonNullable +>param : { prop: T; } | undefined +>prop : NonNullable + + return 1; +>1 : 1 + } + return 2; +>2 : 2 +} + +function fn6(param: { prop1?: { prop?: T } }): RetU { // Bad. +>fn6 : (param: { prop1?: { prop?: T; }; }) => RetU +>param : { prop1?: { prop?: T; }; } +>prop1 : { prop?: T; } | undefined +>prop : T | undefined + + if (param.prop1.prop == undefined) { +>param.prop1.prop == undefined : boolean +>param.prop1.prop : T | undefined +>param.prop1 : { prop?: T; } | undefined +>param : { prop1?: { prop?: T; }; } +>prop1 : { prop?: T; } | undefined +>prop : T | undefined +>undefined : undefined + + return 3; +>3 : 3 + } + if (param.prop1.prop) { +>param.prop1.prop : NonNullable +>param.prop1 : { prop?: T; } | undefined +>param : { prop1?: { prop?: T; }; } +>prop1 : { prop?: T; } | undefined +>prop : NonNullable + + return 1; +>1 : 1 + } + return 2; +>2 : 2 +} + +type Not = T extends true ? false : true; +>Not : Not +>true : true +>false : false +>true : true + +function transformedTypeArgument(param: Type5>): Ret { // Bad. +>transformedTypeArgument : (param: Type5>) => Ret +>param : Type5> + + return param.prop ? 1 : 2; +>param.prop ? 1 : 2 : 1 | 2 +>param.prop : Not +>param : Type5> +>prop : Not +>1 : 1 +>2 : 2 +} diff --git a/tsc/testdata/baselines/reference/compiler/dependentReturnType12.errors.txt b/tsc/testdata/baselines/reference/compiler/dependentReturnType12.errors.txt new file mode 100644 index 0000000000000..c2fdfab1cc166 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/dependentReturnType12.errors.txt @@ -0,0 +1,56 @@ +dependentReturnType12.ts(31,9): error TS2322: Type '1' is not assignable to type 'RetU'. +dependentReturnType12.ts(33,5): error TS2322: Type '2' is not assignable to type 'RetU'. +dependentReturnType12.ts(38,9): error TS2322: Type '1' is not assignable to type 'RetU'. +dependentReturnType12.ts(40,5): error TS2322: Type '2' is not assignable to type 'RetU'. + + +==== dependentReturnType12.ts (4 errors) ==== + // Tests for optionality of parameters and properties. + + type RetU = + T extends string ? 2 : + T extends number ? 1 : + never; + + function fn1(param?: T): RetU { + if (typeof param === "number") { + return 1; + } + return 2; + } + + function fn3(param: { prop?: T }): RetU { + if (typeof param.prop === "number") { + return 1; + } + return 2; + } + + function fn4({ prop }: { prop?: T }): RetU { + if (typeof prop === "number") { + return 1; + } + return 2; + } + + function fn5(param?: { prop: T }): RetU { // Bad. + if (typeof param.prop === "number") { + return 1; + ~~~~~~ +!!! error TS2322: Type '1' is not assignable to type 'RetU'. + } + return 2; + ~~~~~~ +!!! error TS2322: Type '2' is not assignable to type 'RetU'. + } + + function fn6(param: { prop1?: { prop?: T } }): RetU { // Bad. + if (typeof param.prop1.prop === "number") { + return 1; + ~~~~~~ +!!! error TS2322: Type '1' is not assignable to type 'RetU'. + } + return 2; + ~~~~~~ +!!! error TS2322: Type '2' is not assignable to type 'RetU'. + } \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/dependentReturnType12.symbols b/tsc/testdata/baselines/reference/compiler/dependentReturnType12.symbols new file mode 100644 index 0000000000000..e26b0f0c2a887 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/dependentReturnType12.symbols @@ -0,0 +1,109 @@ +//// [tests/cases/compiler/dependentReturnType12.ts] //// + +=== dependentReturnType12.ts === +// Tests for optionality of parameters and properties. + +type RetU = +>RetU : Symbol(RetU, Decl(dependentReturnType12.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType12.ts, 2, 10)) + + T extends string ? 2 : +>T : Symbol(T, Decl(dependentReturnType12.ts, 2, 10)) + + T extends number ? 1 : +>T : Symbol(T, Decl(dependentReturnType12.ts, 2, 10)) + + never; + +function fn1(param?: T): RetU { +>fn1 : Symbol(fn1, Decl(dependentReturnType12.ts, 5, 10)) +>T : Symbol(T, Decl(dependentReturnType12.ts, 7, 13)) +>param : Symbol(param, Decl(dependentReturnType12.ts, 7, 40)) +>T : Symbol(T, Decl(dependentReturnType12.ts, 7, 13)) +>RetU : Symbol(RetU, Decl(dependentReturnType12.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType12.ts, 7, 13)) + + if (typeof param === "number") { +>param : Symbol(param, Decl(dependentReturnType12.ts, 7, 40)) + + return 1; + } + return 2; +} + +function fn3(param: { prop?: T }): RetU { +>fn3 : Symbol(fn3, Decl(dependentReturnType12.ts, 12, 1)) +>T : Symbol(T, Decl(dependentReturnType12.ts, 14, 13)) +>param : Symbol(param, Decl(dependentReturnType12.ts, 14, 40)) +>prop : Symbol(prop, Decl(dependentReturnType12.ts, 14, 48)) +>T : Symbol(T, Decl(dependentReturnType12.ts, 14, 13)) +>RetU : Symbol(RetU, Decl(dependentReturnType12.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType12.ts, 14, 13)) + + if (typeof param.prop === "number") { +>param.prop : Symbol(prop, Decl(dependentReturnType12.ts, 14, 48)) +>param : Symbol(param, Decl(dependentReturnType12.ts, 14, 40)) +>prop : Symbol(prop, Decl(dependentReturnType12.ts, 14, 48)) + + return 1; + } + return 2; +} + +function fn4({ prop }: { prop?: T }): RetU { +>fn4 : Symbol(fn4, Decl(dependentReturnType12.ts, 19, 1)) +>T : Symbol(T, Decl(dependentReturnType12.ts, 21, 13)) +>prop : Symbol(prop, Decl(dependentReturnType12.ts, 21, 41)) +>prop : Symbol(prop, Decl(dependentReturnType12.ts, 21, 51)) +>T : Symbol(T, Decl(dependentReturnType12.ts, 21, 13)) +>RetU : Symbol(RetU, Decl(dependentReturnType12.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType12.ts, 21, 13)) + + if (typeof prop === "number") { +>prop : Symbol(prop, Decl(dependentReturnType12.ts, 21, 41)) + + return 1; + } + return 2; +} + +function fn5(param?: { prop: T }): RetU { // Bad. +>fn5 : Symbol(fn5, Decl(dependentReturnType12.ts, 26, 1)) +>T : Symbol(T, Decl(dependentReturnType12.ts, 28, 13)) +>param : Symbol(param, Decl(dependentReturnType12.ts, 28, 40)) +>prop : Symbol(prop, Decl(dependentReturnType12.ts, 28, 49)) +>T : Symbol(T, Decl(dependentReturnType12.ts, 28, 13)) +>RetU : Symbol(RetU, Decl(dependentReturnType12.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType12.ts, 28, 13)) + + if (typeof param.prop === "number") { +>param.prop : Symbol(prop, Decl(dependentReturnType12.ts, 28, 49)) +>param : Symbol(param, Decl(dependentReturnType12.ts, 28, 40)) +>prop : Symbol(prop, Decl(dependentReturnType12.ts, 28, 49)) + + return 1; + } + return 2; +} + +function fn6(param: { prop1?: { prop?: T } }): RetU { // Bad. +>fn6 : Symbol(fn6, Decl(dependentReturnType12.ts, 33, 1)) +>T : Symbol(T, Decl(dependentReturnType12.ts, 35, 13)) +>param : Symbol(param, Decl(dependentReturnType12.ts, 35, 40)) +>prop1 : Symbol(prop1, Decl(dependentReturnType12.ts, 35, 48)) +>prop : Symbol(prop, Decl(dependentReturnType12.ts, 35, 58)) +>T : Symbol(T, Decl(dependentReturnType12.ts, 35, 13)) +>RetU : Symbol(RetU, Decl(dependentReturnType12.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType12.ts, 35, 13)) + + if (typeof param.prop1.prop === "number") { +>param.prop1.prop : Symbol(prop, Decl(dependentReturnType12.ts, 35, 58)) +>param.prop1 : Symbol(prop1, Decl(dependentReturnType12.ts, 35, 48)) +>param : Symbol(param, Decl(dependentReturnType12.ts, 35, 40)) +>prop1 : Symbol(prop1, Decl(dependentReturnType12.ts, 35, 48)) +>prop : Symbol(prop, Decl(dependentReturnType12.ts, 35, 58)) + + return 1; + } + return 2; +} diff --git a/tsc/testdata/baselines/reference/compiler/dependentReturnType12.types b/tsc/testdata/baselines/reference/compiler/dependentReturnType12.types new file mode 100644 index 0000000000000..a5b09723e4ca3 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/dependentReturnType12.types @@ -0,0 +1,109 @@ +//// [tests/cases/compiler/dependentReturnType12.ts] //// + +=== dependentReturnType12.ts === +// Tests for optionality of parameters and properties. + +type RetU = +>RetU : RetU + + T extends string ? 2 : + T extends number ? 1 : + never; + +function fn1(param?: T): RetU { +>fn1 : (param?: T) => RetU +>param : T + + if (typeof param === "number") { +>typeof param === "number" : boolean +>typeof param : "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" +>param : T +>"number" : "number" + + return 1; +>1 : 1 + } + return 2; +>2 : 2 +} + +function fn3(param: { prop?: T }): RetU { +>fn3 : (param: { prop?: T; }) => RetU +>param : { prop?: T; } +>prop : T + + if (typeof param.prop === "number") { +>typeof param.prop === "number" : boolean +>typeof param.prop : "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" +>param.prop : T +>param : { prop?: T; } +>prop : T +>"number" : "number" + + return 1; +>1 : 1 + } + return 2; +>2 : 2 +} + +function fn4({ prop }: { prop?: T }): RetU { +>fn4 : ({ prop }: { prop?: T; }) => RetU +>prop : T +>prop : T + + if (typeof prop === "number") { +>typeof prop === "number" : boolean +>typeof prop : "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" +>prop : T +>"number" : "number" + + return 1; +>1 : 1 + } + return 2; +>2 : 2 +} + +function fn5(param?: { prop: T }): RetU { // Bad. +>fn5 : (param?: { prop: T; }) => RetU +>param : { prop: T; } +>prop : T + + if (typeof param.prop === "number") { +>typeof param.prop === "number" : boolean +>typeof param.prop : "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" +>param.prop : T +>param : { prop: T; } +>prop : T +>"number" : "number" + + return 1; +>1 : 1 + } + return 2; +>2 : 2 +} + +function fn6(param: { prop1?: { prop?: T } }): RetU { // Bad. +>fn6 : (param: { prop1?: { prop?: T; }; }) => RetU +>param : { prop1?: { prop?: T; }; } +>prop1 : { prop?: T; } +>prop : T + + if (typeof param.prop1.prop === "number") { +>typeof param.prop1.prop === "number" : boolean +>typeof param.prop1.prop : "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" +>param.prop1.prop : T +>param.prop1 : { prop?: T; } +>param : { prop1?: { prop?: T; }; } +>prop1 : { prop?: T; } +>prop : T +>"number" : "number" + + return 1; +>1 : 1 + } + return 2; +>2 : 2 +} diff --git a/tsc/testdata/baselines/reference/compiler/dependentReturnType13.errors.txt b/tsc/testdata/baselines/reference/compiler/dependentReturnType13.errors.txt new file mode 100644 index 0000000000000..a37ae3d553d0b --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/dependentReturnType13.errors.txt @@ -0,0 +1,151 @@ +dependentReturnType13.ts(31,9): error TS2322: Type '3' is not assignable to type 'T extends string ? 1 : T extends string[] ? 2 : T extends number[] ? 3 : never'. +dependentReturnType13.ts(34,9): error TS2322: Type '2' is not assignable to type 'T extends string ? 1 : T extends string[] ? 2 : T extends number[] ? 3 : never'. +dependentReturnType13.ts(36,5): error TS2322: Type '1' is not assignable to type 'T extends string ? 1 : T extends string[] ? 2 : T extends number[] ? 3 : never'. +dependentReturnType13.ts(54,9): error TS2322: Type '2' is not assignable to type 'T extends Cat ? 1 : T extends Dog ? 2 : never'. +dependentReturnType13.ts(57,5): error TS2322: Type '1' is not assignable to type 'T extends Cat ? 1 : T extends Dog ? 2 : never'. +dependentReturnType13.ts(84,9): error TS2322: Type '2' is not assignable to type 'T extends number[] ? 2 : U extends true ? T extends string[] ? 3 : T extends string ? 1 : never : U extends false ? T extends string[] ? 4 : T extends string ? 5 : never : never'. +dependentReturnType13.ts(88,13): error TS2322: Type '3' is not assignable to type 'T extends number[] ? 2 : U extends true ? T extends string[] ? 3 : T extends string ? 1 : never : U extends false ? T extends string[] ? 4 : T extends string ? 5 : never : never'. +dependentReturnType13.ts(90,9): error TS2322: Type '1' is not assignable to type 'T extends number[] ? 2 : U extends true ? T extends string[] ? 3 : T extends string ? 1 : never : U extends false ? T extends string[] ? 4 : T extends string ? 5 : never : never'. +dependentReturnType13.ts(93,9): error TS2322: Type '4' is not assignable to type 'T extends number[] ? 2 : U extends true ? T extends string[] ? 3 : T extends string ? 1 : never : U extends false ? T extends string[] ? 4 : T extends string ? 5 : never : never'. +dependentReturnType13.ts(95,5): error TS2322: Type '5' is not assignable to type 'T extends number[] ? 2 : U extends true ? T extends string[] ? 3 : T extends string ? 1 : never : U extends false ? T extends string[] ? 4 : T extends string ? 5 : never : never'. + + +==== dependentReturnType13.ts (10 errors) ==== + // Restrictions on what kind of union types can be narrowed. + + function f1(param: T): + T extends string ? 1 : + T extends string[] ? 2 : + never { + if (Array.isArray(param)) { + return 2; + } + return 1; + } + + function f2(param: T): + T extends string ? 1 : + T extends string[] | number[] ? 2 : + never { + if (Array.isArray(param)) { + return 2; + } + return 1; + } + + declare function isNumberArray(x: unknown): x is number[]; + + function f3(param: T): // Bad. + T extends string ? 1 : + T extends string[] ? 2 : + T extends number[] ? 3 : + never { + if (isNumberArray(param)) { + return 3; + ~~~~~~ +!!! error TS2322: Type '3' is not assignable to type 'T extends string ? 1 : T extends string[] ? 2 : T extends number[] ? 3 : never'. + } + if (Array.isArray(param)) { + return 2; + ~~~~~~ +!!! error TS2322: Type '2' is not assignable to type 'T extends string ? 1 : T extends string[] ? 2 : T extends number[] ? 3 : never'. + } + return 1; + ~~~~~~ +!!! error TS2322: Type '1' is not assignable to type 'T extends string ? 1 : T extends string[] ? 2 : T extends number[] ? 3 : never'. + } + + class Dog { + bark(): void {} + } + + class Cat { + meow(): void {} + + } + + function f4(param: T): // Bad. + T extends Cat ? 1 : + T extends Dog ? 2 : + never { + if ('bark' in param) { + const _: Dog = param; + return 2; + ~~~~~~ +!!! error TS2322: Type '2' is not assignable to type 'T extends Cat ? 1 : T extends Dog ? 2 : never'. + } + const _: Cat = param; + return 1; + ~~~~~~ +!!! error TS2322: Type '1' is not assignable to type 'T extends Cat ? 1 : T extends Dog ? 2 : never'. + } + + function f5(param: T): + T extends string ? 1 : + T extends number | string[] ? 2 : + never { + if (Array.isArray(param) || typeof param === "number") { + const _: string[] | number = param; + return 2; + } + const _: string = param; + return 1; + } + + function f6(param: T, other: U): + T extends number[] ? 2 : + U extends true ? + T extends string[] ? 3 : + T extends string ? 1 : + never : + U extends false ? + T extends string[] ? 4 : + T extends string ? 5 : + never : + never { + if (isNumberArray(param)) { + return 2; + ~~~~~~ +!!! error TS2322: Type '2' is not assignable to type 'T extends number[] ? 2 : U extends true ? T extends string[] ? 3 : T extends string ? 1 : never : U extends false ? T extends string[] ? 4 : T extends string ? 5 : never : never'. + } + if (other) { + if (Array.isArray(param)) { + return 3; + ~~~~~~ +!!! error TS2322: Type '3' is not assignable to type 'T extends number[] ? 2 : U extends true ? T extends string[] ? 3 : T extends string ? 1 : never : U extends false ? T extends string[] ? 4 : T extends string ? 5 : never : never'. + } + return 1; + ~~~~~~ +!!! error TS2322: Type '1' is not assignable to type 'T extends number[] ? 2 : U extends true ? T extends string[] ? 3 : T extends string ? 1 : never : U extends false ? T extends string[] ? 4 : T extends string ? 5 : never : never'. + } + if (Array.isArray(param)) { + return 4; + ~~~~~~ +!!! error TS2322: Type '4' is not assignable to type 'T extends number[] ? 2 : U extends true ? T extends string[] ? 3 : T extends string ? 1 : never : U extends false ? T extends string[] ? 4 : T extends string ? 5 : never : never'. + } + return 5; + ~~~~~~ +!!! error TS2322: Type '5' is not assignable to type 'T extends number[] ? 2 : U extends true ? T extends string[] ? 3 : T extends string ? 1 : never : U extends false ? T extends string[] ? 4 : T extends string ? 5 : never : never'. + } + + function f7(param: T, other: U): + U extends number ? + T extends string[] ? 2 : + T extends string ? 1 : + never : + U extends number[] ? + T extends string[] ? 4 : + T extends string ? 3 : + never : + never { + if (Array.isArray(other)) { + if (Array.isArray(param)) { + return 4; + } + return 3; + } + if (Array.isArray(param)) { + return 2; + } + return 1; + } \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/dependentReturnType13.symbols b/tsc/testdata/baselines/reference/compiler/dependentReturnType13.symbols new file mode 100644 index 0000000000000..95e348425ac6a --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/dependentReturnType13.symbols @@ -0,0 +1,293 @@ +//// [tests/cases/compiler/dependentReturnType13.ts] //// + +=== dependentReturnType13.ts === +// Restrictions on what kind of union types can be narrowed. + +function f1(param: T): +>f1 : Symbol(f1, Decl(dependentReturnType13.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType13.ts, 2, 12)) +>param : Symbol(param, Decl(dependentReturnType13.ts, 2, 41)) +>T : Symbol(T, Decl(dependentReturnType13.ts, 2, 12)) + + T extends string ? 1 : +>T : Symbol(T, Decl(dependentReturnType13.ts, 2, 12)) + + T extends string[] ? 2 : +>T : Symbol(T, Decl(dependentReturnType13.ts, 2, 12)) + + never { + if (Array.isArray(param)) { +>Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more) +>isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>param : Symbol(param, Decl(dependentReturnType13.ts, 2, 41)) + + return 2; + } + return 1; +} + +function f2(param: T): +>f2 : Symbol(f2, Decl(dependentReturnType13.ts, 10, 1)) +>T : Symbol(T, Decl(dependentReturnType13.ts, 12, 12)) +>param : Symbol(param, Decl(dependentReturnType13.ts, 12, 52)) +>T : Symbol(T, Decl(dependentReturnType13.ts, 12, 12)) + + T extends string ? 1 : +>T : Symbol(T, Decl(dependentReturnType13.ts, 12, 12)) + + T extends string[] | number[] ? 2 : +>T : Symbol(T, Decl(dependentReturnType13.ts, 12, 12)) + + never { + if (Array.isArray(param)) { +>Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more) +>isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>param : Symbol(param, Decl(dependentReturnType13.ts, 12, 52)) + + return 2; + } + return 1; +} + +declare function isNumberArray(x: unknown): x is number[]; +>isNumberArray : Symbol(isNumberArray, Decl(dependentReturnType13.ts, 20, 1)) +>x : Symbol(x, Decl(dependentReturnType13.ts, 22, 31)) +>x : Symbol(x, Decl(dependentReturnType13.ts, 22, 31)) + +function f3(param: T): // Bad. +>f3 : Symbol(f3, Decl(dependentReturnType13.ts, 22, 58)) +>T : Symbol(T, Decl(dependentReturnType13.ts, 24, 12)) +>param : Symbol(param, Decl(dependentReturnType13.ts, 24, 52)) +>T : Symbol(T, Decl(dependentReturnType13.ts, 24, 12)) + + T extends string ? 1 : +>T : Symbol(T, Decl(dependentReturnType13.ts, 24, 12)) + + T extends string[] ? 2 : +>T : Symbol(T, Decl(dependentReturnType13.ts, 24, 12)) + + T extends number[] ? 3 : +>T : Symbol(T, Decl(dependentReturnType13.ts, 24, 12)) + + never { + if (isNumberArray(param)) { +>isNumberArray : Symbol(isNumberArray, Decl(dependentReturnType13.ts, 20, 1)) +>param : Symbol(param, Decl(dependentReturnType13.ts, 24, 52)) + + return 3; + } + if (Array.isArray(param)) { +>Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more) +>isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>param : Symbol(param, Decl(dependentReturnType13.ts, 24, 52)) + + return 2; + } + return 1; +} + +class Dog { +>Dog : Symbol(Dog, Decl(dependentReturnType13.ts, 36, 1)) + + bark(): void {} +>bark : Symbol(Dog.bark, Decl(dependentReturnType13.ts, 38, 11)) +} + +class Cat { +>Cat : Symbol(Cat, Decl(dependentReturnType13.ts, 40, 1)) + + meow(): void {} +>meow : Symbol(Cat.meow, Decl(dependentReturnType13.ts, 42, 11)) + +} + +function f4(param: T): // Bad. +>f4 : Symbol(f4, Decl(dependentReturnType13.ts, 45, 1)) +>T : Symbol(T, Decl(dependentReturnType13.ts, 47, 12)) +>Cat : Symbol(Cat, Decl(dependentReturnType13.ts, 40, 1)) +>Dog : Symbol(Dog, Decl(dependentReturnType13.ts, 36, 1)) +>param : Symbol(param, Decl(dependentReturnType13.ts, 47, 33)) +>T : Symbol(T, Decl(dependentReturnType13.ts, 47, 12)) + + T extends Cat ? 1 : +>T : Symbol(T, Decl(dependentReturnType13.ts, 47, 12)) +>Cat : Symbol(Cat, Decl(dependentReturnType13.ts, 40, 1)) + + T extends Dog ? 2 : +>T : Symbol(T, Decl(dependentReturnType13.ts, 47, 12)) +>Dog : Symbol(Dog, Decl(dependentReturnType13.ts, 36, 1)) + + never { + if ('bark' in param) { +>param : Symbol(param, Decl(dependentReturnType13.ts, 47, 33)) + + const _: Dog = param; +>_ : Symbol(_, Decl(dependentReturnType13.ts, 52, 13)) +>Dog : Symbol(Dog, Decl(dependentReturnType13.ts, 36, 1)) +>param : Symbol(param, Decl(dependentReturnType13.ts, 47, 33)) + + return 2; + } + const _: Cat = param; +>_ : Symbol(_, Decl(dependentReturnType13.ts, 55, 9)) +>Cat : Symbol(Cat, Decl(dependentReturnType13.ts, 40, 1)) +>param : Symbol(param, Decl(dependentReturnType13.ts, 47, 33)) + + return 1; +} + +function f5(param: T): +>f5 : Symbol(f5, Decl(dependentReturnType13.ts, 57, 1)) +>T : Symbol(T, Decl(dependentReturnType13.ts, 59, 12)) +>param : Symbol(param, Decl(dependentReturnType13.ts, 59, 50)) +>T : Symbol(T, Decl(dependentReturnType13.ts, 59, 12)) + + T extends string ? 1 : +>T : Symbol(T, Decl(dependentReturnType13.ts, 59, 12)) + + T extends number | string[] ? 2 : +>T : Symbol(T, Decl(dependentReturnType13.ts, 59, 12)) + + never { + if (Array.isArray(param) || typeof param === "number") { +>Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more) +>isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>param : Symbol(param, Decl(dependentReturnType13.ts, 59, 50)) +>param : Symbol(param, Decl(dependentReturnType13.ts, 59, 50)) + + const _: string[] | number = param; +>_ : Symbol(_, Decl(dependentReturnType13.ts, 64, 13)) +>param : Symbol(param, Decl(dependentReturnType13.ts, 59, 50)) + + return 2; + } + const _: string = param; +>_ : Symbol(_, Decl(dependentReturnType13.ts, 67, 9)) +>param : Symbol(param, Decl(dependentReturnType13.ts, 59, 50)) + + return 1; +} + +function f6(param: T, other: U): +>f6 : Symbol(f6, Decl(dependentReturnType13.ts, 69, 1)) +>T : Symbol(T, Decl(dependentReturnType13.ts, 71, 12)) +>U : Symbol(U, Decl(dependentReturnType13.ts, 71, 51)) +>param : Symbol(param, Decl(dependentReturnType13.ts, 71, 71)) +>T : Symbol(T, Decl(dependentReturnType13.ts, 71, 12)) +>other : Symbol(other, Decl(dependentReturnType13.ts, 71, 80)) +>U : Symbol(U, Decl(dependentReturnType13.ts, 71, 51)) + + T extends number[] ? 2 : +>T : Symbol(T, Decl(dependentReturnType13.ts, 71, 12)) + + U extends true ? +>U : Symbol(U, Decl(dependentReturnType13.ts, 71, 51)) + + T extends string[] ? 3 : +>T : Symbol(T, Decl(dependentReturnType13.ts, 71, 12)) + + T extends string ? 1 : +>T : Symbol(T, Decl(dependentReturnType13.ts, 71, 12)) + + never : + U extends false ? +>U : Symbol(U, Decl(dependentReturnType13.ts, 71, 51)) + + T extends string[] ? 4 : +>T : Symbol(T, Decl(dependentReturnType13.ts, 71, 12)) + + T extends string ? 5 : +>T : Symbol(T, Decl(dependentReturnType13.ts, 71, 12)) + + never : + never { + if (isNumberArray(param)) { +>isNumberArray : Symbol(isNumberArray, Decl(dependentReturnType13.ts, 20, 1)) +>param : Symbol(param, Decl(dependentReturnType13.ts, 71, 71)) + + return 2; + } + if (other) { +>other : Symbol(other, Decl(dependentReturnType13.ts, 71, 80)) + + if (Array.isArray(param)) { +>Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more) +>isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>param : Symbol(param, Decl(dependentReturnType13.ts, 71, 71)) + + return 3; + } + return 1; + } + if (Array.isArray(param)) { +>Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more) +>isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>param : Symbol(param, Decl(dependentReturnType13.ts, 71, 71)) + + return 4; + } + return 5; +} + +function f7(param: T, other: U): +>f7 : Symbol(f7, Decl(dependentReturnType13.ts, 95, 1)) +>T : Symbol(T, Decl(dependentReturnType13.ts, 97, 12)) +>U : Symbol(U, Decl(dependentReturnType13.ts, 97, 40)) +>param : Symbol(param, Decl(dependentReturnType13.ts, 97, 70)) +>T : Symbol(T, Decl(dependentReturnType13.ts, 97, 12)) +>other : Symbol(other, Decl(dependentReturnType13.ts, 97, 79)) +>U : Symbol(U, Decl(dependentReturnType13.ts, 97, 40)) + + U extends number ? +>U : Symbol(U, Decl(dependentReturnType13.ts, 97, 40)) + + T extends string[] ? 2 : +>T : Symbol(T, Decl(dependentReturnType13.ts, 97, 12)) + + T extends string ? 1 : +>T : Symbol(T, Decl(dependentReturnType13.ts, 97, 12)) + + never : + U extends number[] ? +>U : Symbol(U, Decl(dependentReturnType13.ts, 97, 40)) + + T extends string[] ? 4 : +>T : Symbol(T, Decl(dependentReturnType13.ts, 97, 12)) + + T extends string ? 3 : +>T : Symbol(T, Decl(dependentReturnType13.ts, 97, 12)) + + never : + never { + if (Array.isArray(other)) { +>Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more) +>isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>other : Symbol(other, Decl(dependentReturnType13.ts, 97, 79)) + + if (Array.isArray(param)) { +>Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more) +>isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>param : Symbol(param, Decl(dependentReturnType13.ts, 97, 70)) + + return 4; + } + return 3; + } + if (Array.isArray(param)) { +>Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more) +>isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>param : Symbol(param, Decl(dependentReturnType13.ts, 97, 70)) + + return 2; + } + return 1; +} diff --git a/tsc/testdata/baselines/reference/compiler/dependentReturnType13.types b/tsc/testdata/baselines/reference/compiler/dependentReturnType13.types new file mode 100644 index 0000000000000..7911a09d64818 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/dependentReturnType13.types @@ -0,0 +1,261 @@ +//// [tests/cases/compiler/dependentReturnType13.ts] //// + +=== dependentReturnType13.ts === +// Restrictions on what kind of union types can be narrowed. + +function f1(param: T): +>f1 : (param: T) => T extends string ? 1 : T extends string[] ? 2 : never +>param : T + + T extends string ? 1 : + T extends string[] ? 2 : + never { + if (Array.isArray(param)) { +>Array.isArray(param) : boolean +>Array.isArray : (arg: any) => arg is any[] +>Array : ArrayConstructor +>isArray : (arg: any) => arg is any[] +>param : string | string[] + + return 2; +>2 : 2 + } + return 1; +>1 : 1 +} + +function f2(param: T): +>f2 : (param: T) => T extends string ? 1 : T extends string[] | number[] ? 2 : never +>param : T + + T extends string ? 1 : + T extends string[] | number[] ? 2 : + never { + if (Array.isArray(param)) { +>Array.isArray(param) : boolean +>Array.isArray : (arg: any) => arg is any[] +>Array : ArrayConstructor +>isArray : (arg: any) => arg is any[] +>param : string | string[] | number[] + + return 2; +>2 : 2 + } + return 1; +>1 : 1 +} + +declare function isNumberArray(x: unknown): x is number[]; +>isNumberArray : (x: unknown) => x is number[] +>x : unknown + +function f3(param: T): // Bad. +>f3 : (param: T) => T extends string ? 1 : T extends string[] ? 2 : T extends number[] ? 3 : never +>param : T + + T extends string ? 1 : + T extends string[] ? 2 : + T extends number[] ? 3 : + never { + if (isNumberArray(param)) { +>isNumberArray(param) : boolean +>isNumberArray : (x: unknown) => x is number[] +>param : string | string[] | number[] + + return 3; +>3 : 3 + } + if (Array.isArray(param)) { +>Array.isArray(param) : boolean +>Array.isArray : (arg: any) => arg is any[] +>Array : ArrayConstructor +>isArray : (arg: any) => arg is any[] +>param : string | string[] + + return 2; +>2 : 2 + } + return 1; +>1 : 1 +} + +class Dog { +>Dog : Dog + + bark(): void {} +>bark : () => void +} + +class Cat { +>Cat : Cat + + meow(): void {} +>meow : () => void + +} + +function f4(param: T): // Bad. +>f4 : (param: T) => T extends Cat ? 1 : T extends Dog ? 2 : never +>param : T + + T extends Cat ? 1 : + T extends Dog ? 2 : + never { + if ('bark' in param) { +>'bark' in param : boolean +>'bark' : "bark" +>param : T + + const _: Dog = param; +>_ : Dog +>param : Dog + + return 2; +>2 : 2 + } + const _: Cat = param; +>_ : Cat +>param : Cat + + return 1; +>1 : 1 +} + +function f5(param: T): +>f5 : (param: T) => T extends string ? 1 : T extends number | string[] ? 2 : never +>param : T + + T extends string ? 1 : + T extends number | string[] ? 2 : + never { + if (Array.isArray(param) || typeof param === "number") { +>Array.isArray(param) || typeof param === "number" : boolean +>Array.isArray(param) : boolean +>Array.isArray : (arg: any) => arg is any[] +>Array : ArrayConstructor +>isArray : (arg: any) => arg is any[] +>param : string | number | string[] +>typeof param === "number" : boolean +>typeof param : "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" +>param : T +>"number" : "number" + + const _: string[] | number = param; +>_ : number | string[] +>param : number | string[] + + return 2; +>2 : 2 + } + const _: string = param; +>_ : string +>param : string + + return 1; +>1 : 1 +} + +function f6(param: T, other: U): +>f6 : (param: T, other: U) => T extends number[] ? 2 : U extends true ? T extends string[] ? 3 : T extends string ? 1 : never : U extends false ? T extends string[] ? 4 : T extends string ? 5 : never : never +>param : T +>other : U + + T extends number[] ? 2 : + U extends true ? +>true : true + + T extends string[] ? 3 : + T extends string ? 1 : + never : + U extends false ? +>false : false + + T extends string[] ? 4 : + T extends string ? 5 : + never : + never { + if (isNumberArray(param)) { +>isNumberArray(param) : boolean +>isNumberArray : (x: unknown) => x is number[] +>param : string | string[] | number[] + + return 2; +>2 : 2 + } + if (other) { +>other : U + + if (Array.isArray(param)) { +>Array.isArray(param) : boolean +>Array.isArray : (arg: any) => arg is any[] +>Array : ArrayConstructor +>isArray : (arg: any) => arg is any[] +>param : string | string[] + + return 3; +>3 : 3 + } + return 1; +>1 : 1 + } + if (Array.isArray(param)) { +>Array.isArray(param) : boolean +>Array.isArray : (arg: any) => arg is any[] +>Array : ArrayConstructor +>isArray : (arg: any) => arg is any[] +>param : string | string[] + + return 4; +>4 : 4 + } + return 5; +>5 : 5 +} + +function f7(param: T, other: U): +>f7 : (param: T, other: U) => U extends number ? T extends string[] ? 2 : T extends string ? 1 : never : U extends number[] ? T extends string[] ? 4 : T extends string ? 3 : never : never +>param : T +>other : U + + U extends number ? + T extends string[] ? 2 : + T extends string ? 1 : + never : + U extends number[] ? + T extends string[] ? 4 : + T extends string ? 3 : + never : + never { + if (Array.isArray(other)) { +>Array.isArray(other) : boolean +>Array.isArray : (arg: any) => arg is any[] +>Array : ArrayConstructor +>isArray : (arg: any) => arg is any[] +>other : number | number[] + + if (Array.isArray(param)) { +>Array.isArray(param) : boolean +>Array.isArray : (arg: any) => arg is any[] +>Array : ArrayConstructor +>isArray : (arg: any) => arg is any[] +>param : string | string[] + + return 4; +>4 : 4 + } + return 3; +>3 : 3 + } + if (Array.isArray(param)) { +>Array.isArray(param) : boolean +>Array.isArray : (arg: any) => arg is any[] +>Array : ArrayConstructor +>isArray : (arg: any) => arg is any[] +>param : string | string[] + + return 2; +>2 : 2 + } + return 1; +>1 : 1 +} diff --git a/tsc/testdata/baselines/reference/compiler/dependentReturnType2.errors.txt b/tsc/testdata/baselines/reference/compiler/dependentReturnType2.errors.txt new file mode 100644 index 0000000000000..4eddac31221f3 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/dependentReturnType2.errors.txt @@ -0,0 +1,314 @@ +file.js(155,13): error TS2322: Type 'undefined' is not assignable to type 'HelperCond[]>'. +file.js(168,16): error TS2536: Type 'I' cannot be used to index type '{ [s: string]: any; }'. +file.js(185,9): error TS2322: Type 'Record' is not assignable to type 'HelperCond>'. + + +==== file.js (3 errors) ==== + // Adapted from ts-error-deltas repos + + /** + * @template T + * @template A + * @template R1 + * @template B + * @template R2 + * @typedef {T extends A ? R1 : T extends B ? R2 : never} HelperCond + */ + + /** + * @typedef IMessage + * @property {string} [html] + * @property {Object[]} [tokens] + */ + + class NewKatex { + /** + * @param {string} s + * @returns {string} + */ + render(s) { + return ""; + } + + /** + * @template {string | IMessage} T + * @param {T} message + * @returns {T extends string ? string : T extends IMessage ? IMessage : never} + */ + renderMessage(message) { + if (typeof message === 'string') { + return this.render(message); // Ok + } + + if (!message.html?.trim()) { + return message; // Ok + } + + if (!message.tokens) { + message.tokens = []; + } + + message.html = this.render(message.html); + return message; // Ok + } + } + + /** + * @template {true | false} T + * @param {{ dollarSyntax: boolean; parenthesisSyntax: boolean; }} options + * @param {T} _isMessage + * @returns {T extends true ? (message: IMessage) => IMessage : T extends false ? (message: string) => string : never} + */ + function createKatexMessageRendering(options, _isMessage) { + const instance = new NewKatex(); + if (_isMessage) { + return (/** @type {IMessage} */ message) => instance.renderMessage(message); // Ok + } + return (/** @type {string} */ message) => instance.renderMessage(message); // Ok + } + + // File: Rocket.Chat/apps/meteor/app/settings/lib/settings.ts + + /** + * @typedef {Record} MyObj + */ + + + /** + * @typedef {MyObj} SettingValue + */ + + /** + * @template {SettingValue} T + * @typedef {Object} SettingComposedValue + * @property {string} key + * @property {SettingValue} value + */ + + /** + * @callback SettingCallback + * @param {string} key + * @param {SettingValue} value + * @param {boolean} [initialLoad] + * @returns {void} + */ + + /** @type {{ settings: { [s: string]: any } }} */ + const Meteor = /** @type {any} */ (undefined); + /** @type {{ isRegExp(x: unknown): x is RegExp; }} */ + const _ = /** @type {any} */ (undefined); + + /** + * @param {RegExp} x + * @returns {void} + */ + function takesRegExp(x) { + return /** @type {any} */ undefined; + } + /** + * @param {string} x + * @returns {void} + */ + function takesString(x) { + return /** @type {any} */ undefined; + } + + /** + * @class NewSettingsBase + */ + class NewSettingsBase { + /** + * @template {SettingCallback | undefined} C + * @template {string | RegExp} I + * @template {SettingValue} T + * @param {I} _id + * @param {C} [callback] + * @returns {HelperCond[]>>} + */ + newGet(_id, callback) { + if (callback !== undefined) { + if (!Meteor.settings) { + return; // Ok + } + if (_id === '*') { + return Object.keys(Meteor.settings).forEach((key) => { + const value = Meteor.settings[key]; + callback(key, value); + }); + } + if (_.isRegExp(_id) && Meteor.settings) { + return Object.keys(Meteor.settings).forEach((key) => { + if (!_id.test(key)) { + return; + } + const value = Meteor.settings[key]; + callback(key, value); + }); + } + + if (typeof _id === 'string') { + const value = Meteor.settings[_id]; + if (value != null) { + callback(_id, Meteor.settings[_id]); + } + return; // Ok + } + + return; // Ok, needed for exhaustiveness check + } + + if (!Meteor.settings) { + return undefined; // Error + ~~~~~~ +!!! error TS2322: Type 'undefined' is not assignable to type 'HelperCond[]>'. + } + + if (_.isRegExp(_id)) { + return Object.keys(Meteor.settings).reduce((/** @type {SettingComposedValue[]} */ items, key) => { + const value = Meteor.settings[key]; + if (_id.test(key)) { + items.push({ key, value }); + } + return items; + }, []); // Ok + } + + return Meteor.settings?.[_id]; // Error + ~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2536: Type 'I' cannot be used to index type '{ [s: string]: any; }'. + } + } + + // File: Rocket.Chat/apps/meteor/app/ui-utils/client/lib/messageBox.ts + + /** + * @typedef {MyObj} MessageBoxAction + */ + + /** + * @template {string | undefined} T + * @param {T} group + * @returns {HelperCond>} + */ + function getWithBug(group) { + if (!group) { + return /** @type {Record} */({}); // Error + ~~~~~~ +!!! error TS2322: Type 'Record' is not assignable to type 'HelperCond>'. + } + return /** @type {MessageBoxAction[]} */([]); // Ok + } + + /** + * @template {string | undefined} T + * @param {T} group + * @returns {HelperCond>} + */ + function getWithoutBug(group) { + if (group === undefined) { + return /** @type {Record} */({}); // Ok + } + return /** @type {MessageBoxAction[]} */([]); // Ok + } + + // File: Rocket.Chat/apps/meteor/ee/server/lib/engagementDashboard/date.ts + + /** + * @param {string} x + * @returns {Date} + */ + function mapDateForAPI(x) { + return /** @type {any} */ (undefined); + } + + /** + * @template {string | undefined} T + * @param {string} start + * @param {T} [end] + * @returns {HelperCond} + */ + function transformDatesForAPI(start, end) { + return end !== undefined ? + { + start: mapDateForAPI(start), + end: mapDateForAPI(end), + } : + { + start: mapDateForAPI(start), + end: undefined + }; + } + + // File: Rocket.Chat/packages/agenda/src/Agenda.ts + + /** + * @typedef {MyObj} RepeatOptions + */ + + /** + * @typedef {MyObj} Job + */ + + /** + * @typedef {Object} IJob + * @property {MyObj} data + */ + class NewAgenda { + /** + * @param {string | number} interval + * @param {string} name + * @param {IJob['data']} data + * @param {RepeatOptions} options + * @returns {Promise} + */ + async _createIntervalJob(interval, name, data, options) { + return /** @type {any} */ (undefined); + } + + /** + * @param {string | number} interval + * @param {string[]} names + * @param {IJob['data']} data + * @param {RepeatOptions} options + * @returns {Promise | undefined} + */ + _createIntervalJobs(interval, names, data, options) { + return undefined; + } + + /** + * @template {string | string[]} T + * @param {string | number} interval + * @param {T} name + * @param {IJob['data']} data + * @param {RepeatOptions} options + * @returns {Promise>} + */ + async newEvery(interval, name, data, options) { + if (typeof name === 'string') { + return this._createIntervalJob(interval, name, data, options); // Ok + } + + if (Array.isArray(name)) { + return this._createIntervalJobs(interval, name, data, options); // Ok + } + + throw new Error('Unexpected error: Invalid job name(s)'); + } + } + + // File: angular/packages/common/src/pipes/case_conversion_pipes.ts + + /** + * @template {string | null | undefined} T + * @param {T} value + * @returns {HelperCond} + */ + function transform1(value) { + if (value == null) return null; // Ok + if (typeof value !== 'string') { + throw new Error(); + } + return value.toLowerCase(); // Ok + } + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/dependentReturnType2.symbols b/tsc/testdata/baselines/reference/compiler/dependentReturnType2.symbols new file mode 100644 index 0000000000000..4b41f3fe744db --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/dependentReturnType2.symbols @@ -0,0 +1,594 @@ +//// [tests/cases/compiler/dependentReturnType2.ts] //// + +=== file.js === +// Adapted from ts-error-deltas repos + +/** + * @template T + * @template A + * @template R1 + * @template B + * @template R2 + * @typedef {T extends A ? R1 : T extends B ? R2 : never} HelperCond + */ + +/** + * @typedef IMessage + * @property {string} [html] + * @property {Object[]} [tokens] + */ + +class NewKatex { +>NewKatex : Symbol(NewKatex, Decl(file.js, 0, 0)) + + /** + * @param {string} s + * @returns {string} + */ + render(s) { +>render : Symbol(NewKatex.render, Decl(file.js, 17, 16)) +>s : Symbol(s, Decl(file.js, 22, 11)) + + return ""; + } + + /** + * @template {string | IMessage} T + * @param {T} message + * @returns {T extends string ? string : T extends IMessage ? IMessage : never} + */ + renderMessage(message) { +>renderMessage : Symbol(NewKatex.renderMessage, Decl(file.js, 24, 5)) +>message : Symbol(message, Decl(file.js, 31, 18)) + + if (typeof message === 'string') { +>message : Symbol(message, Decl(file.js, 31, 18)) + + return this.render(message); // Ok +>this.render : Symbol(NewKatex.render, Decl(file.js, 17, 16)) +>this : Symbol(NewKatex, Decl(file.js, 0, 0)) +>render : Symbol(NewKatex.render, Decl(file.js, 17, 16)) +>message : Symbol(message, Decl(file.js, 31, 18)) + } + + if (!message.html?.trim()) { +>message.html?.trim : Symbol(String.trim, Decl(lib.es5.d.ts, --, --)) +>message.html : Symbol(html, Decl(file.js, 13, 3)) +>message : Symbol(message, Decl(file.js, 31, 18)) +>html : Symbol(html, Decl(file.js, 13, 3)) +>trim : Symbol(String.trim, Decl(lib.es5.d.ts, --, --)) + + return message; // Ok +>message : Symbol(message, Decl(file.js, 31, 18)) + } + + if (!message.tokens) { +>message.tokens : Symbol(tokens, Decl(file.js, 14, 3)) +>message : Symbol(message, Decl(file.js, 31, 18)) +>tokens : Symbol(tokens, Decl(file.js, 14, 3)) + + message.tokens = []; +>message.tokens : Symbol(tokens, Decl(file.js, 14, 3)) +>message : Symbol(message, Decl(file.js, 31, 18)) +>tokens : Symbol(tokens, Decl(file.js, 14, 3)) + } + + message.html = this.render(message.html); +>message.html : Symbol(html, Decl(file.js, 13, 3)) +>message : Symbol(message, Decl(file.js, 31, 18)) +>html : Symbol(html, Decl(file.js, 13, 3)) +>this.render : Symbol(NewKatex.render, Decl(file.js, 17, 16)) +>this : Symbol(NewKatex, Decl(file.js, 0, 0)) +>render : Symbol(NewKatex.render, Decl(file.js, 17, 16)) +>message.html : Symbol(html, Decl(file.js, 13, 3)) +>message : Symbol(message, Decl(file.js, 31, 18)) +>html : Symbol(html, Decl(file.js, 13, 3)) + + return message; // Ok +>message : Symbol(message, Decl(file.js, 31, 18)) + } +} + +/** + * @template {true | false} T + * @param {{ dollarSyntax: boolean; parenthesisSyntax: boolean; }} options + * @param {T} _isMessage + * @returns {T extends true ? (message: IMessage) => IMessage : T extends false ? (message: string) => string : never} + */ +function createKatexMessageRendering(options, _isMessage) { +>createKatexMessageRendering : Symbol(createKatexMessageRendering, Decl(file.js, 47, 1)) +>options : Symbol(options, Decl(file.js, 55, 37)) +>_isMessage : Symbol(_isMessage, Decl(file.js, 55, 45)) + + const instance = new NewKatex(); +>instance : Symbol(instance, Decl(file.js, 56, 9)) +>NewKatex : Symbol(NewKatex, Decl(file.js, 0, 0)) + + if (_isMessage) { +>_isMessage : Symbol(_isMessage, Decl(file.js, 55, 45)) + + return (/** @type {IMessage} */ message) => instance.renderMessage(message); // Ok +>message : Symbol(message, Decl(file.js, 58, 16)) +>instance.renderMessage : Symbol(NewKatex.renderMessage, Decl(file.js, 24, 5)) +>instance : Symbol(instance, Decl(file.js, 56, 9)) +>renderMessage : Symbol(NewKatex.renderMessage, Decl(file.js, 24, 5)) +>message : Symbol(message, Decl(file.js, 58, 16)) + } + return (/** @type {string} */ message) => instance.renderMessage(message); // Ok +>message : Symbol(message, Decl(file.js, 60, 12)) +>instance.renderMessage : Symbol(NewKatex.renderMessage, Decl(file.js, 24, 5)) +>instance : Symbol(instance, Decl(file.js, 56, 9)) +>renderMessage : Symbol(NewKatex.renderMessage, Decl(file.js, 24, 5)) +>message : Symbol(message, Decl(file.js, 60, 12)) +} + +// File: Rocket.Chat/apps/meteor/app/settings/lib/settings.ts + +/** + * @typedef {Record} MyObj + */ + + +/** + * @typedef {MyObj} SettingValue + */ + +/** + * @template {SettingValue} T + * @typedef {Object} SettingComposedValue + * @property {string} key + * @property {SettingValue} value + */ + +/** + * @callback SettingCallback + * @param {string} key + * @param {SettingValue} value + * @param {boolean} [initialLoad] + * @returns {void} + */ + +/** @type {{ settings: { [s: string]: any } }} */ +const Meteor = /** @type {any} */ (undefined); +>Meteor : Symbol(Meteor, Decl(file.js, 90, 5)) +>undefined : Symbol(undefined) + +/** @type {{ isRegExp(x: unknown): x is RegExp; }} */ +const _ = /** @type {any} */ (undefined); +>_ : Symbol(_, Decl(file.js, 92, 5)) +>undefined : Symbol(undefined) + +/** + * @param {RegExp} x + * @returns {void} + */ +function takesRegExp(x) { +>takesRegExp : Symbol(takesRegExp, Decl(file.js, 92, 41)) +>x : Symbol(x, Decl(file.js, 98, 21)) + + return /** @type {any} */ undefined; +>undefined : Symbol(undefined) +} +/** + * @param {string} x + * @returns {void} + */ +function takesString(x) { +>takesString : Symbol(takesString, Decl(file.js, 100, 1)) +>x : Symbol(x, Decl(file.js, 105, 21)) + + return /** @type {any} */ undefined; +>undefined : Symbol(undefined) +} + +/** + * @class NewSettingsBase + */ +class NewSettingsBase { +>NewSettingsBase : Symbol(NewSettingsBase, Decl(file.js, 107, 1)) + + /** + * @template {SettingCallback | undefined} C + * @template {string | RegExp} I + * @template {SettingValue} T + * @param {I} _id + * @param {C} [callback] + * @returns {HelperCond[]>>} + */ + newGet(_id, callback) { +>newGet : Symbol(NewSettingsBase.newGet, Decl(file.js, 112, 23)) +>_id : Symbol(_id, Decl(file.js, 121, 11)) +>callback : Symbol(callback, Decl(file.js, 121, 15)) + + if (callback !== undefined) { +>callback : Symbol(callback, Decl(file.js, 121, 15)) +>undefined : Symbol(undefined) + + if (!Meteor.settings) { +>Meteor.settings : Symbol(settings, Decl(file.js, 89, 12)) +>Meteor : Symbol(Meteor, Decl(file.js, 90, 5)) +>settings : Symbol(settings, Decl(file.js, 89, 12)) + + return; // Ok + } + if (_id === '*') { +>_id : Symbol(_id, Decl(file.js, 121, 11)) + + return Object.keys(Meteor.settings).forEach((key) => { +>Object.keys(Meteor.settings).forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --)) +>Object.keys : Symbol(ObjectConstructor.keys, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>keys : Symbol(ObjectConstructor.keys, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --)) +>Meteor.settings : Symbol(settings, Decl(file.js, 89, 12)) +>Meteor : Symbol(Meteor, Decl(file.js, 90, 5)) +>settings : Symbol(settings, Decl(file.js, 89, 12)) +>forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --)) +>key : Symbol(key, Decl(file.js, 127, 61)) + + const value = Meteor.settings[key]; +>value : Symbol(value, Decl(file.js, 128, 25)) +>Meteor.settings : Symbol(settings, Decl(file.js, 89, 12)) +>Meteor : Symbol(Meteor, Decl(file.js, 90, 5)) +>settings : Symbol(settings, Decl(file.js, 89, 12)) +>key : Symbol(key, Decl(file.js, 127, 61)) + + callback(key, value); +>callback : Symbol(callback, Decl(file.js, 121, 15)) +>key : Symbol(key, Decl(file.js, 127, 61)) +>value : Symbol(value, Decl(file.js, 128, 25)) + + }); + } + if (_.isRegExp(_id) && Meteor.settings) { +>_.isRegExp : Symbol(isRegExp, Decl(file.js, 91, 12)) +>_ : Symbol(_, Decl(file.js, 92, 5)) +>isRegExp : Symbol(isRegExp, Decl(file.js, 91, 12)) +>_id : Symbol(_id, Decl(file.js, 121, 11)) +>Meteor.settings : Symbol(settings, Decl(file.js, 89, 12)) +>Meteor : Symbol(Meteor, Decl(file.js, 90, 5)) +>settings : Symbol(settings, Decl(file.js, 89, 12)) + + return Object.keys(Meteor.settings).forEach((key) => { +>Object.keys(Meteor.settings).forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --)) +>Object.keys : Symbol(ObjectConstructor.keys, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>keys : Symbol(ObjectConstructor.keys, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --)) +>Meteor.settings : Symbol(settings, Decl(file.js, 89, 12)) +>Meteor : Symbol(Meteor, Decl(file.js, 90, 5)) +>settings : Symbol(settings, Decl(file.js, 89, 12)) +>forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --)) +>key : Symbol(key, Decl(file.js, 133, 61)) + + if (!_id.test(key)) { +>_id.test : Symbol(RegExp.test, Decl(lib.es5.d.ts, --, --)) +>_id : Symbol(_id, Decl(file.js, 121, 11)) +>test : Symbol(RegExp.test, Decl(lib.es5.d.ts, --, --)) +>key : Symbol(key, Decl(file.js, 133, 61)) + + return; + } + const value = Meteor.settings[key]; +>value : Symbol(value, Decl(file.js, 137, 25)) +>Meteor.settings : Symbol(settings, Decl(file.js, 89, 12)) +>Meteor : Symbol(Meteor, Decl(file.js, 90, 5)) +>settings : Symbol(settings, Decl(file.js, 89, 12)) +>key : Symbol(key, Decl(file.js, 133, 61)) + + callback(key, value); +>callback : Symbol(callback, Decl(file.js, 121, 15)) +>key : Symbol(key, Decl(file.js, 133, 61)) +>value : Symbol(value, Decl(file.js, 137, 25)) + + }); + } + + if (typeof _id === 'string') { +>_id : Symbol(_id, Decl(file.js, 121, 11)) + + const value = Meteor.settings[_id]; +>value : Symbol(value, Decl(file.js, 143, 21)) +>Meteor.settings : Symbol(settings, Decl(file.js, 89, 12)) +>Meteor : Symbol(Meteor, Decl(file.js, 90, 5)) +>settings : Symbol(settings, Decl(file.js, 89, 12)) +>_id : Symbol(_id, Decl(file.js, 121, 11)) + + if (value != null) { +>value : Symbol(value, Decl(file.js, 143, 21)) + + callback(_id, Meteor.settings[_id]); +>callback : Symbol(callback, Decl(file.js, 121, 15)) +>_id : Symbol(_id, Decl(file.js, 121, 11)) +>Meteor.settings : Symbol(settings, Decl(file.js, 89, 12)) +>Meteor : Symbol(Meteor, Decl(file.js, 90, 5)) +>settings : Symbol(settings, Decl(file.js, 89, 12)) +>_id : Symbol(_id, Decl(file.js, 121, 11)) + } + return; // Ok + } + + return; // Ok, needed for exhaustiveness check + } + + if (!Meteor.settings) { +>Meteor.settings : Symbol(settings, Decl(file.js, 89, 12)) +>Meteor : Symbol(Meteor, Decl(file.js, 90, 5)) +>settings : Symbol(settings, Decl(file.js, 89, 12)) + + return undefined; // Error +>undefined : Symbol(undefined) + } + + if (_.isRegExp(_id)) { +>_.isRegExp : Symbol(isRegExp, Decl(file.js, 91, 12)) +>_ : Symbol(_, Decl(file.js, 92, 5)) +>isRegExp : Symbol(isRegExp, Decl(file.js, 91, 12)) +>_id : Symbol(_id, Decl(file.js, 121, 11)) + + return Object.keys(Meteor.settings).reduce((/** @type {SettingComposedValue[]} */ items, key) => { +>Object.keys(Meteor.settings).reduce : Symbol(Array.reduce, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>Object.keys : Symbol(ObjectConstructor.keys, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>keys : Symbol(ObjectConstructor.keys, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --)) +>Meteor.settings : Symbol(settings, Decl(file.js, 89, 12)) +>Meteor : Symbol(Meteor, Decl(file.js, 90, 5)) +>settings : Symbol(settings, Decl(file.js, 89, 12)) +>reduce : Symbol(Array.reduce, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>items : Symbol(items, Decl(file.js, 158, 56)) +>key : Symbol(key, Decl(file.js, 158, 103)) + + const value = Meteor.settings[key]; +>value : Symbol(value, Decl(file.js, 159, 21)) +>Meteor.settings : Symbol(settings, Decl(file.js, 89, 12)) +>Meteor : Symbol(Meteor, Decl(file.js, 90, 5)) +>settings : Symbol(settings, Decl(file.js, 89, 12)) +>key : Symbol(key, Decl(file.js, 158, 103)) + + if (_id.test(key)) { +>_id.test : Symbol(RegExp.test, Decl(lib.es5.d.ts, --, --)) +>_id : Symbol(_id, Decl(file.js, 121, 11)) +>test : Symbol(RegExp.test, Decl(lib.es5.d.ts, --, --)) +>key : Symbol(key, Decl(file.js, 158, 103)) + + items.push({ key, value }); +>items.push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --)) +>items : Symbol(items, Decl(file.js, 158, 56)) +>push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --)) +>key : Symbol(key, Decl(file.js, 161, 32)) +>value : Symbol(value, Decl(file.js, 161, 37)) + } + return items; +>items : Symbol(items, Decl(file.js, 158, 56)) + + }, []); // Ok + } + + return Meteor.settings?.[_id]; // Error +>Meteor.settings : Symbol(settings, Decl(file.js, 89, 12)) +>Meteor : Symbol(Meteor, Decl(file.js, 90, 5)) +>settings : Symbol(settings, Decl(file.js, 89, 12)) +>_id : Symbol(_id, Decl(file.js, 121, 11)) + } +} + +// File: Rocket.Chat/apps/meteor/app/ui-utils/client/lib/messageBox.ts + +/** + * @typedef {MyObj} MessageBoxAction + */ + +/** + * @template {string | undefined} T + * @param {T} group + * @returns {HelperCond>} + */ +function getWithBug(group) { +>getWithBug : Symbol(getWithBug, Decl(file.js, 169, 1)) +>group : Symbol(group, Decl(file.js, 182, 20)) + + if (!group) { +>group : Symbol(group, Decl(file.js, 182, 20)) + + return /** @type {Record} */({}); // Error + } + return /** @type {MessageBoxAction[]} */([]); // Ok +} + +/** + * @template {string | undefined} T + * @param {T} group + * @returns {HelperCond>} + */ +function getWithoutBug(group) { +>getWithoutBug : Symbol(getWithoutBug, Decl(file.js, 187, 1)) +>group : Symbol(group, Decl(file.js, 194, 23)) + + if (group === undefined) { +>group : Symbol(group, Decl(file.js, 194, 23)) +>undefined : Symbol(undefined) + + return /** @type {Record} */({}); // Ok + } + return /** @type {MessageBoxAction[]} */([]); // Ok +} + +// File: Rocket.Chat/apps/meteor/ee/server/lib/engagementDashboard/date.ts + +/** + * @param {string} x + * @returns {Date} + */ +function mapDateForAPI(x) { +>mapDateForAPI : Symbol(mapDateForAPI, Decl(file.js, 199, 1)) +>x : Symbol(x, Decl(file.js, 207, 23)) + + return /** @type {any} */ (undefined); +>undefined : Symbol(undefined) +} + +/** + * @template {string | undefined} T + * @param {string} start + * @param {T} [end] + * @returns {HelperCond} + */ +function transformDatesForAPI(start, end) { +>transformDatesForAPI : Symbol(transformDatesForAPI, Decl(file.js, 209, 1)) +>start : Symbol(start, Decl(file.js, 217, 30)) +>end : Symbol(end, Decl(file.js, 217, 36)) + + return end !== undefined ? +>end : Symbol(end, Decl(file.js, 217, 36)) +>undefined : Symbol(undefined) + { + start: mapDateForAPI(start), +>start : Symbol(start, Decl(file.js, 219, 9)) +>mapDateForAPI : Symbol(mapDateForAPI, Decl(file.js, 199, 1)) +>start : Symbol(start, Decl(file.js, 217, 30)) + + end: mapDateForAPI(end), +>end : Symbol(end, Decl(file.js, 220, 40)) +>mapDateForAPI : Symbol(mapDateForAPI, Decl(file.js, 199, 1)) +>end : Symbol(end, Decl(file.js, 217, 36)) + + } : + { + start: mapDateForAPI(start), +>start : Symbol(start, Decl(file.js, 223, 9)) +>mapDateForAPI : Symbol(mapDateForAPI, Decl(file.js, 199, 1)) +>start : Symbol(start, Decl(file.js, 217, 30)) + + end: undefined +>end : Symbol(end, Decl(file.js, 224, 40)) +>undefined : Symbol(undefined) + + }; +} + +// File: Rocket.Chat/packages/agenda/src/Agenda.ts + +/** + * @typedef {MyObj} RepeatOptions + */ + +/** + * @typedef {MyObj} Job + */ + +/** + * @typedef {Object} IJob + * @property {MyObj} data + */ +class NewAgenda { +>NewAgenda : Symbol(NewAgenda, Decl(file.js, 227, 1)) + + /** + * @param {string | number} interval + * @param {string} name + * @param {IJob['data']} data + * @param {RepeatOptions} options + * @returns {Promise} + */ + async _createIntervalJob(interval, name, data, options) { +>_createIntervalJob : Symbol(NewAgenda._createIntervalJob, Decl(file.js, 243, 17)) +>interval : Symbol(interval, Decl(file.js, 251, 29)) +>name : Symbol(name, Decl(file.js, 251, 38)) +>data : Symbol(data, Decl(file.js, 251, 44)) +>options : Symbol(options, Decl(file.js, 251, 50)) + + return /** @type {any} */ (undefined); +>undefined : Symbol(undefined) + } + + /** + * @param {string | number} interval + * @param {string[]} names + * @param {IJob['data']} data + * @param {RepeatOptions} options + * @returns {Promise | undefined} + */ + _createIntervalJobs(interval, names, data, options) { +>_createIntervalJobs : Symbol(NewAgenda._createIntervalJobs, Decl(file.js, 253, 5)) +>interval : Symbol(interval, Decl(file.js, 262, 24)) +>names : Symbol(names, Decl(file.js, 262, 33)) +>data : Symbol(data, Decl(file.js, 262, 40)) +>options : Symbol(options, Decl(file.js, 262, 46)) + + return undefined; +>undefined : Symbol(undefined) + } + + /** + * @template {string | string[]} T + * @param {string | number} interval + * @param {T} name + * @param {IJob['data']} data + * @param {RepeatOptions} options + * @returns {Promise>} + */ + async newEvery(interval, name, data, options) { +>newEvery : Symbol(NewAgenda.newEvery, Decl(file.js, 264, 5)) +>interval : Symbol(interval, Decl(file.js, 274, 19)) +>name : Symbol(name, Decl(file.js, 274, 28)) +>data : Symbol(data, Decl(file.js, 274, 34)) +>options : Symbol(options, Decl(file.js, 274, 40)) + + if (typeof name === 'string') { +>name : Symbol(name, Decl(file.js, 274, 28)) + + return this._createIntervalJob(interval, name, data, options); // Ok +>this._createIntervalJob : Symbol(NewAgenda._createIntervalJob, Decl(file.js, 243, 17)) +>this : Symbol(NewAgenda, Decl(file.js, 227, 1)) +>_createIntervalJob : Symbol(NewAgenda._createIntervalJob, Decl(file.js, 243, 17)) +>interval : Symbol(interval, Decl(file.js, 274, 19)) +>name : Symbol(name, Decl(file.js, 274, 28)) +>data : Symbol(data, Decl(file.js, 274, 34)) +>options : Symbol(options, Decl(file.js, 274, 40)) + } + + if (Array.isArray(name)) { +>Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more) +>isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>name : Symbol(name, Decl(file.js, 274, 28)) + + return this._createIntervalJobs(interval, name, data, options); // Ok +>this._createIntervalJobs : Symbol(NewAgenda._createIntervalJobs, Decl(file.js, 253, 5)) +>this : Symbol(NewAgenda, Decl(file.js, 227, 1)) +>_createIntervalJobs : Symbol(NewAgenda._createIntervalJobs, Decl(file.js, 253, 5)) +>interval : Symbol(interval, Decl(file.js, 274, 19)) +>name : Symbol(name, Decl(file.js, 274, 28)) +>data : Symbol(data, Decl(file.js, 274, 34)) +>options : Symbol(options, Decl(file.js, 274, 40)) + } + + throw new Error('Unexpected error: Invalid job name(s)'); +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2022.error.d.ts, --, --)) + } +} + +// File: angular/packages/common/src/pipes/case_conversion_pipes.ts + +/** + * @template {string | null | undefined} T + * @param {T} value + * @returns {HelperCond} + */ +function transform1(value) { +>transform1 : Symbol(transform1, Decl(file.js, 285, 1)) +>value : Symbol(value, Decl(file.js, 294, 20)) + + if (value == null) return null; // Ok +>value : Symbol(value, Decl(file.js, 294, 20)) + + if (typeof value !== 'string') { +>value : Symbol(value, Decl(file.js, 294, 20)) + + throw new Error(); +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2022.error.d.ts, --, --)) + } + return value.toLowerCase(); // Ok +>value.toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --)) +>value : Symbol(value, Decl(file.js, 294, 20)) +>toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --)) +} + diff --git a/tsc/testdata/baselines/reference/compiler/dependentReturnType2.types b/tsc/testdata/baselines/reference/compiler/dependentReturnType2.types new file mode 100644 index 0000000000000..05962b47111ff --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/dependentReturnType2.types @@ -0,0 +1,684 @@ +//// [tests/cases/compiler/dependentReturnType2.ts] //// + +=== file.js === +// Adapted from ts-error-deltas repos + +/** + * @template T + * @template A + * @template R1 + * @template B + * @template R2 + * @typedef {T extends A ? R1 : T extends B ? R2 : never} HelperCond + */ + +/** + * @typedef IMessage + * @property {string} [html] + * @property {Object[]} [tokens] + */ + +class NewKatex { +>NewKatex : NewKatex + + /** + * @param {string} s + * @returns {string} + */ + render(s) { +>render : (s: string) => string +>s : string + + return ""; +>"" : "" + } + + /** + * @template {string | IMessage} T + * @param {T} message + * @returns {T extends string ? string : T extends IMessage ? IMessage : never} + */ + renderMessage(message) { +>renderMessage : (message: T) => T extends string ? string : T extends IMessage ? IMessage : never +>message : T + + if (typeof message === 'string') { +>typeof message === 'string' : boolean +>typeof message : "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" +>message : T +>'string' : "string" + + return this.render(message); // Ok +>this.render(message) : string +>this.render : (s: string) => string +>this : this +>render : (s: string) => string +>message : string + } + + if (!message.html?.trim()) { +>!message.html?.trim() : boolean +>message.html?.trim() : string | undefined +>message.html?.trim : (() => string) | undefined +>message.html : string | undefined +>message : IMessage +>html : string | undefined +>trim : (() => string) | undefined + + return message; // Ok +>message : IMessage + } + + if (!message.tokens) { +>!message.tokens : boolean +>message.tokens : Object[] | undefined +>message : IMessage +>tokens : Object[] | undefined + + message.tokens = []; +>message.tokens = [] : never[] +>message.tokens : Object[] | undefined +>message : IMessage +>tokens : Object[] | undefined +>[] : never[] + } + + message.html = this.render(message.html); +>message.html = this.render(message.html) : string +>message.html : string | undefined +>message : IMessage +>html : string | undefined +>this.render(message.html) : string +>this.render : (s: string) => string +>this : this +>render : (s: string) => string +>message.html : string +>message : IMessage +>html : string + + return message; // Ok +>message : IMessage + } +} + +/** + * @template {true | false} T + * @param {{ dollarSyntax: boolean; parenthesisSyntax: boolean; }} options + * @param {T} _isMessage + * @returns {T extends true ? (message: IMessage) => IMessage : T extends false ? (message: string) => string : never} + */ +function createKatexMessageRendering(options, _isMessage) { +>createKatexMessageRendering : (options: { dollarSyntax: boolean; parenthesisSyntax: boolean; }, _isMessage: T) => T extends true ? (message: IMessage) => IMessage : T extends false ? (message: string) => string : never +>options : { dollarSyntax: boolean; parenthesisSyntax: boolean; } +>_isMessage : T + + const instance = new NewKatex(); +>instance : NewKatex +>new NewKatex() : NewKatex +>NewKatex : typeof NewKatex + + if (_isMessage) { +>_isMessage : T + + return (/** @type {IMessage} */ message) => instance.renderMessage(message); // Ok +>(/** @type {IMessage} */ message) => instance.renderMessage(message) : (message: IMessage) => IMessage +>message : IMessage +>instance.renderMessage(message) : IMessage +>instance.renderMessage : (message: T_1) => T_1 extends string ? string : T_1 extends IMessage ? IMessage : never +>instance : NewKatex +>renderMessage : (message: T_1) => T_1 extends string ? string : T_1 extends IMessage ? IMessage : never +>message : IMessage + } + return (/** @type {string} */ message) => instance.renderMessage(message); // Ok +>(/** @type {string} */ message) => instance.renderMessage(message) : (message: string) => string +>message : string +>instance.renderMessage(message) : string +>instance.renderMessage : (message: T_1) => T_1 extends string ? string : T_1 extends IMessage ? IMessage : never +>instance : NewKatex +>renderMessage : (message: T_1) => T_1 extends string ? string : T_1 extends IMessage ? IMessage : never +>message : string +} + +// File: Rocket.Chat/apps/meteor/app/settings/lib/settings.ts + +/** + * @typedef {Record} MyObj + */ + + +/** + * @typedef {MyObj} SettingValue + */ + +/** + * @template {SettingValue} T + * @typedef {Object} SettingComposedValue + * @property {string} key + * @property {SettingValue} value + */ + +/** + * @callback SettingCallback + * @param {string} key + * @param {SettingValue} value + * @param {boolean} [initialLoad] + * @returns {void} + */ + +/** @type {{ settings: { [s: string]: any } }} */ +const Meteor = /** @type {any} */ (undefined); +>Meteor : { settings: { [s: string]: any; }; } +>(undefined) : any +>undefined : undefined + +/** @type {{ isRegExp(x: unknown): x is RegExp; }} */ +const _ = /** @type {any} */ (undefined); +>_ : { isRegExp(x: unknown): x is RegExp; } +>(undefined) : any +>undefined : undefined + +/** + * @param {RegExp} x + * @returns {void} + */ +function takesRegExp(x) { +>takesRegExp : (x: RegExp) => void +>x : RegExp + + return /** @type {any} */ undefined; +>undefined : undefined +} +/** + * @param {string} x + * @returns {void} + */ +function takesString(x) { +>takesString : (x: string) => void +>x : string + + return /** @type {any} */ undefined; +>undefined : undefined +} + +/** + * @class NewSettingsBase + */ +class NewSettingsBase { +>NewSettingsBase : NewSettingsBase + + /** + * @template {SettingCallback | undefined} C + * @template {string | RegExp} I + * @template {SettingValue} T + * @param {I} _id + * @param {C} [callback] + * @returns {HelperCond[]>>} + */ + newGet(_id, callback) { +>newGet : (_id: I, callback?: C) => HelperCond[]>> +>_id : I +>callback : C | undefined + + if (callback !== undefined) { +>callback !== undefined : boolean +>callback : C | undefined +>undefined : undefined + + if (!Meteor.settings) { +>!Meteor.settings : false +>Meteor.settings : { [s: string]: any; } +>Meteor : { settings: { [s: string]: any; }; } +>settings : { [s: string]: any; } + + return; // Ok + } + if (_id === '*') { +>_id === '*' : boolean +>_id : I +>'*' : "*" + + return Object.keys(Meteor.settings).forEach((key) => { +>Object.keys(Meteor.settings).forEach((key) => { const value = Meteor.settings[key]; callback(key, value); }) : void +>Object.keys(Meteor.settings).forEach : (callbackfn: (value: string, index: number, array: string[]) => void, thisArg?: any) => void +>Object.keys(Meteor.settings) : string[] +>Object.keys : { (o: object): string[]; (o: {}): string[]; } +>Object : ObjectConstructor +>keys : { (o: object): string[]; (o: {}): string[]; } +>Meteor.settings : { [s: string]: any; } +>Meteor : { settings: { [s: string]: any; }; } +>settings : { [s: string]: any; } +>forEach : (callbackfn: (value: string, index: number, array: string[]) => void, thisArg?: any) => void +>(key) => { const value = Meteor.settings[key]; callback(key, value); } : (key: string) => void +>key : string + + const value = Meteor.settings[key]; +>value : any +>Meteor.settings[key] : any +>Meteor.settings : { [s: string]: any; } +>Meteor : { settings: { [s: string]: any; }; } +>settings : { [s: string]: any; } +>key : string + + callback(key, value); +>callback(key, value) : void +>callback : SettingCallback +>key : string +>value : any + + }); + } + if (_.isRegExp(_id) && Meteor.settings) { +>_.isRegExp(_id) && Meteor.settings : false | { [s: string]: any; } +>_.isRegExp(_id) : boolean +>_.isRegExp : (x: unknown) => x is RegExp +>_ : { isRegExp(x: unknown): x is RegExp; } +>isRegExp : (x: unknown) => x is RegExp +>_id : string | RegExp +>Meteor.settings : { [s: string]: any; } +>Meteor : { settings: { [s: string]: any; }; } +>settings : { [s: string]: any; } + + return Object.keys(Meteor.settings).forEach((key) => { +>Object.keys(Meteor.settings).forEach((key) => { if (!_id.test(key)) { return; } const value = Meteor.settings[key]; callback(key, value); }) : void +>Object.keys(Meteor.settings).forEach : (callbackfn: (value: string, index: number, array: string[]) => void, thisArg?: any) => void +>Object.keys(Meteor.settings) : string[] +>Object.keys : { (o: object): string[]; (o: {}): string[]; } +>Object : ObjectConstructor +>keys : { (o: object): string[]; (o: {}): string[]; } +>Meteor.settings : { [s: string]: any; } +>Meteor : { settings: { [s: string]: any; }; } +>settings : { [s: string]: any; } +>forEach : (callbackfn: (value: string, index: number, array: string[]) => void, thisArg?: any) => void +>(key) => { if (!_id.test(key)) { return; } const value = Meteor.settings[key]; callback(key, value); } : (key: string) => void +>key : string + + if (!_id.test(key)) { +>!_id.test(key) : boolean +>_id.test(key) : boolean +>_id.test : (string: string) => boolean +>_id : RegExp +>test : (string: string) => boolean +>key : string + + return; + } + const value = Meteor.settings[key]; +>value : any +>Meteor.settings[key] : any +>Meteor.settings : { [s: string]: any; } +>Meteor : { settings: { [s: string]: any; }; } +>settings : { [s: string]: any; } +>key : string + + callback(key, value); +>callback(key, value) : void +>callback : SettingCallback +>key : string +>value : any + + }); + } + + if (typeof _id === 'string') { +>typeof _id === 'string' : boolean +>typeof _id : "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" +>_id : I +>'string' : "string" + + const value = Meteor.settings[_id]; +>value : any +>Meteor.settings[_id] : any +>Meteor.settings : { [s: string]: any; } +>Meteor : { settings: { [s: string]: any; }; } +>settings : { [s: string]: any; } +>_id : I & string + + if (value != null) { +>value != null : boolean +>value : any + + callback(_id, Meteor.settings[_id]); +>callback(_id, Meteor.settings[_id]) : void +>callback : SettingCallback +>_id : string +>Meteor.settings[_id] : any +>Meteor.settings : { [s: string]: any; } +>Meteor : { settings: { [s: string]: any; }; } +>settings : { [s: string]: any; } +>_id : I & string + } + return; // Ok + } + + return; // Ok, needed for exhaustiveness check + } + + if (!Meteor.settings) { +>!Meteor.settings : false +>Meteor.settings : { [s: string]: any; } +>Meteor : { settings: { [s: string]: any; }; } +>settings : { [s: string]: any; } + + return undefined; // Error +>undefined : undefined + } + + if (_.isRegExp(_id)) { +>_.isRegExp(_id) : boolean +>_.isRegExp : (x: unknown) => x is RegExp +>_ : { isRegExp(x: unknown): x is RegExp; } +>isRegExp : (x: unknown) => x is RegExp +>_id : string | RegExp + + return Object.keys(Meteor.settings).reduce((/** @type {SettingComposedValue[]} */ items, key) => { +>Object.keys(Meteor.settings).reduce((/** @type {SettingComposedValue[]} */ items, key) => { const value = Meteor.settings[key]; if (_id.test(key)) { items.push({ key, value }); } return items; }, []) : SettingComposedValue[] +>Object.keys(Meteor.settings).reduce : { (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string; (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string; (callbackfn: (previousValue: U, currentValue: string, currentIndex: number, array: string[]) => U, initialValue: U): U; } +>Object.keys(Meteor.settings) : string[] +>Object.keys : { (o: object): string[]; (o: {}): string[]; } +>Object : ObjectConstructor +>keys : { (o: object): string[]; (o: {}): string[]; } +>Meteor.settings : { [s: string]: any; } +>Meteor : { settings: { [s: string]: any; }; } +>settings : { [s: string]: any; } +>reduce : { (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string; (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string; (callbackfn: (previousValue: U, currentValue: string, currentIndex: number, array: string[]) => U, initialValue: U): U; } +>(/** @type {SettingComposedValue[]} */ items, key) => { const value = Meteor.settings[key]; if (_id.test(key)) { items.push({ key, value }); } return items; } : (items: SettingComposedValue[], key: string) => SettingComposedValue[] +>items : SettingComposedValue[] +>key : string + + const value = Meteor.settings[key]; +>value : any +>Meteor.settings[key] : any +>Meteor.settings : { [s: string]: any; } +>Meteor : { settings: { [s: string]: any; }; } +>settings : { [s: string]: any; } +>key : string + + if (_id.test(key)) { +>_id.test(key) : boolean +>_id.test : (string: string) => boolean +>_id : RegExp +>test : (string: string) => boolean +>key : string + + items.push({ key, value }); +>items.push({ key, value }) : number +>items.push : (...items: SettingComposedValue[]) => number +>items : SettingComposedValue[] +>push : (...items: SettingComposedValue[]) => number +>{ key, value } : { key: string; value: any; } +>key : string +>value : any + } + return items; +>items : SettingComposedValue[] + + }, []); // Ok +>[] : never[] + } + + return Meteor.settings?.[_id]; // Error +>Meteor.settings?.[_id] : any +>Meteor.settings : { [s: string]: any; } +>Meteor : { settings: { [s: string]: any; }; } +>settings : { [s: string]: any; } +>_id : I + } +} + +// File: Rocket.Chat/apps/meteor/app/ui-utils/client/lib/messageBox.ts + +/** + * @typedef {MyObj} MessageBoxAction + */ + +/** + * @template {string | undefined} T + * @param {T} group + * @returns {HelperCond>} + */ +function getWithBug(group) { +>getWithBug : (group: T) => HelperCond> +>group : T + + if (!group) { +>!group : boolean +>group : T + + return /** @type {Record} */({}); // Error +>({}) : Record +>{} : {} + } + return /** @type {MessageBoxAction[]} */([]); // Ok +>([]) : MyObj[] +>[] : never[] +} + +/** + * @template {string | undefined} T + * @param {T} group + * @returns {HelperCond>} + */ +function getWithoutBug(group) { +>getWithoutBug : (group: T) => HelperCond> +>group : T + + if (group === undefined) { +>group === undefined : boolean +>group : T +>undefined : undefined + + return /** @type {Record} */({}); // Ok +>({}) : Record +>{} : {} + } + return /** @type {MessageBoxAction[]} */([]); // Ok +>([]) : MyObj[] +>[] : never[] +} + +// File: Rocket.Chat/apps/meteor/ee/server/lib/engagementDashboard/date.ts + +/** + * @param {string} x + * @returns {Date} + */ +function mapDateForAPI(x) { +>mapDateForAPI : (x: string) => Date +>x : string + + return /** @type {any} */ (undefined); +>(undefined) : any +>undefined : undefined +} + +/** + * @template {string | undefined} T + * @param {string} start + * @param {T} [end] + * @returns {HelperCond} + */ +function transformDatesForAPI(start, end) { +>transformDatesForAPI : (start: string, end?: T) => HelperCond +>start : string +>end : T | undefined + + return end !== undefined ? +>end !== undefined ? { start: mapDateForAPI(start), end: mapDateForAPI(end), } : { start: mapDateForAPI(start), end: undefined } : { start: Date; end: Date; } | { start: Date; end: undefined; } +>end !== undefined : boolean +>end : T | undefined +>undefined : undefined + { +>{ start: mapDateForAPI(start), end: mapDateForAPI(end), } : { start: Date; end: Date; } + + start: mapDateForAPI(start), +>start : Date +>mapDateForAPI(start) : Date +>mapDateForAPI : (x: string) => Date +>start : string + + end: mapDateForAPI(end), +>end : Date +>mapDateForAPI(end) : Date +>mapDateForAPI : (x: string) => Date +>end : string + + } : + { +>{ start: mapDateForAPI(start), end: undefined } : { start: Date; end: undefined; } + + start: mapDateForAPI(start), +>start : Date +>mapDateForAPI(start) : Date +>mapDateForAPI : (x: string) => Date +>start : string + + end: undefined +>end : undefined +>undefined : undefined + + }; +} + +// File: Rocket.Chat/packages/agenda/src/Agenda.ts + +/** + * @typedef {MyObj} RepeatOptions + */ + +/** + * @typedef {MyObj} Job + */ + +/** + * @typedef {Object} IJob + * @property {MyObj} data + */ +class NewAgenda { +>NewAgenda : NewAgenda + + /** + * @param {string | number} interval + * @param {string} name + * @param {IJob['data']} data + * @param {RepeatOptions} options + * @returns {Promise} + */ + async _createIntervalJob(interval, name, data, options) { +>_createIntervalJob : (interval: string | number, name: string, data: IJob['data'], options: RepeatOptions) => Promise +>interval : string | number +>name : string +>data : MyObj +>options : MyObj + + return /** @type {any} */ (undefined); +>(undefined) : any +>undefined : undefined + } + + /** + * @param {string | number} interval + * @param {string[]} names + * @param {IJob['data']} data + * @param {RepeatOptions} options + * @returns {Promise | undefined} + */ + _createIntervalJobs(interval, names, data, options) { +>_createIntervalJobs : (interval: string | number, names: string[], data: IJob['data'], options: RepeatOptions) => Promise | undefined +>interval : string | number +>names : string[] +>data : MyObj +>options : MyObj + + return undefined; +>undefined : undefined + } + + /** + * @template {string | string[]} T + * @param {string | number} interval + * @param {T} name + * @param {IJob['data']} data + * @param {RepeatOptions} options + * @returns {Promise>} + */ + async newEvery(interval, name, data, options) { +>newEvery : (interval: string | number, name: T, data: IJob['data'], options: RepeatOptions) => Promise> +>interval : string | number +>name : T +>data : MyObj +>options : MyObj + + if (typeof name === 'string') { +>typeof name === 'string' : boolean +>typeof name : "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" +>name : T +>'string' : "string" + + return this._createIntervalJob(interval, name, data, options); // Ok +>this._createIntervalJob(interval, name, data, options) : Promise +>this._createIntervalJob : (interval: string | number, name: string, data: IJob['data'], options: RepeatOptions) => Promise +>this : this +>_createIntervalJob : (interval: string | number, name: string, data: IJob['data'], options: RepeatOptions) => Promise +>interval : string | number +>name : string +>data : MyObj +>options : MyObj + } + + if (Array.isArray(name)) { +>Array.isArray(name) : boolean +>Array.isArray : (arg: any) => arg is any[] +>Array : ArrayConstructor +>isArray : (arg: any) => arg is any[] +>name : string[] + + return this._createIntervalJobs(interval, name, data, options); // Ok +>this._createIntervalJobs(interval, name, data, options) : Promise | undefined +>this._createIntervalJobs : (interval: string | number, names: string[], data: IJob['data'], options: RepeatOptions) => Promise | undefined +>this : this +>_createIntervalJobs : (interval: string | number, names: string[], data: IJob['data'], options: RepeatOptions) => Promise | undefined +>interval : string | number +>name : string[] +>data : MyObj +>options : MyObj + } + + throw new Error('Unexpected error: Invalid job name(s)'); +>new Error('Unexpected error: Invalid job name(s)') : Error +>Error : ErrorConstructor +>'Unexpected error: Invalid job name(s)' : "Unexpected error: Invalid job name(s)" + } +} + +// File: angular/packages/common/src/pipes/case_conversion_pipes.ts + +/** + * @template {string | null | undefined} T + * @param {T} value + * @returns {HelperCond} + */ +function transform1(value) { +>transform1 : (value: T) => HelperCond +>value : T + + if (value == null) return null; // Ok +>value == null : boolean +>value : T + + if (typeof value !== 'string') { +>typeof value !== 'string' : boolean +>typeof value : "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" +>value : NonNullable +>'string' : "string" + + throw new Error(); +>new Error() : Error +>Error : ErrorConstructor + } + return value.toLowerCase(); // Ok +>value.toLowerCase() : string +>value.toLowerCase : () => string +>value : string +>toLowerCase : () => string +} + diff --git a/tsc/testdata/baselines/reference/compiler/dependentReturnType3.errors.txt b/tsc/testdata/baselines/reference/compiler/dependentReturnType3.errors.txt new file mode 100644 index 0000000000000..39a9326242262 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/dependentReturnType3.errors.txt @@ -0,0 +1,224 @@ +dependentReturnType3.ts(114,13): error TS2322: Type 'undefined' is not assignable to type 'HelperCond[]>'. +dependentReturnType3.ts(130,16): error TS2536: Type 'I' cannot be used to index type '{ [s: string]: any; }'. +dependentReturnType3.ts(141,9): error TS2322: Type 'Record' is not assignable to type 'HelperCond>'. + + +==== dependentReturnType3.ts (3 errors) ==== + // Adapted from ts-error-deltas repos + + type HelperCond = + T extends A + ? R1 + : T extends B + ? R2 + : never; + + + // File: Rocket.Chat/apps/meteor/app/katex/client/index.ts + interface IMessage { + html?: string; + tokens?: {}[]; + } + + class NewKatex { + render(s: string): string { + return ""; + } + + renderMessage(message: T): + T extends string + ? string + : T extends IMessage + ? IMessage + : never { + if (typeof message === 'string') { + return this.render(message); // Ok + } + + if (!message.html?.trim()) { + return message; // Ok + } + + if (!message.tokens) { + message.tokens = []; + } + + message.html = this.render(message.html); + return message; // Ok + } + } + + export function createKatexMessageRendering( + options: { + dollarSyntax: boolean; + parenthesisSyntax: boolean; + }, + _isMessage: T, + ): T extends true + ? (message: IMessage) => IMessage + : T extends false + ? (message: string) => string + : never { + const instance = new NewKatex(); + if (_isMessage) { + return (message: IMessage): IMessage => instance.renderMessage(message); // Ok + } + return (message: string): string => instance.renderMessage(message); // Ok + } + + // File: Rocket.Chat/apps/meteor/app/settings/lib/settings.ts + type SettingComposedValue = { key: string; value: T }; + type SettingCallback = (key: string, value: SettingValue, initialLoad?: boolean) => void; + + type SettingValue = object; + declare const Meteor: { settings: { [s: string]: any } }; + declare const _: { isRegExp(x: unknown): x is RegExp; }; + declare function takesRegExp(x: RegExp): void; + declare function takesString(x: string): void; + + class NewSettingsBase { + public newGet( + _id: I, + callback?: C, + ): HelperCond[]>> { + if (callback !== undefined) { + if (!Meteor.settings) { + return; // Ok + } + if (_id === '*') { + return Object.keys(Meteor.settings).forEach((key) => { // Ok + const value = Meteor.settings[key]; + callback(key, value); + }); + } + if (_.isRegExp(_id) && Meteor.settings) { + return Object.keys(Meteor.settings).forEach((key) => { // Ok + if (!_id.test(key)) { + return; + } + const value = Meteor.settings[key]; + callback(key, value); + }); + } + + if (typeof _id === 'string') { + const value = Meteor.settings[_id]; + if (value != null) { + callback(_id, Meteor.settings[_id]); + } + return; // Ok + } + + return; // Ok, needed for exhaustiveness check + } + + if (!Meteor.settings) { // Wrong: we don't know that _id is string here, cannot return undefined + return undefined; // Error + ~~~~~~ +!!! error TS2322: Type 'undefined' is not assignable to type 'HelperCond[]>'. + } + + if (_.isRegExp(_id)) { + return Object.keys(Meteor.settings).reduce((items: SettingComposedValue[], key) => { + const value = Meteor.settings[key]; + if (_id.test(key)) { + items.push({ + key, + value, + }); + } + return items; + }, []); // Ok + } + + return Meteor.settings?.[_id]; // Error + ~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2536: Type 'I' cannot be used to index type '{ [s: string]: any; }'. + // The indexing currently doesn't work because it doesn't use the narrowed type of `_id`. + } + } + + // File: Rocket.Chat/apps/meteor/app/ui-utils/client/lib/messageBox.ts + type MessageBoxAction = object; + + function getWithBug(group: T): + HelperCond> { + if (!group) { + return {} as Record; // Error, could fall into this branch when group is empty string + ~~~~~~ +!!! error TS2322: Type 'Record' is not assignable to type 'HelperCond>'. + } + + return [] as MessageBoxAction[]; // Ok + } + + function getWithoutBug(group: T): + HelperCond> { + if (group === undefined) { + return {} as Record; // Ok + } + + return [] as MessageBoxAction[]; // Ok + } + + // File: Rocket.Chat/apps/meteor/ee/server/lib/engagementDashboard/date.ts + declare function mapDateForAPI(x: string): Date; + export function transformDatesForAPI( + start: string, + end?: T + ): HelperCond { + return end !== undefined ? // Ok + { + start: mapDateForAPI(start), + end: mapDateForAPI(end), + } : + { + start: mapDateForAPI(start), + end: undefined + }; + } + + // File: Rocket.Chat/packages/agenda/src/Agenda.ts + type RepeatOptions = object; + type Job = object; + type IJob = { data: object }; + class NewAgenda { + public async _createIntervalJob(interval: string | number, name: string, data: IJob['data'], options: RepeatOptions): Promise { return undefined as any; } + private _createIntervalJobs( + interval: string | number, + names: string[], + data: IJob['data'], + options: RepeatOptions, + ): Promise | undefined { return undefined as any; } + + public async newEvery( + interval: string | number, + name: T, + data: IJob['data'], + options: RepeatOptions): Promise> { + if (typeof name === 'string') { + return this._createIntervalJob(interval, name, data, options); // Ok + } + + if (Array.isArray(name)) { + return this._createIntervalJobs(interval, name, data, options); // Ok + // Possible bug in original: createIntervalJobs can return undefined, but the original overload did not acount for that. + } + + throw new Error('Unexpected error: Invalid job name(s)'); + } + } + + // File: angular/packages/common/src/pipes/case_conversion_pipes.ts + + function transform1(value: T): HelperCond { + if (value == null) return null; // Ok + if (typeof value !== 'string') { + throw new Error(); + } + return value.toLowerCase(); // Ok + } \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/dependentReturnType3.symbols b/tsc/testdata/baselines/reference/compiler/dependentReturnType3.symbols new file mode 100644 index 0000000000000..3d39bf1559fba --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/dependentReturnType3.symbols @@ -0,0 +1,679 @@ +//// [tests/cases/compiler/dependentReturnType3.ts] //// + +=== dependentReturnType3.ts === +// Adapted from ts-error-deltas repos + +type HelperCond = +>HelperCond : Symbol(HelperCond, Decl(dependentReturnType3.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType3.ts, 2, 16)) +>A : Symbol(A, Decl(dependentReturnType3.ts, 2, 18)) +>R1 : Symbol(R1, Decl(dependentReturnType3.ts, 2, 21)) +>B : Symbol(B, Decl(dependentReturnType3.ts, 2, 25)) +>R2 : Symbol(R2, Decl(dependentReturnType3.ts, 2, 28)) + + T extends A +>T : Symbol(T, Decl(dependentReturnType3.ts, 2, 16)) +>A : Symbol(A, Decl(dependentReturnType3.ts, 2, 18)) + + ? R1 +>R1 : Symbol(R1, Decl(dependentReturnType3.ts, 2, 21)) + + : T extends B +>T : Symbol(T, Decl(dependentReturnType3.ts, 2, 16)) +>B : Symbol(B, Decl(dependentReturnType3.ts, 2, 25)) + + ? R2 +>R2 : Symbol(R2, Decl(dependentReturnType3.ts, 2, 28)) + + : never; + + +// File: Rocket.Chat/apps/meteor/app/katex/client/index.ts +interface IMessage { +>IMessage : Symbol(IMessage, Decl(dependentReturnType3.ts, 7, 20)) + + html?: string; +>html : Symbol(IMessage.html, Decl(dependentReturnType3.ts, 11, 20)) + + tokens?: {}[]; +>tokens : Symbol(IMessage.tokens, Decl(dependentReturnType3.ts, 12, 18)) +} + +class NewKatex { +>NewKatex : Symbol(NewKatex, Decl(dependentReturnType3.ts, 14, 1)) + + render(s: string): string { +>render : Symbol(NewKatex.render, Decl(dependentReturnType3.ts, 16, 16)) +>s : Symbol(s, Decl(dependentReturnType3.ts, 17, 11)) + + return ""; + } + + renderMessage(message: T): +>renderMessage : Symbol(NewKatex.renderMessage, Decl(dependentReturnType3.ts, 19, 5)) +>T : Symbol(T, Decl(dependentReturnType3.ts, 21, 18)) +>IMessage : Symbol(IMessage, Decl(dependentReturnType3.ts, 7, 20)) +>message : Symbol(message, Decl(dependentReturnType3.ts, 21, 47)) +>T : Symbol(T, Decl(dependentReturnType3.ts, 21, 18)) + + T extends string +>T : Symbol(T, Decl(dependentReturnType3.ts, 21, 18)) + + ? string + : T extends IMessage +>T : Symbol(T, Decl(dependentReturnType3.ts, 21, 18)) +>IMessage : Symbol(IMessage, Decl(dependentReturnType3.ts, 7, 20)) + + ? IMessage +>IMessage : Symbol(IMessage, Decl(dependentReturnType3.ts, 7, 20)) + + : never { + if (typeof message === 'string') { +>message : Symbol(message, Decl(dependentReturnType3.ts, 21, 47)) + + return this.render(message); // Ok +>this.render : Symbol(NewKatex.render, Decl(dependentReturnType3.ts, 16, 16)) +>this : Symbol(NewKatex, Decl(dependentReturnType3.ts, 14, 1)) +>render : Symbol(NewKatex.render, Decl(dependentReturnType3.ts, 16, 16)) +>message : Symbol(message, Decl(dependentReturnType3.ts, 21, 47)) + } + + if (!message.html?.trim()) { +>message.html?.trim : Symbol(String.trim, Decl(lib.es5.d.ts, --, --)) +>message.html : Symbol(IMessage.html, Decl(dependentReturnType3.ts, 11, 20)) +>message : Symbol(message, Decl(dependentReturnType3.ts, 21, 47)) +>html : Symbol(IMessage.html, Decl(dependentReturnType3.ts, 11, 20)) +>trim : Symbol(String.trim, Decl(lib.es5.d.ts, --, --)) + + return message; // Ok +>message : Symbol(message, Decl(dependentReturnType3.ts, 21, 47)) + } + + if (!message.tokens) { +>message.tokens : Symbol(IMessage.tokens, Decl(dependentReturnType3.ts, 12, 18)) +>message : Symbol(message, Decl(dependentReturnType3.ts, 21, 47)) +>tokens : Symbol(IMessage.tokens, Decl(dependentReturnType3.ts, 12, 18)) + + message.tokens = []; +>message.tokens : Symbol(IMessage.tokens, Decl(dependentReturnType3.ts, 12, 18)) +>message : Symbol(message, Decl(dependentReturnType3.ts, 21, 47)) +>tokens : Symbol(IMessage.tokens, Decl(dependentReturnType3.ts, 12, 18)) + } + + message.html = this.render(message.html); +>message.html : Symbol(IMessage.html, Decl(dependentReturnType3.ts, 11, 20)) +>message : Symbol(message, Decl(dependentReturnType3.ts, 21, 47)) +>html : Symbol(IMessage.html, Decl(dependentReturnType3.ts, 11, 20)) +>this.render : Symbol(NewKatex.render, Decl(dependentReturnType3.ts, 16, 16)) +>this : Symbol(NewKatex, Decl(dependentReturnType3.ts, 14, 1)) +>render : Symbol(NewKatex.render, Decl(dependentReturnType3.ts, 16, 16)) +>message.html : Symbol(IMessage.html, Decl(dependentReturnType3.ts, 11, 20)) +>message : Symbol(message, Decl(dependentReturnType3.ts, 21, 47)) +>html : Symbol(IMessage.html, Decl(dependentReturnType3.ts, 11, 20)) + + return message; // Ok +>message : Symbol(message, Decl(dependentReturnType3.ts, 21, 47)) + } +} + +export function createKatexMessageRendering( +>createKatexMessageRendering : Symbol(createKatexMessageRendering, Decl(dependentReturnType3.ts, 42, 1)) +>T : Symbol(T, Decl(dependentReturnType3.ts, 44, 44)) + + options: { +>options : Symbol(options, Decl(dependentReturnType3.ts, 44, 68)) + + dollarSyntax: boolean; +>dollarSyntax : Symbol(dollarSyntax, Decl(dependentReturnType3.ts, 45, 14)) + + parenthesisSyntax: boolean; +>parenthesisSyntax : Symbol(parenthesisSyntax, Decl(dependentReturnType3.ts, 46, 30)) + + }, + _isMessage: T, +>_isMessage : Symbol(_isMessage, Decl(dependentReturnType3.ts, 48, 6)) +>T : Symbol(T, Decl(dependentReturnType3.ts, 44, 44)) + +): T extends true +>T : Symbol(T, Decl(dependentReturnType3.ts, 44, 44)) + + ? (message: IMessage) => IMessage +>message : Symbol(message, Decl(dependentReturnType3.ts, 51, 7)) +>IMessage : Symbol(IMessage, Decl(dependentReturnType3.ts, 7, 20)) +>IMessage : Symbol(IMessage, Decl(dependentReturnType3.ts, 7, 20)) + + : T extends false +>T : Symbol(T, Decl(dependentReturnType3.ts, 44, 44)) + + ? (message: string) => string +>message : Symbol(message, Decl(dependentReturnType3.ts, 53, 9)) + + : never { + const instance = new NewKatex(); +>instance : Symbol(instance, Decl(dependentReturnType3.ts, 55, 9)) +>NewKatex : Symbol(NewKatex, Decl(dependentReturnType3.ts, 14, 1)) + + if (_isMessage) { +>_isMessage : Symbol(_isMessage, Decl(dependentReturnType3.ts, 48, 6)) + + return (message: IMessage): IMessage => instance.renderMessage(message); // Ok +>message : Symbol(message, Decl(dependentReturnType3.ts, 57, 16)) +>IMessage : Symbol(IMessage, Decl(dependentReturnType3.ts, 7, 20)) +>IMessage : Symbol(IMessage, Decl(dependentReturnType3.ts, 7, 20)) +>instance.renderMessage : Symbol(NewKatex.renderMessage, Decl(dependentReturnType3.ts, 19, 5)) +>instance : Symbol(instance, Decl(dependentReturnType3.ts, 55, 9)) +>renderMessage : Symbol(NewKatex.renderMessage, Decl(dependentReturnType3.ts, 19, 5)) +>message : Symbol(message, Decl(dependentReturnType3.ts, 57, 16)) + } + return (message: string): string => instance.renderMessage(message); // Ok +>message : Symbol(message, Decl(dependentReturnType3.ts, 59, 12)) +>instance.renderMessage : Symbol(NewKatex.renderMessage, Decl(dependentReturnType3.ts, 19, 5)) +>instance : Symbol(instance, Decl(dependentReturnType3.ts, 55, 9)) +>renderMessage : Symbol(NewKatex.renderMessage, Decl(dependentReturnType3.ts, 19, 5)) +>message : Symbol(message, Decl(dependentReturnType3.ts, 59, 12)) +} + +// File: Rocket.Chat/apps/meteor/app/settings/lib/settings.ts +type SettingComposedValue = { key: string; value: T }; +>SettingComposedValue : Symbol(SettingComposedValue, Decl(dependentReturnType3.ts, 60, 1)) +>T : Symbol(T, Decl(dependentReturnType3.ts, 63, 26)) +>SettingValue : Symbol(SettingValue, Decl(dependentReturnType3.ts, 64, 89)) +>SettingValue : Symbol(SettingValue, Decl(dependentReturnType3.ts, 64, 89)) +>key : Symbol(key, Decl(dependentReturnType3.ts, 63, 68)) +>value : Symbol(value, Decl(dependentReturnType3.ts, 63, 81)) +>T : Symbol(T, Decl(dependentReturnType3.ts, 63, 26)) + +type SettingCallback = (key: string, value: SettingValue, initialLoad?: boolean) => void; +>SettingCallback : Symbol(SettingCallback, Decl(dependentReturnType3.ts, 63, 93)) +>key : Symbol(key, Decl(dependentReturnType3.ts, 64, 24)) +>value : Symbol(value, Decl(dependentReturnType3.ts, 64, 36)) +>SettingValue : Symbol(SettingValue, Decl(dependentReturnType3.ts, 64, 89)) +>initialLoad : Symbol(initialLoad, Decl(dependentReturnType3.ts, 64, 57)) + +type SettingValue = object; +>SettingValue : Symbol(SettingValue, Decl(dependentReturnType3.ts, 64, 89)) + +declare const Meteor: { settings: { [s: string]: any } }; +>Meteor : Symbol(Meteor, Decl(dependentReturnType3.ts, 67, 13)) +>settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23)) +>s : Symbol(s, Decl(dependentReturnType3.ts, 67, 37)) + +declare const _: { isRegExp(x: unknown): x is RegExp; }; +>_ : Symbol(_, Decl(dependentReturnType3.ts, 68, 13)) +>isRegExp : Symbol(isRegExp, Decl(dependentReturnType3.ts, 68, 18)) +>x : Symbol(x, Decl(dependentReturnType3.ts, 68, 28)) +>x : Symbol(x, Decl(dependentReturnType3.ts, 68, 28)) +>RegExp : Symbol(RegExp, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) + +declare function takesRegExp(x: RegExp): void; +>takesRegExp : Symbol(takesRegExp, Decl(dependentReturnType3.ts, 68, 56)) +>x : Symbol(x, Decl(dependentReturnType3.ts, 69, 29)) +>RegExp : Symbol(RegExp, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) + +declare function takesString(x: string): void; +>takesString : Symbol(takesString, Decl(dependentReturnType3.ts, 69, 46)) +>x : Symbol(x, Decl(dependentReturnType3.ts, 70, 29)) + +class NewSettingsBase { +>NewSettingsBase : Symbol(NewSettingsBase, Decl(dependentReturnType3.ts, 70, 46)) + + public newGet( +>newGet : Symbol(NewSettingsBase.newGet, Decl(dependentReturnType3.ts, 72, 23)) +>C : Symbol(C, Decl(dependentReturnType3.ts, 73, 18)) +>SettingCallback : Symbol(SettingCallback, Decl(dependentReturnType3.ts, 63, 93)) +>I : Symbol(I, Decl(dependentReturnType3.ts, 73, 56)) +>RegExp : Symbol(RegExp, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) +>T : Symbol(T, Decl(dependentReturnType3.ts, 73, 83)) +>SettingValue : Symbol(SettingValue, Decl(dependentReturnType3.ts, 64, 89)) +>SettingValue : Symbol(SettingValue, Decl(dependentReturnType3.ts, 64, 89)) + + _id: I, +>_id : Symbol(_id, Decl(dependentReturnType3.ts, 73, 123)) +>I : Symbol(I, Decl(dependentReturnType3.ts, 73, 56)) + + callback?: C, +>callback : Symbol(callback, Decl(dependentReturnType3.ts, 74, 15)) +>C : Symbol(C, Decl(dependentReturnType3.ts, 73, 18)) + + ): HelperCondHelperCond : Symbol(HelperCond, Decl(dependentReturnType3.ts, 0, 0)) +>C : Symbol(C, Decl(dependentReturnType3.ts, 73, 18)) + + SettingCallback, void, +>SettingCallback : Symbol(SettingCallback, Decl(dependentReturnType3.ts, 63, 93)) + + undefined, HelperCondHelperCond : Symbol(HelperCond, Decl(dependentReturnType3.ts, 0, 0)) +>I : Symbol(I, Decl(dependentReturnType3.ts, 73, 56)) + + string, T | undefined, +>T : Symbol(T, Decl(dependentReturnType3.ts, 73, 83)) + + RegExp, SettingComposedValue[]>> { +>RegExp : Symbol(RegExp, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) +>SettingComposedValue : Symbol(SettingComposedValue, Decl(dependentReturnType3.ts, 60, 1)) +>T : Symbol(T, Decl(dependentReturnType3.ts, 73, 83)) + + if (callback !== undefined) { +>callback : Symbol(callback, Decl(dependentReturnType3.ts, 74, 15)) +>undefined : Symbol(undefined) + + if (!Meteor.settings) { +>Meteor.settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23)) +>Meteor : Symbol(Meteor, Decl(dependentReturnType3.ts, 67, 13)) +>settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23)) + + return; // Ok + } + if (_id === '*') { +>_id : Symbol(_id, Decl(dependentReturnType3.ts, 73, 123)) + + return Object.keys(Meteor.settings).forEach((key) => { // Ok +>Object.keys(Meteor.settings).forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --)) +>Object.keys : Symbol(ObjectConstructor.keys, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>keys : Symbol(ObjectConstructor.keys, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --)) +>Meteor.settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23)) +>Meteor : Symbol(Meteor, Decl(dependentReturnType3.ts, 67, 13)) +>settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23)) +>forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --)) +>key : Symbol(key, Decl(dependentReturnType3.ts, 86, 61)) + + const value = Meteor.settings[key]; +>value : Symbol(value, Decl(dependentReturnType3.ts, 87, 25)) +>Meteor.settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23)) +>Meteor : Symbol(Meteor, Decl(dependentReturnType3.ts, 67, 13)) +>settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23)) +>key : Symbol(key, Decl(dependentReturnType3.ts, 86, 61)) + + callback(key, value); +>callback : Symbol(callback, Decl(dependentReturnType3.ts, 74, 15)) +>key : Symbol(key, Decl(dependentReturnType3.ts, 86, 61)) +>value : Symbol(value, Decl(dependentReturnType3.ts, 87, 25)) + + }); + } + if (_.isRegExp(_id) && Meteor.settings) { +>_.isRegExp : Symbol(isRegExp, Decl(dependentReturnType3.ts, 68, 18)) +>_ : Symbol(_, Decl(dependentReturnType3.ts, 68, 13)) +>isRegExp : Symbol(isRegExp, Decl(dependentReturnType3.ts, 68, 18)) +>_id : Symbol(_id, Decl(dependentReturnType3.ts, 73, 123)) +>Meteor.settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23)) +>Meteor : Symbol(Meteor, Decl(dependentReturnType3.ts, 67, 13)) +>settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23)) + + return Object.keys(Meteor.settings).forEach((key) => { // Ok +>Object.keys(Meteor.settings).forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --)) +>Object.keys : Symbol(ObjectConstructor.keys, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>keys : Symbol(ObjectConstructor.keys, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --)) +>Meteor.settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23)) +>Meteor : Symbol(Meteor, Decl(dependentReturnType3.ts, 67, 13)) +>settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23)) +>forEach : Symbol(Array.forEach, Decl(lib.es5.d.ts, --, --)) +>key : Symbol(key, Decl(dependentReturnType3.ts, 92, 61)) + + if (!_id.test(key)) { +>_id.test : Symbol(RegExp.test, Decl(lib.es5.d.ts, --, --)) +>_id : Symbol(_id, Decl(dependentReturnType3.ts, 73, 123)) +>test : Symbol(RegExp.test, Decl(lib.es5.d.ts, --, --)) +>key : Symbol(key, Decl(dependentReturnType3.ts, 92, 61)) + + return; + } + const value = Meteor.settings[key]; +>value : Symbol(value, Decl(dependentReturnType3.ts, 96, 25)) +>Meteor.settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23)) +>Meteor : Symbol(Meteor, Decl(dependentReturnType3.ts, 67, 13)) +>settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23)) +>key : Symbol(key, Decl(dependentReturnType3.ts, 92, 61)) + + callback(key, value); +>callback : Symbol(callback, Decl(dependentReturnType3.ts, 74, 15)) +>key : Symbol(key, Decl(dependentReturnType3.ts, 92, 61)) +>value : Symbol(value, Decl(dependentReturnType3.ts, 96, 25)) + + }); + } + + if (typeof _id === 'string') { +>_id : Symbol(_id, Decl(dependentReturnType3.ts, 73, 123)) + + const value = Meteor.settings[_id]; +>value : Symbol(value, Decl(dependentReturnType3.ts, 102, 21)) +>Meteor.settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23)) +>Meteor : Symbol(Meteor, Decl(dependentReturnType3.ts, 67, 13)) +>settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23)) +>_id : Symbol(_id, Decl(dependentReturnType3.ts, 73, 123)) + + if (value != null) { +>value : Symbol(value, Decl(dependentReturnType3.ts, 102, 21)) + + callback(_id, Meteor.settings[_id]); +>callback : Symbol(callback, Decl(dependentReturnType3.ts, 74, 15)) +>_id : Symbol(_id, Decl(dependentReturnType3.ts, 73, 123)) +>Meteor.settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23)) +>Meteor : Symbol(Meteor, Decl(dependentReturnType3.ts, 67, 13)) +>settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23)) +>_id : Symbol(_id, Decl(dependentReturnType3.ts, 73, 123)) + } + return; // Ok + } + + return; // Ok, needed for exhaustiveness check + } + + if (!Meteor.settings) { // Wrong: we don't know that _id is string here, cannot return undefined +>Meteor.settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23)) +>Meteor : Symbol(Meteor, Decl(dependentReturnType3.ts, 67, 13)) +>settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23)) + + return undefined; // Error +>undefined : Symbol(undefined) + } + + if (_.isRegExp(_id)) { +>_.isRegExp : Symbol(isRegExp, Decl(dependentReturnType3.ts, 68, 18)) +>_ : Symbol(_, Decl(dependentReturnType3.ts, 68, 13)) +>isRegExp : Symbol(isRegExp, Decl(dependentReturnType3.ts, 68, 18)) +>_id : Symbol(_id, Decl(dependentReturnType3.ts, 73, 123)) + + return Object.keys(Meteor.settings).reduce((items: SettingComposedValue[], key) => { +>Object.keys(Meteor.settings).reduce : Symbol(Array.reduce, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>Object.keys : Symbol(ObjectConstructor.keys, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>keys : Symbol(ObjectConstructor.keys, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --)) +>Meteor.settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23)) +>Meteor : Symbol(Meteor, Decl(dependentReturnType3.ts, 67, 13)) +>settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23)) +>reduce : Symbol(Array.reduce, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>items : Symbol(items, Decl(dependentReturnType3.ts, 117, 56)) +>SettingComposedValue : Symbol(SettingComposedValue, Decl(dependentReturnType3.ts, 60, 1)) +>T : Symbol(T, Decl(dependentReturnType3.ts, 73, 83)) +>key : Symbol(key, Decl(dependentReturnType3.ts, 117, 89)) + + const value = Meteor.settings[key]; +>value : Symbol(value, Decl(dependentReturnType3.ts, 118, 9)) +>Meteor.settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23)) +>Meteor : Symbol(Meteor, Decl(dependentReturnType3.ts, 67, 13)) +>settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23)) +>key : Symbol(key, Decl(dependentReturnType3.ts, 117, 89)) + + if (_id.test(key)) { +>_id.test : Symbol(RegExp.test, Decl(lib.es5.d.ts, --, --)) +>_id : Symbol(_id, Decl(dependentReturnType3.ts, 73, 123)) +>test : Symbol(RegExp.test, Decl(lib.es5.d.ts, --, --)) +>key : Symbol(key, Decl(dependentReturnType3.ts, 117, 89)) + + items.push({ +>items.push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --)) +>items : Symbol(items, Decl(dependentReturnType3.ts, 117, 56)) +>push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --)) + + key, +>key : Symbol(key, Decl(dependentReturnType3.ts, 120, 17)) + + value, +>value : Symbol(value, Decl(dependentReturnType3.ts, 121, 10)) + + }); + } + return items; +>items : Symbol(items, Decl(dependentReturnType3.ts, 117, 56)) + + }, []); // Ok + } + + return Meteor.settings?.[_id]; // Error +>Meteor.settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23)) +>Meteor : Symbol(Meteor, Decl(dependentReturnType3.ts, 67, 13)) +>settings : Symbol(settings, Decl(dependentReturnType3.ts, 67, 23)) +>_id : Symbol(_id, Decl(dependentReturnType3.ts, 73, 123)) + + // The indexing currently doesn't work because it doesn't use the narrowed type of `_id`. + } +} + +// File: Rocket.Chat/apps/meteor/app/ui-utils/client/lib/messageBox.ts +type MessageBoxAction = object; +>MessageBoxAction : Symbol(MessageBoxAction, Decl(dependentReturnType3.ts, 132, 1)) + +function getWithBug(group: T): +>getWithBug : Symbol(getWithBug, Decl(dependentReturnType3.ts, 135, 31)) +>T : Symbol(T, Decl(dependentReturnType3.ts, 137, 20)) +>group : Symbol(group, Decl(dependentReturnType3.ts, 137, 50)) +>T : Symbol(T, Decl(dependentReturnType3.ts, 137, 20)) + +HelperCond> { +>HelperCond : Symbol(HelperCond, Decl(dependentReturnType3.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType3.ts, 137, 20)) +>MessageBoxAction : Symbol(MessageBoxAction, Decl(dependentReturnType3.ts, 132, 1)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>MessageBoxAction : Symbol(MessageBoxAction, Decl(dependentReturnType3.ts, 132, 1)) + + if (!group) { +>group : Symbol(group, Decl(dependentReturnType3.ts, 137, 50)) + + return {} as Record; // Error, could fall into this branch when group is empty string +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>MessageBoxAction : Symbol(MessageBoxAction, Decl(dependentReturnType3.ts, 132, 1)) + } + + return [] as MessageBoxAction[]; // Ok +>MessageBoxAction : Symbol(MessageBoxAction, Decl(dependentReturnType3.ts, 132, 1)) +} + +function getWithoutBug(group: T): +>getWithoutBug : Symbol(getWithoutBug, Decl(dependentReturnType3.ts, 144, 1)) +>T : Symbol(T, Decl(dependentReturnType3.ts, 146, 23)) +>group : Symbol(group, Decl(dependentReturnType3.ts, 146, 53)) +>T : Symbol(T, Decl(dependentReturnType3.ts, 146, 23)) + +HelperCond> { +>HelperCond : Symbol(HelperCond, Decl(dependentReturnType3.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType3.ts, 146, 23)) +>MessageBoxAction : Symbol(MessageBoxAction, Decl(dependentReturnType3.ts, 132, 1)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>MessageBoxAction : Symbol(MessageBoxAction, Decl(dependentReturnType3.ts, 132, 1)) + + if (group === undefined) { +>group : Symbol(group, Decl(dependentReturnType3.ts, 146, 53)) +>undefined : Symbol(undefined) + + return {} as Record; // Ok +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>MessageBoxAction : Symbol(MessageBoxAction, Decl(dependentReturnType3.ts, 132, 1)) + } + + return [] as MessageBoxAction[]; // Ok +>MessageBoxAction : Symbol(MessageBoxAction, Decl(dependentReturnType3.ts, 132, 1)) +} + +// File: Rocket.Chat/apps/meteor/ee/server/lib/engagementDashboard/date.ts +declare function mapDateForAPI(x: string): Date; +>mapDateForAPI : Symbol(mapDateForAPI, Decl(dependentReturnType3.ts, 153, 1)) +>x : Symbol(x, Decl(dependentReturnType3.ts, 156, 31)) +>Date : Symbol(Date, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.scripthost.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) + +export function transformDatesForAPI( +>transformDatesForAPI : Symbol(transformDatesForAPI, Decl(dependentReturnType3.ts, 156, 48)) +>T : Symbol(T, Decl(dependentReturnType3.ts, 157, 37)) + + start: string, +>start : Symbol(start, Decl(dependentReturnType3.ts, 157, 67)) + + end?: T +>end : Symbol(end, Decl(dependentReturnType3.ts, 158, 18)) +>T : Symbol(T, Decl(dependentReturnType3.ts, 157, 37)) + +): HelperCond { +>HelperCond : Symbol(HelperCond, Decl(dependentReturnType3.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType3.ts, 157, 37)) +>start : Symbol(start, Decl(dependentReturnType3.ts, 160, 26)) +>Date : Symbol(Date, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.scripthost.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) +>end : Symbol(end, Decl(dependentReturnType3.ts, 160, 39)) +>Date : Symbol(Date, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.scripthost.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) +>start : Symbol(start, Decl(dependentReturnType3.ts, 160, 65)) +>Date : Symbol(Date, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.scripthost.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) +>end : Symbol(end, Decl(dependentReturnType3.ts, 160, 78)) + + return end !== undefined ? // Ok +>end : Symbol(end, Decl(dependentReturnType3.ts, 158, 18)) +>undefined : Symbol(undefined) + { + start: mapDateForAPI(start), +>start : Symbol(start, Decl(dependentReturnType3.ts, 162, 9)) +>mapDateForAPI : Symbol(mapDateForAPI, Decl(dependentReturnType3.ts, 153, 1)) +>start : Symbol(start, Decl(dependentReturnType3.ts, 157, 67)) + + end: mapDateForAPI(end), +>end : Symbol(end, Decl(dependentReturnType3.ts, 163, 40)) +>mapDateForAPI : Symbol(mapDateForAPI, Decl(dependentReturnType3.ts, 153, 1)) +>end : Symbol(end, Decl(dependentReturnType3.ts, 158, 18)) + + } : + { + start: mapDateForAPI(start), +>start : Symbol(start, Decl(dependentReturnType3.ts, 166, 9)) +>mapDateForAPI : Symbol(mapDateForAPI, Decl(dependentReturnType3.ts, 153, 1)) +>start : Symbol(start, Decl(dependentReturnType3.ts, 157, 67)) + + end: undefined +>end : Symbol(end, Decl(dependentReturnType3.ts, 167, 40)) +>undefined : Symbol(undefined) + + }; +} + +// File: Rocket.Chat/packages/agenda/src/Agenda.ts +type RepeatOptions = object; +>RepeatOptions : Symbol(RepeatOptions, Decl(dependentReturnType3.ts, 170, 1)) + +type Job = object; +>Job : Symbol(Job, Decl(dependentReturnType3.ts, 173, 28)) + +type IJob = { data: object }; +>IJob : Symbol(IJob, Decl(dependentReturnType3.ts, 174, 18)) +>data : Symbol(data, Decl(dependentReturnType3.ts, 175, 13)) + +class NewAgenda { +>NewAgenda : Symbol(NewAgenda, Decl(dependentReturnType3.ts, 175, 29)) + + public async _createIntervalJob(interval: string | number, name: string, data: IJob['data'], options: RepeatOptions): Promise { return undefined as any; } +>_createIntervalJob : Symbol(NewAgenda._createIntervalJob, Decl(dependentReturnType3.ts, 176, 17)) +>interval : Symbol(interval, Decl(dependentReturnType3.ts, 177, 36)) +>name : Symbol(name, Decl(dependentReturnType3.ts, 177, 62)) +>data : Symbol(data, Decl(dependentReturnType3.ts, 177, 76)) +>IJob : Symbol(IJob, Decl(dependentReturnType3.ts, 174, 18)) +>options : Symbol(options, Decl(dependentReturnType3.ts, 177, 96)) +>RepeatOptions : Symbol(RepeatOptions, Decl(dependentReturnType3.ts, 170, 1)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) +>Job : Symbol(Job, Decl(dependentReturnType3.ts, 173, 28)) +>undefined : Symbol(undefined) + + private _createIntervalJobs( +>_createIntervalJobs : Symbol(NewAgenda._createIntervalJobs, Decl(dependentReturnType3.ts, 177, 163)) + + interval: string | number, +>interval : Symbol(interval, Decl(dependentReturnType3.ts, 178, 32)) + + names: string[], +>names : Symbol(names, Decl(dependentReturnType3.ts, 179, 34)) + + data: IJob['data'], +>data : Symbol(data, Decl(dependentReturnType3.ts, 180, 24)) +>IJob : Symbol(IJob, Decl(dependentReturnType3.ts, 174, 18)) + + options: RepeatOptions, +>options : Symbol(options, Decl(dependentReturnType3.ts, 181, 27)) +>RepeatOptions : Symbol(RepeatOptions, Decl(dependentReturnType3.ts, 170, 1)) + + ): Promise | undefined { return undefined as any; } +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) +>Job : Symbol(Job, Decl(dependentReturnType3.ts, 173, 28)) +>undefined : Symbol(undefined) + + public async newEvery( +>newEvery : Symbol(NewAgenda.newEvery, Decl(dependentReturnType3.ts, 183, 62)) +>T : Symbol(T, Decl(dependentReturnType3.ts, 185, 26)) + + interval: string | number, +>interval : Symbol(interval, Decl(dependentReturnType3.ts, 185, 55)) + + name: T, +>name : Symbol(name, Decl(dependentReturnType3.ts, 186, 34)) +>T : Symbol(T, Decl(dependentReturnType3.ts, 185, 26)) + + data: IJob['data'], +>data : Symbol(data, Decl(dependentReturnType3.ts, 187, 16)) +>IJob : Symbol(IJob, Decl(dependentReturnType3.ts, 174, 18)) + + options: RepeatOptions): Promise> { +>options : Symbol(options, Decl(dependentReturnType3.ts, 188, 27)) +>RepeatOptions : Symbol(RepeatOptions, Decl(dependentReturnType3.ts, 170, 1)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) +>HelperCond : Symbol(HelperCond, Decl(dependentReturnType3.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType3.ts, 185, 26)) +>Job : Symbol(Job, Decl(dependentReturnType3.ts, 173, 28)) +>Job : Symbol(Job, Decl(dependentReturnType3.ts, 173, 28)) + + if (typeof name === 'string') { +>name : Symbol(name, Decl(dependentReturnType3.ts, 186, 34)) + + return this._createIntervalJob(interval, name, data, options); // Ok +>this._createIntervalJob : Symbol(NewAgenda._createIntervalJob, Decl(dependentReturnType3.ts, 176, 17)) +>this : Symbol(NewAgenda, Decl(dependentReturnType3.ts, 175, 29)) +>_createIntervalJob : Symbol(NewAgenda._createIntervalJob, Decl(dependentReturnType3.ts, 176, 17)) +>interval : Symbol(interval, Decl(dependentReturnType3.ts, 185, 55)) +>name : Symbol(name, Decl(dependentReturnType3.ts, 186, 34)) +>data : Symbol(data, Decl(dependentReturnType3.ts, 187, 16)) +>options : Symbol(options, Decl(dependentReturnType3.ts, 188, 27)) + } + + if (Array.isArray(name)) { +>Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) +>isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>name : Symbol(name, Decl(dependentReturnType3.ts, 186, 34)) + + return this._createIntervalJobs(interval, name, data, options); // Ok +>this._createIntervalJobs : Symbol(NewAgenda._createIntervalJobs, Decl(dependentReturnType3.ts, 177, 163)) +>this : Symbol(NewAgenda, Decl(dependentReturnType3.ts, 175, 29)) +>_createIntervalJobs : Symbol(NewAgenda._createIntervalJobs, Decl(dependentReturnType3.ts, 177, 163)) +>interval : Symbol(interval, Decl(dependentReturnType3.ts, 185, 55)) +>name : Symbol(name, Decl(dependentReturnType3.ts, 186, 34)) +>data : Symbol(data, Decl(dependentReturnType3.ts, 187, 16)) +>options : Symbol(options, Decl(dependentReturnType3.ts, 188, 27)) + + // Possible bug in original: createIntervalJobs can return undefined, but the original overload did not acount for that. + } + + throw new Error('Unexpected error: Invalid job name(s)'); +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + } +} + +// File: angular/packages/common/src/pipes/case_conversion_pipes.ts + +function transform1(value: T): HelperCond { +>transform1 : Symbol(transform1, Decl(dependentReturnType3.ts, 201, 1)) +>T : Symbol(T, Decl(dependentReturnType3.ts, 205, 20)) +>value : Symbol(value, Decl(dependentReturnType3.ts, 205, 57)) +>T : Symbol(T, Decl(dependentReturnType3.ts, 205, 20)) +>HelperCond : Symbol(HelperCond, Decl(dependentReturnType3.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType3.ts, 205, 20)) + + if (value == null) return null; // Ok +>value : Symbol(value, Decl(dependentReturnType3.ts, 205, 57)) + + if (typeof value !== 'string') { +>value : Symbol(value, Decl(dependentReturnType3.ts, 205, 57)) + + throw new Error(); +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + } + return value.toLowerCase(); // Ok +>value.toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --)) +>value : Symbol(value, Decl(dependentReturnType3.ts, 205, 57)) +>toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --)) +} diff --git a/tsc/testdata/baselines/reference/compiler/dependentReturnType3.types b/tsc/testdata/baselines/reference/compiler/dependentReturnType3.types new file mode 100644 index 0000000000000..7d558c18473cf --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/dependentReturnType3.types @@ -0,0 +1,652 @@ +//// [tests/cases/compiler/dependentReturnType3.ts] //// + +=== dependentReturnType3.ts === +// Adapted from ts-error-deltas repos + +type HelperCond = +>HelperCond : HelperCond + + T extends A + ? R1 + : T extends B + ? R2 + : never; + + +// File: Rocket.Chat/apps/meteor/app/katex/client/index.ts +interface IMessage { + html?: string; +>html : string | undefined + + tokens?: {}[]; +>tokens : {}[] | undefined +} + +class NewKatex { +>NewKatex : NewKatex + + render(s: string): string { +>render : (s: string) => string +>s : string + + return ""; +>"" : "" + } + + renderMessage(message: T): +>renderMessage : (message: T) => T extends string ? string : T extends IMessage ? IMessage : never +>message : T + + T extends string + ? string + : T extends IMessage + ? IMessage + : never { + if (typeof message === 'string') { +>typeof message === 'string' : boolean +>typeof message : "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" +>message : T +>'string' : "string" + + return this.render(message); // Ok +>this.render(message) : string +>this.render : (s: string) => string +>this : this +>render : (s: string) => string +>message : string + } + + if (!message.html?.trim()) { +>!message.html?.trim() : boolean +>message.html?.trim() : string | undefined +>message.html?.trim : (() => string) | undefined +>message.html : string | undefined +>message : IMessage +>html : string | undefined +>trim : (() => string) | undefined + + return message; // Ok +>message : IMessage + } + + if (!message.tokens) { +>!message.tokens : boolean +>message.tokens : {}[] | undefined +>message : IMessage +>tokens : {}[] | undefined + + message.tokens = []; +>message.tokens = [] : never[] +>message.tokens : {}[] | undefined +>message : IMessage +>tokens : {}[] | undefined +>[] : never[] + } + + message.html = this.render(message.html); +>message.html = this.render(message.html) : string +>message.html : string | undefined +>message : IMessage +>html : string | undefined +>this.render(message.html) : string +>this.render : (s: string) => string +>this : this +>render : (s: string) => string +>message.html : string +>message : IMessage +>html : string + + return message; // Ok +>message : IMessage + } +} + +export function createKatexMessageRendering( +>createKatexMessageRendering : (options: { dollarSyntax: boolean; parenthesisSyntax: boolean; }, _isMessage: T) => T extends true ? (message: IMessage) => IMessage : T extends false ? (message: string) => string : never +>true : true +>false : false + + options: { +>options : { dollarSyntax: boolean; parenthesisSyntax: boolean; } + + dollarSyntax: boolean; +>dollarSyntax : boolean + + parenthesisSyntax: boolean; +>parenthesisSyntax : boolean + + }, + _isMessage: T, +>_isMessage : T + +): T extends true +>true : true + + ? (message: IMessage) => IMessage +>message : IMessage + + : T extends false +>false : false + + ? (message: string) => string +>message : string + + : never { + const instance = new NewKatex(); +>instance : NewKatex +>new NewKatex() : NewKatex +>NewKatex : typeof NewKatex + + if (_isMessage) { +>_isMessage : T + + return (message: IMessage): IMessage => instance.renderMessage(message); // Ok +>(message: IMessage): IMessage => instance.renderMessage(message) : (message: IMessage) => IMessage +>message : IMessage +>instance.renderMessage(message) : IMessage +>instance.renderMessage : (message: T_1) => T_1 extends string ? string : T_1 extends IMessage ? IMessage : never +>instance : NewKatex +>renderMessage : (message: T_1) => T_1 extends string ? string : T_1 extends IMessage ? IMessage : never +>message : IMessage + } + return (message: string): string => instance.renderMessage(message); // Ok +>(message: string): string => instance.renderMessage(message) : (message: string) => string +>message : string +>instance.renderMessage(message) : string +>instance.renderMessage : (message: T_1) => T_1 extends string ? string : T_1 extends IMessage ? IMessage : never +>instance : NewKatex +>renderMessage : (message: T_1) => T_1 extends string ? string : T_1 extends IMessage ? IMessage : never +>message : string +} + +// File: Rocket.Chat/apps/meteor/app/settings/lib/settings.ts +type SettingComposedValue = { key: string; value: T }; +>SettingComposedValue : SettingComposedValue +>key : string +>value : T + +type SettingCallback = (key: string, value: SettingValue, initialLoad?: boolean) => void; +>SettingCallback : SettingCallback +>key : string +>value : object +>initialLoad : boolean | undefined + +type SettingValue = object; +>SettingValue : object + +declare const Meteor: { settings: { [s: string]: any } }; +>Meteor : { settings: { [s: string]: any; }; } +>settings : { [s: string]: any; } +>s : string + +declare const _: { isRegExp(x: unknown): x is RegExp; }; +>_ : { isRegExp(x: unknown): x is RegExp; } +>isRegExp : (x: unknown) => x is RegExp +>x : unknown + +declare function takesRegExp(x: RegExp): void; +>takesRegExp : (x: RegExp) => void +>x : RegExp + +declare function takesString(x: string): void; +>takesString : (x: string) => void +>x : string + +class NewSettingsBase { +>NewSettingsBase : NewSettingsBase + + public newGet( +>newGet : (_id: I, callback?: C) => HelperCond[]>> + + _id: I, +>_id : I + + callback?: C, +>callback : C | undefined + + ): HelperCond[]>> { + if (callback !== undefined) { +>callback !== undefined : boolean +>callback : C | undefined +>undefined : undefined + + if (!Meteor.settings) { +>!Meteor.settings : false +>Meteor.settings : { [s: string]: any; } +>Meteor : { settings: { [s: string]: any; }; } +>settings : { [s: string]: any; } + + return; // Ok + } + if (_id === '*') { +>_id === '*' : boolean +>_id : I +>'*' : "*" + + return Object.keys(Meteor.settings).forEach((key) => { // Ok +>Object.keys(Meteor.settings).forEach((key) => { // Ok const value = Meteor.settings[key]; callback(key, value); }) : void +>Object.keys(Meteor.settings).forEach : (callbackfn: (value: string, index: number, array: string[]) => void, thisArg?: any) => void +>Object.keys(Meteor.settings) : string[] +>Object.keys : { (o: object): string[]; (o: {}): string[]; } +>Object : ObjectConstructor +>keys : { (o: object): string[]; (o: {}): string[]; } +>Meteor.settings : { [s: string]: any; } +>Meteor : { settings: { [s: string]: any; }; } +>settings : { [s: string]: any; } +>forEach : (callbackfn: (value: string, index: number, array: string[]) => void, thisArg?: any) => void +>(key) => { // Ok const value = Meteor.settings[key]; callback(key, value); } : (key: string) => void +>key : string + + const value = Meteor.settings[key]; +>value : any +>Meteor.settings[key] : any +>Meteor.settings : { [s: string]: any; } +>Meteor : { settings: { [s: string]: any; }; } +>settings : { [s: string]: any; } +>key : string + + callback(key, value); +>callback(key, value) : void +>callback : SettingCallback +>key : string +>value : any + + }); + } + if (_.isRegExp(_id) && Meteor.settings) { +>_.isRegExp(_id) && Meteor.settings : false | { [s: string]: any; } +>_.isRegExp(_id) : boolean +>_.isRegExp : (x: unknown) => x is RegExp +>_ : { isRegExp(x: unknown): x is RegExp; } +>isRegExp : (x: unknown) => x is RegExp +>_id : string | RegExp +>Meteor.settings : { [s: string]: any; } +>Meteor : { settings: { [s: string]: any; }; } +>settings : { [s: string]: any; } + + return Object.keys(Meteor.settings).forEach((key) => { // Ok +>Object.keys(Meteor.settings).forEach((key) => { // Ok if (!_id.test(key)) { return; } const value = Meteor.settings[key]; callback(key, value); }) : void +>Object.keys(Meteor.settings).forEach : (callbackfn: (value: string, index: number, array: string[]) => void, thisArg?: any) => void +>Object.keys(Meteor.settings) : string[] +>Object.keys : { (o: object): string[]; (o: {}): string[]; } +>Object : ObjectConstructor +>keys : { (o: object): string[]; (o: {}): string[]; } +>Meteor.settings : { [s: string]: any; } +>Meteor : { settings: { [s: string]: any; }; } +>settings : { [s: string]: any; } +>forEach : (callbackfn: (value: string, index: number, array: string[]) => void, thisArg?: any) => void +>(key) => { // Ok if (!_id.test(key)) { return; } const value = Meteor.settings[key]; callback(key, value); } : (key: string) => void +>key : string + + if (!_id.test(key)) { +>!_id.test(key) : boolean +>_id.test(key) : boolean +>_id.test : (string: string) => boolean +>_id : RegExp +>test : (string: string) => boolean +>key : string + + return; + } + const value = Meteor.settings[key]; +>value : any +>Meteor.settings[key] : any +>Meteor.settings : { [s: string]: any; } +>Meteor : { settings: { [s: string]: any; }; } +>settings : { [s: string]: any; } +>key : string + + callback(key, value); +>callback(key, value) : void +>callback : SettingCallback +>key : string +>value : any + + }); + } + + if (typeof _id === 'string') { +>typeof _id === 'string' : boolean +>typeof _id : "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" +>_id : I +>'string' : "string" + + const value = Meteor.settings[_id]; +>value : any +>Meteor.settings[_id] : any +>Meteor.settings : { [s: string]: any; } +>Meteor : { settings: { [s: string]: any; }; } +>settings : { [s: string]: any; } +>_id : I & string + + if (value != null) { +>value != null : boolean +>value : any + + callback(_id, Meteor.settings[_id]); +>callback(_id, Meteor.settings[_id]) : void +>callback : SettingCallback +>_id : string +>Meteor.settings[_id] : any +>Meteor.settings : { [s: string]: any; } +>Meteor : { settings: { [s: string]: any; }; } +>settings : { [s: string]: any; } +>_id : I & string + } + return; // Ok + } + + return; // Ok, needed for exhaustiveness check + } + + if (!Meteor.settings) { // Wrong: we don't know that _id is string here, cannot return undefined +>!Meteor.settings : false +>Meteor.settings : { [s: string]: any; } +>Meteor : { settings: { [s: string]: any; }; } +>settings : { [s: string]: any; } + + return undefined; // Error +>undefined : undefined + } + + if (_.isRegExp(_id)) { +>_.isRegExp(_id) : boolean +>_.isRegExp : (x: unknown) => x is RegExp +>_ : { isRegExp(x: unknown): x is RegExp; } +>isRegExp : (x: unknown) => x is RegExp +>_id : string | RegExp + + return Object.keys(Meteor.settings).reduce((items: SettingComposedValue[], key) => { +>Object.keys(Meteor.settings).reduce((items: SettingComposedValue[], key) => { const value = Meteor.settings[key]; if (_id.test(key)) { items.push({ key, value, }); } return items; }, []) : SettingComposedValue[] +>Object.keys(Meteor.settings).reduce : { (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string; (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string; (callbackfn: (previousValue: U, currentValue: string, currentIndex: number, array: string[]) => U, initialValue: U): U; } +>Object.keys(Meteor.settings) : string[] +>Object.keys : { (o: object): string[]; (o: {}): string[]; } +>Object : ObjectConstructor +>keys : { (o: object): string[]; (o: {}): string[]; } +>Meteor.settings : { [s: string]: any; } +>Meteor : { settings: { [s: string]: any; }; } +>settings : { [s: string]: any; } +>reduce : { (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string): string; (callbackfn: (previousValue: string, currentValue: string, currentIndex: number, array: string[]) => string, initialValue: string): string; (callbackfn: (previousValue: U, currentValue: string, currentIndex: number, array: string[]) => U, initialValue: U): U; } +>(items: SettingComposedValue[], key) => { const value = Meteor.settings[key]; if (_id.test(key)) { items.push({ key, value, }); } return items; } : (items: SettingComposedValue[], key: string) => SettingComposedValue[] +>items : SettingComposedValue[] +>key : string + + const value = Meteor.settings[key]; +>value : any +>Meteor.settings[key] : any +>Meteor.settings : { [s: string]: any; } +>Meteor : { settings: { [s: string]: any; }; } +>settings : { [s: string]: any; } +>key : string + + if (_id.test(key)) { +>_id.test(key) : boolean +>_id.test : (string: string) => boolean +>_id : RegExp +>test : (string: string) => boolean +>key : string + + items.push({ +>items.push({ key, value, }) : number +>items.push : (...items: SettingComposedValue[]) => number +>items : SettingComposedValue[] +>push : (...items: SettingComposedValue[]) => number +>{ key, value, } : { key: string; value: any; } + + key, +>key : string + + value, +>value : any + + }); + } + return items; +>items : SettingComposedValue[] + + }, []); // Ok +>[] : never[] + } + + return Meteor.settings?.[_id]; // Error +>Meteor.settings?.[_id] : any +>Meteor.settings : { [s: string]: any; } +>Meteor : { settings: { [s: string]: any; }; } +>settings : { [s: string]: any; } +>_id : I + + // The indexing currently doesn't work because it doesn't use the narrowed type of `_id`. + } +} + +// File: Rocket.Chat/apps/meteor/app/ui-utils/client/lib/messageBox.ts +type MessageBoxAction = object; +>MessageBoxAction : object + +function getWithBug(group: T): +>getWithBug : (group: T) => HelperCond> +>group : T + +HelperCond> { + if (!group) { +>!group : boolean +>group : T + + return {} as Record; // Error, could fall into this branch when group is empty string +>{} as Record : Record +>{} : {} + } + + return [] as MessageBoxAction[]; // Ok +>[] as MessageBoxAction[] : object[] +>[] : never[] +} + +function getWithoutBug(group: T): +>getWithoutBug : (group: T) => HelperCond> +>group : T + +HelperCond> { + if (group === undefined) { +>group === undefined : boolean +>group : T +>undefined : undefined + + return {} as Record; // Ok +>{} as Record : Record +>{} : {} + } + + return [] as MessageBoxAction[]; // Ok +>[] as MessageBoxAction[] : object[] +>[] : never[] +} + +// File: Rocket.Chat/apps/meteor/ee/server/lib/engagementDashboard/date.ts +declare function mapDateForAPI(x: string): Date; +>mapDateForAPI : (x: string) => Date +>x : string + +export function transformDatesForAPI( +>transformDatesForAPI : (start: string, end?: T) => HelperCond + + start: string, +>start : string + + end?: T +>end : T | undefined + +): HelperCond { +>start : Date +>end : Date +>start : Date +>end : undefined + + return end !== undefined ? // Ok +>end !== undefined ? // Ok { start: mapDateForAPI(start), end: mapDateForAPI(end), } : { start: mapDateForAPI(start), end: undefined } : { start: Date; end: Date; } | { start: Date; end: undefined; } +>end !== undefined : boolean +>end : T | undefined +>undefined : undefined + { +>{ start: mapDateForAPI(start), end: mapDateForAPI(end), } : { start: Date; end: Date; } + + start: mapDateForAPI(start), +>start : Date +>mapDateForAPI(start) : Date +>mapDateForAPI : (x: string) => Date +>start : string + + end: mapDateForAPI(end), +>end : Date +>mapDateForAPI(end) : Date +>mapDateForAPI : (x: string) => Date +>end : string + + } : + { +>{ start: mapDateForAPI(start), end: undefined } : { start: Date; end: undefined; } + + start: mapDateForAPI(start), +>start : Date +>mapDateForAPI(start) : Date +>mapDateForAPI : (x: string) => Date +>start : string + + end: undefined +>end : undefined +>undefined : undefined + + }; +} + +// File: Rocket.Chat/packages/agenda/src/Agenda.ts +type RepeatOptions = object; +>RepeatOptions : object + +type Job = object; +>Job : object + +type IJob = { data: object }; +>IJob : IJob +>data : object + +class NewAgenda { +>NewAgenda : NewAgenda + + public async _createIntervalJob(interval: string | number, name: string, data: IJob['data'], options: RepeatOptions): Promise { return undefined as any; } +>_createIntervalJob : (interval: string | number, name: string, data: IJob['data'], options: RepeatOptions) => Promise +>interval : string | number +>name : string +>data : object +>options : object +>undefined as any : any +>undefined : undefined + + private _createIntervalJobs( +>_createIntervalJobs : (interval: string | number, names: string[], data: IJob['data'], options: RepeatOptions) => Promise | undefined + + interval: string | number, +>interval : string | number + + names: string[], +>names : string[] + + data: IJob['data'], +>data : object + + options: RepeatOptions, +>options : object + + ): Promise | undefined { return undefined as any; } +>undefined as any : any +>undefined : undefined + + public async newEvery( +>newEvery : (interval: string | number, name: T, data: IJob['data'], options: RepeatOptions) => Promise> + + interval: string | number, +>interval : string | number + + name: T, +>name : T + + data: IJob['data'], +>data : object + + options: RepeatOptions): Promise> { +>options : object + + if (typeof name === 'string') { +>typeof name === 'string' : boolean +>typeof name : "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" +>name : T +>'string' : "string" + + return this._createIntervalJob(interval, name, data, options); // Ok +>this._createIntervalJob(interval, name, data, options) : Promise +>this._createIntervalJob : (interval: string | number, name: string, data: IJob['data'], options: RepeatOptions) => Promise +>this : this +>_createIntervalJob : (interval: string | number, name: string, data: IJob['data'], options: RepeatOptions) => Promise +>interval : string | number +>name : string +>data : object +>options : object + } + + if (Array.isArray(name)) { +>Array.isArray(name) : boolean +>Array.isArray : (arg: any) => arg is any[] +>Array : ArrayConstructor +>isArray : (arg: any) => arg is any[] +>name : string[] + + return this._createIntervalJobs(interval, name, data, options); // Ok +>this._createIntervalJobs(interval, name, data, options) : Promise | undefined +>this._createIntervalJobs : (interval: string | number, names: string[], data: IJob['data'], options: RepeatOptions) => Promise | undefined +>this : this +>_createIntervalJobs : (interval: string | number, names: string[], data: IJob['data'], options: RepeatOptions) => Promise | undefined +>interval : string | number +>name : string[] +>data : object +>options : object + + // Possible bug in original: createIntervalJobs can return undefined, but the original overload did not acount for that. + } + + throw new Error('Unexpected error: Invalid job name(s)'); +>new Error('Unexpected error: Invalid job name(s)') : Error +>Error : ErrorConstructor +>'Unexpected error: Invalid job name(s)' : "Unexpected error: Invalid job name(s)" + } +} + +// File: angular/packages/common/src/pipes/case_conversion_pipes.ts + +function transform1(value: T): HelperCond { +>transform1 : (value: T) => HelperCond +>value : T + + if (value == null) return null; // Ok +>value == null : boolean +>value : T + + if (typeof value !== 'string') { +>typeof value !== 'string' : boolean +>typeof value : "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" +>value : NonNullable +>'string' : "string" + + throw new Error(); +>new Error() : Error +>Error : ErrorConstructor + } + return value.toLowerCase(); // Ok +>value.toLowerCase() : string +>value.toLowerCase : () => string +>value : string +>toLowerCase : () => string +} diff --git a/tsc/testdata/baselines/reference/compiler/dependentReturnType4.errors.txt b/tsc/testdata/baselines/reference/compiler/dependentReturnType4.errors.txt new file mode 100644 index 0000000000000..5f06601f9fe98 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/dependentReturnType4.errors.txt @@ -0,0 +1,40 @@ +dependentReturnType4.ts(28,9): error TS2322: Type '0' is not assignable to type 'T extends undefined ? 0 : T extends string ? 1 : never'. +dependentReturnType4.ts(30,5): error TS2322: Type '1' is not assignable to type 'T extends undefined ? 0 : T extends string ? 1 : never'. + + +==== dependentReturnType4.ts (2 errors) ==== + declare const rand: { a?: never }; + type Missing = typeof rand.a; + + // Detection of valid optional parameter references + + // Ok, will narrow return type + function bar1(x?: T): + T extends Missing ? 0 : T extends string ? 1 : never { + if (x === undefined) { + return 0; + } + return 1; + } + + // Ok, will narrow return type + function bar2(x?: T): + T extends undefined ? 0 : T extends string ? 1 : never { + if (x === undefined) { + return 0; + } + return 1; + } + + // Not ok, will not narrow return type + function bar3(x?: T): + T extends undefined ? 0 : T extends string ? 1 : never { + if (x === undefined) { + return 0; + ~~~~~~ +!!! error TS2322: Type '0' is not assignable to type 'T extends undefined ? 0 : T extends string ? 1 : never'. + } + return 1; + ~~~~~~ +!!! error TS2322: Type '1' is not assignable to type 'T extends undefined ? 0 : T extends string ? 1 : never'. + } \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/dependentReturnType4.symbols b/tsc/testdata/baselines/reference/compiler/dependentReturnType4.symbols new file mode 100644 index 0000000000000..77b99a29dd972 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/dependentReturnType4.symbols @@ -0,0 +1,76 @@ +//// [tests/cases/compiler/dependentReturnType4.ts] //// + +=== dependentReturnType4.ts === +declare const rand: { a?: never }; +>rand : Symbol(rand, Decl(dependentReturnType4.ts, 0, 13)) +>a : Symbol(a, Decl(dependentReturnType4.ts, 0, 21)) + +type Missing = typeof rand.a; +>Missing : Symbol(Missing, Decl(dependentReturnType4.ts, 0, 34)) +>rand.a : Symbol(a, Decl(dependentReturnType4.ts, 0, 21)) +>rand : Symbol(rand, Decl(dependentReturnType4.ts, 0, 13)) +>a : Symbol(a, Decl(dependentReturnType4.ts, 0, 21)) + +// Detection of valid optional parameter references + +// Ok, will narrow return type +function bar1(x?: T): +>bar1 : Symbol(bar1, Decl(dependentReturnType4.ts, 1, 29)) +>T : Symbol(T, Decl(dependentReturnType4.ts, 6, 14)) +>Missing : Symbol(Missing, Decl(dependentReturnType4.ts, 0, 34)) +>x : Symbol(x, Decl(dependentReturnType4.ts, 6, 42)) +>T : Symbol(T, Decl(dependentReturnType4.ts, 6, 14)) + + T extends Missing ? 0 : T extends string ? 1 : never { +>T : Symbol(T, Decl(dependentReturnType4.ts, 6, 14)) +>Missing : Symbol(Missing, Decl(dependentReturnType4.ts, 0, 34)) +>T : Symbol(T, Decl(dependentReturnType4.ts, 6, 14)) + + if (x === undefined) { +>x : Symbol(x, Decl(dependentReturnType4.ts, 6, 42)) +>undefined : Symbol(undefined) + + return 0; + } + return 1; +} + +// Ok, will narrow return type +function bar2(x?: T): +>bar2 : Symbol(bar2, Decl(dependentReturnType4.ts, 12, 1)) +>T : Symbol(T, Decl(dependentReturnType4.ts, 15, 14)) +>x : Symbol(x, Decl(dependentReturnType4.ts, 15, 44)) +>T : Symbol(T, Decl(dependentReturnType4.ts, 15, 14)) + + T extends undefined ? 0 : T extends string ? 1 : never { +>T : Symbol(T, Decl(dependentReturnType4.ts, 15, 14)) +>T : Symbol(T, Decl(dependentReturnType4.ts, 15, 14)) + + if (x === undefined) { +>x : Symbol(x, Decl(dependentReturnType4.ts, 15, 44)) +>undefined : Symbol(undefined) + + return 0; + } + return 1; +} + +// Not ok, will not narrow return type +function bar3(x?: T): +>bar3 : Symbol(bar3, Decl(dependentReturnType4.ts, 21, 1)) +>T : Symbol(T, Decl(dependentReturnType4.ts, 24, 14)) +>x : Symbol(x, Decl(dependentReturnType4.ts, 24, 32)) +>T : Symbol(T, Decl(dependentReturnType4.ts, 24, 14)) + + T extends undefined ? 0 : T extends string ? 1 : never { +>T : Symbol(T, Decl(dependentReturnType4.ts, 24, 14)) +>T : Symbol(T, Decl(dependentReturnType4.ts, 24, 14)) + + if (x === undefined) { +>x : Symbol(x, Decl(dependentReturnType4.ts, 24, 32)) +>undefined : Symbol(undefined) + + return 0; + } + return 1; +} diff --git a/tsc/testdata/baselines/reference/compiler/dependentReturnType4.types b/tsc/testdata/baselines/reference/compiler/dependentReturnType4.types new file mode 100644 index 0000000000000..14540358a9623 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/dependentReturnType4.types @@ -0,0 +1,68 @@ +//// [tests/cases/compiler/dependentReturnType4.ts] //// + +=== dependentReturnType4.ts === +declare const rand: { a?: never }; +>rand : { a?: never; } +>a : undefined + +type Missing = typeof rand.a; +>Missing : undefined +>rand.a : undefined +>rand : { a?: never; } +>a : undefined + +// Detection of valid optional parameter references + +// Ok, will narrow return type +function bar1(x?: T): +>bar1 : (x?: T) => T extends Missing ? 0 : T extends string ? 1 : never +>x : T | undefined + + T extends Missing ? 0 : T extends string ? 1 : never { + if (x === undefined) { +>x === undefined : boolean +>x : T | undefined +>undefined : undefined + + return 0; +>0 : 0 + } + return 1; +>1 : 1 +} + +// Ok, will narrow return type +function bar2(x?: T): +>bar2 : (x?: T) => T extends undefined ? 0 : T extends string ? 1 : never +>x : T | undefined + + T extends undefined ? 0 : T extends string ? 1 : never { + if (x === undefined) { +>x === undefined : boolean +>x : T | undefined +>undefined : undefined + + return 0; +>0 : 0 + } + return 1; +>1 : 1 +} + +// Not ok, will not narrow return type +function bar3(x?: T): +>bar3 : (x?: T) => T extends undefined ? 0 : T extends string ? 1 : never +>x : T | undefined + + T extends undefined ? 0 : T extends string ? 1 : never { + if (x === undefined) { +>x === undefined : boolean +>x : T | undefined +>undefined : undefined + + return 0; +>0 : 0 + } + return 1; +>1 : 1 +} diff --git a/tsc/testdata/baselines/reference/compiler/dependentReturnType5.errors.txt b/tsc/testdata/baselines/reference/compiler/dependentReturnType5.errors.txt new file mode 100644 index 0000000000000..115237668d77c --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/dependentReturnType5.errors.txt @@ -0,0 +1,109 @@ +dependentReturnType5.ts(54,13): error TS2322: Type '2' is not assignable to type 'Comp[T]'. + Type '2' is not assignable to type '3'. +dependentReturnType5.ts(65,5): error TS2322: Type '2' is not assignable to type 'Comp[T]'. + Type '2' is not assignable to type '3'. + + +==== dependentReturnType5.ts (2 errors) ==== + // Indexed access return type + interface A1 { + "prop": true; + [s: string]: boolean; + } + + // This was already allowed but is unsound. + function foo1(x: T): A1[T] { + return false; + } + const rfoo1 = foo1("prop"); // Type says true, but actually returns false. + + interface A2 { + "prop": true; + [n: number]: string; + } + + // We could soundly allow that, because `"prop"` and `[n: number]` are disjoint types. + function foo2(x: T): A2[T] { + if (x === "prop") { + return true; + } + return "some string"; + } + const rfoo2 = foo2("prop"); + const rfoo22 = foo2(34); + const rfoo222 = foo2(Math.random() ? "prop" : 34); + + interface A3 { + [s: string]: boolean; + } + + // No need for return type narrowing. + function foo3(x: T): A3[T] { + if (Math.random()) return true; + return false; + } + + interface Comp { + foo: 2; + [n: number]: 3; + [s: string]: 2 | 3 | 4; + } + + function indexedComp(x: T): Comp[T] { + if (x === "foo") { + if (Math.random()) { + return 3; // Should be error, but already allowed + } + return 2; // Ok + } + if (typeof x === "number") { + if (Math.random()) { + return 2; // Error + ~~~~~~ +!!! error TS2322: Type '2' is not assignable to type 'Comp[T]'. +!!! error TS2322: Type '2' is not assignable to type '3'. + } + return 3; // // Should be error, but already allowed + } + return 4; // Ok + } + + function indexedComp2(x: T): Comp[T] { + if (Math.random()) { + return 3; // Bad, unsound + } + return 2; // Error + ~~~~~~ +!!! error TS2322: Type '2' is not assignable to type 'Comp[T]'. +!!! error TS2322: Type '2' is not assignable to type '3'. + } + + + // Most common case supported: + interface F { + "t": number, + "f": boolean, + } + + // Ok + function depLikeFun(str: T): F[T] { + if (str === "t") { + return 1; + } else { + return true; + } + } + + depLikeFun("t"); // has type number + depLikeFun("f"); // has type boolean + + type IndirectF = F[T]; + + // Ok + function depLikeFun2(str: T): IndirectF { + if (str === "t") { + return 1; + } else { + return true; + } + } \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/dependentReturnType5.symbols b/tsc/testdata/baselines/reference/compiler/dependentReturnType5.symbols new file mode 100644 index 0000000000000..6351f67f2523e --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/dependentReturnType5.symbols @@ -0,0 +1,220 @@ +//// [tests/cases/compiler/dependentReturnType5.ts] //// + +=== dependentReturnType5.ts === +// Indexed access return type +interface A1 { +>A1 : Symbol(A1, Decl(dependentReturnType5.ts, 0, 0)) + + "prop": true; +>"prop" : Symbol(A1["prop"], Decl(dependentReturnType5.ts, 1, 14)) + + [s: string]: boolean; +>s : Symbol(s, Decl(dependentReturnType5.ts, 3, 5)) +} + +// This was already allowed but is unsound. +function foo1(x: T): A1[T] { +>foo1 : Symbol(foo1, Decl(dependentReturnType5.ts, 4, 1)) +>T : Symbol(T, Decl(dependentReturnType5.ts, 7, 14)) +>x : Symbol(x, Decl(dependentReturnType5.ts, 7, 32)) +>T : Symbol(T, Decl(dependentReturnType5.ts, 7, 14)) +>A1 : Symbol(A1, Decl(dependentReturnType5.ts, 0, 0)) +>T : Symbol(T, Decl(dependentReturnType5.ts, 7, 14)) + + return false; +} +const rfoo1 = foo1("prop"); // Type says true, but actually returns false. +>rfoo1 : Symbol(rfoo1, Decl(dependentReturnType5.ts, 10, 5)) +>foo1 : Symbol(foo1, Decl(dependentReturnType5.ts, 4, 1)) + +interface A2 { +>A2 : Symbol(A2, Decl(dependentReturnType5.ts, 10, 27)) + + "prop": true; +>"prop" : Symbol(A2["prop"], Decl(dependentReturnType5.ts, 12, 14)) + + [n: number]: string; +>n : Symbol(n, Decl(dependentReturnType5.ts, 14, 5)) +} + +// We could soundly allow that, because `"prop"` and `[n: number]` are disjoint types. +function foo2(x: T): A2[T] { +>foo2 : Symbol(foo2, Decl(dependentReturnType5.ts, 15, 1)) +>T : Symbol(T, Decl(dependentReturnType5.ts, 18, 14)) +>x : Symbol(x, Decl(dependentReturnType5.ts, 18, 41)) +>T : Symbol(T, Decl(dependentReturnType5.ts, 18, 14)) +>A2 : Symbol(A2, Decl(dependentReturnType5.ts, 10, 27)) +>T : Symbol(T, Decl(dependentReturnType5.ts, 18, 14)) + + if (x === "prop") { +>x : Symbol(x, Decl(dependentReturnType5.ts, 18, 41)) + + return true; + } + return "some string"; +} +const rfoo2 = foo2("prop"); +>rfoo2 : Symbol(rfoo2, Decl(dependentReturnType5.ts, 24, 5)) +>foo2 : Symbol(foo2, Decl(dependentReturnType5.ts, 15, 1)) + +const rfoo22 = foo2(34); +>rfoo22 : Symbol(rfoo22, Decl(dependentReturnType5.ts, 25, 5)) +>foo2 : Symbol(foo2, Decl(dependentReturnType5.ts, 15, 1)) + +const rfoo222 = foo2(Math.random() ? "prop" : 34); +>rfoo222 : Symbol(rfoo222, Decl(dependentReturnType5.ts, 26, 5)) +>foo2 : Symbol(foo2, Decl(dependentReturnType5.ts, 15, 1)) +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2025.float16.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) + +interface A3 { +>A3 : Symbol(A3, Decl(dependentReturnType5.ts, 26, 50)) + + [s: string]: boolean; +>s : Symbol(s, Decl(dependentReturnType5.ts, 29, 5)) +} + +// No need for return type narrowing. +function foo3(x: T): A3[T] { +>foo3 : Symbol(foo3, Decl(dependentReturnType5.ts, 30, 1)) +>T : Symbol(T, Decl(dependentReturnType5.ts, 33, 14)) +>x : Symbol(x, Decl(dependentReturnType5.ts, 33, 32)) +>T : Symbol(T, Decl(dependentReturnType5.ts, 33, 14)) +>A3 : Symbol(A3, Decl(dependentReturnType5.ts, 26, 50)) +>T : Symbol(T, Decl(dependentReturnType5.ts, 33, 14)) + + if (Math.random()) return true; +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2025.float16.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) + + return false; +} + +interface Comp { +>Comp : Symbol(Comp, Decl(dependentReturnType5.ts, 36, 1)) + + foo: 2; +>foo : Symbol(Comp.foo, Decl(dependentReturnType5.ts, 38, 16)) + + [n: number]: 3; +>n : Symbol(n, Decl(dependentReturnType5.ts, 40, 5)) + + [s: string]: 2 | 3 | 4; +>s : Symbol(s, Decl(dependentReturnType5.ts, 41, 5)) +} + +function indexedComp(x: T): Comp[T] { +>indexedComp : Symbol(indexedComp, Decl(dependentReturnType5.ts, 42, 1)) +>T : Symbol(T, Decl(dependentReturnType5.ts, 44, 21)) +>x : Symbol(x, Decl(dependentReturnType5.ts, 44, 48)) +>T : Symbol(T, Decl(dependentReturnType5.ts, 44, 21)) +>Comp : Symbol(Comp, Decl(dependentReturnType5.ts, 36, 1)) +>T : Symbol(T, Decl(dependentReturnType5.ts, 44, 21)) + + if (x === "foo") { +>x : Symbol(x, Decl(dependentReturnType5.ts, 44, 48)) + + if (Math.random()) { +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2025.float16.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) + + return 3; // Should be error, but already allowed + } + return 2; // Ok + } + if (typeof x === "number") { +>x : Symbol(x, Decl(dependentReturnType5.ts, 44, 48)) + + if (Math.random()) { +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2025.float16.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) + + return 2; // Error + } + return 3; // // Should be error, but already allowed + } + return 4; // Ok +} + +function indexedComp2(x: T): Comp[T] { +>indexedComp2 : Symbol(indexedComp2, Decl(dependentReturnType5.ts, 58, 1)) +>T : Symbol(T, Decl(dependentReturnType5.ts, 60, 22)) +>x : Symbol(x, Decl(dependentReturnType5.ts, 60, 49)) +>T : Symbol(T, Decl(dependentReturnType5.ts, 60, 22)) +>Comp : Symbol(Comp, Decl(dependentReturnType5.ts, 36, 1)) +>T : Symbol(T, Decl(dependentReturnType5.ts, 60, 22)) + + if (Math.random()) { +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2025.float16.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) + + return 3; // Bad, unsound + } + return 2; // Error +} + + +// Most common case supported: +interface F { +>F : Symbol(F, Decl(dependentReturnType5.ts, 65, 1)) + + "t": number, +>"t" : Symbol(F["t"], Decl(dependentReturnType5.ts, 69, 13)) + + "f": boolean, +>"f" : Symbol(F["f"], Decl(dependentReturnType5.ts, 70, 16)) +} + +// Ok +function depLikeFun(str: T): F[T] { +>depLikeFun : Symbol(depLikeFun, Decl(dependentReturnType5.ts, 72, 1)) +>T : Symbol(T, Decl(dependentReturnType5.ts, 75, 20)) +>str : Symbol(str, Decl(dependentReturnType5.ts, 75, 41)) +>T : Symbol(T, Decl(dependentReturnType5.ts, 75, 20)) +>F : Symbol(F, Decl(dependentReturnType5.ts, 65, 1)) +>T : Symbol(T, Decl(dependentReturnType5.ts, 75, 20)) + + if (str === "t") { +>str : Symbol(str, Decl(dependentReturnType5.ts, 75, 41)) + + return 1; + } else { + return true; + } +} + +depLikeFun("t"); // has type number +>depLikeFun : Symbol(depLikeFun, Decl(dependentReturnType5.ts, 72, 1)) + +depLikeFun("f"); // has type boolean +>depLikeFun : Symbol(depLikeFun, Decl(dependentReturnType5.ts, 72, 1)) + +type IndirectF = F[T]; +>IndirectF : Symbol(IndirectF, Decl(dependentReturnType5.ts, 84, 16)) +>T : Symbol(T, Decl(dependentReturnType5.ts, 86, 15)) +>F : Symbol(F, Decl(dependentReturnType5.ts, 65, 1)) +>F : Symbol(F, Decl(dependentReturnType5.ts, 65, 1)) +>T : Symbol(T, Decl(dependentReturnType5.ts, 86, 15)) + +// Ok +function depLikeFun2(str: T): IndirectF { +>depLikeFun2 : Symbol(depLikeFun2, Decl(dependentReturnType5.ts, 86, 41)) +>T : Symbol(T, Decl(dependentReturnType5.ts, 89, 21)) +>str : Symbol(str, Decl(dependentReturnType5.ts, 89, 42)) +>T : Symbol(T, Decl(dependentReturnType5.ts, 89, 21)) +>IndirectF : Symbol(IndirectF, Decl(dependentReturnType5.ts, 84, 16)) +>T : Symbol(T, Decl(dependentReturnType5.ts, 89, 21)) + + if (str === "t") { +>str : Symbol(str, Decl(dependentReturnType5.ts, 89, 42)) + + return 1; + } else { + return true; + } +} diff --git a/tsc/testdata/baselines/reference/compiler/dependentReturnType5.types b/tsc/testdata/baselines/reference/compiler/dependentReturnType5.types new file mode 100644 index 0000000000000..6caa64688965d --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/dependentReturnType5.types @@ -0,0 +1,228 @@ +//// [tests/cases/compiler/dependentReturnType5.ts] //// + +=== dependentReturnType5.ts === +// Indexed access return type +interface A1 { + "prop": true; +>"prop" : true +>true : true + + [s: string]: boolean; +>s : string +} + +// This was already allowed but is unsound. +function foo1(x: T): A1[T] { +>foo1 : (x: T) => A1[T] +>x : T + + return false; +>false : false +} +const rfoo1 = foo1("prop"); // Type says true, but actually returns false. +>rfoo1 : true +>foo1("prop") : true +>foo1 : (x: T) => A1[T] +>"prop" : "prop" + +interface A2 { + "prop": true; +>"prop" : true +>true : true + + [n: number]: string; +>n : number +} + +// We could soundly allow that, because `"prop"` and `[n: number]` are disjoint types. +function foo2(x: T): A2[T] { +>foo2 : (x: T) => A2[T] +>x : T + + if (x === "prop") { +>x === "prop" : boolean +>x : T +>"prop" : "prop" + + return true; +>true : true + } + return "some string"; +>"some string" : "some string" +} +const rfoo2 = foo2("prop"); +>rfoo2 : true +>foo2("prop") : true +>foo2 : (x: T) => A2[T] +>"prop" : "prop" + +const rfoo22 = foo2(34); +>rfoo22 : string +>foo2(34) : string +>foo2 : (x: T) => A2[T] +>34 : 34 + +const rfoo222 = foo2(Math.random() ? "prop" : 34); +>rfoo222 : string | true +>foo2(Math.random() ? "prop" : 34) : string | true +>foo2 : (x: T) => A2[T] +>Math.random() ? "prop" : 34 : "prop" | 34 +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number +>"prop" : "prop" +>34 : 34 + +interface A3 { + [s: string]: boolean; +>s : string +} + +// No need for return type narrowing. +function foo3(x: T): A3[T] { +>foo3 : (x: T) => A3[T] +>x : T + + if (Math.random()) return true; +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number +>true : true + + return false; +>false : false +} + +interface Comp { + foo: 2; +>foo : 2 + + [n: number]: 3; +>n : number + + [s: string]: 2 | 3 | 4; +>s : string +} + +function indexedComp(x: T): Comp[T] { +>indexedComp : (x: T) => Comp[T] +>x : T + + if (x === "foo") { +>x === "foo" : boolean +>x : T +>"foo" : "foo" + + if (Math.random()) { +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number + + return 3; // Should be error, but already allowed +>3 : 3 + } + return 2; // Ok +>2 : 2 + } + if (typeof x === "number") { +>typeof x === "number" : boolean +>typeof x : "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" +>x : T +>"number" : "number" + + if (Math.random()) { +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number + + return 2; // Error +>2 : 2 + } + return 3; // // Should be error, but already allowed +>3 : 3 + } + return 4; // Ok +>4 : 4 +} + +function indexedComp2(x: T): Comp[T] { +>indexedComp2 : (x: T) => Comp[T] +>x : T + + if (Math.random()) { +>Math.random() : number +>Math.random : () => number +>Math : Math +>random : () => number + + return 3; // Bad, unsound +>3 : 3 + } + return 2; // Error +>2 : 2 +} + + +// Most common case supported: +interface F { + "t": number, +>"t" : number + + "f": boolean, +>"f" : boolean +} + +// Ok +function depLikeFun(str: T): F[T] { +>depLikeFun : (str: T) => F[T] +>str : T + + if (str === "t") { +>str === "t" : boolean +>str : T +>"t" : "t" + + return 1; +>1 : 1 + + } else { + return true; +>true : true + } +} + +depLikeFun("t"); // has type number +>depLikeFun("t") : number +>depLikeFun : (str: T) => F[T] +>"t" : "t" + +depLikeFun("f"); // has type boolean +>depLikeFun("f") : boolean +>depLikeFun : (str: T) => F[T] +>"f" : "f" + +type IndirectF = F[T]; +>IndirectF : IndirectF + +// Ok +function depLikeFun2(str: T): IndirectF { +>depLikeFun2 : (str: T) => IndirectF +>str : T + + if (str === "t") { +>str === "t" : boolean +>str : T +>"t" : "t" + + return 1; +>1 : 1 + + } else { + return true; +>true : true + } +} diff --git a/tsc/testdata/baselines/reference/compiler/dependentReturnType6.errors.txt b/tsc/testdata/baselines/reference/compiler/dependentReturnType6.errors.txt new file mode 100644 index 0000000000000..638c23268ad80 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/dependentReturnType6.errors.txt @@ -0,0 +1,193 @@ +file.ts(28,26): error TS2322: Type 'true' is not assignable to type 'SomeInterfaceBad[U]'. + Type 'true' is not assignable to type 'T extends 1 ? true : T extends 2 ? false : never'. +file.ts(28,33): error TS2322: Type 'false' is not assignable to type 'SomeInterfaceBad[U]'. + Type 'false' is not assignable to type 'T extends 1 ? true : T extends 2 ? false : never'. +file.ts(30,16): error TS2322: Type '1' is not assignable to type 'SomeInterfaceBad[U]'. + Type '1' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'. +file.ts(30,20): error TS2322: Type '2' is not assignable to type 'SomeInterfaceBad[U]'. + Type '2' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'. +file.ts(80,13): error TS2322: Type '1' is not assignable to type 'this extends Sub1 ? 1 : this extends Sub2 ? 2 : never'. +file.ts(82,9): error TS2322: Type '2' is not assignable to type 'this extends Sub1 ? 1 : this extends Sub2 ? 2 : never'. +file.ts(94,16): error TS2322: Type '1' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'. +file.ts(94,20): error TS2322: Type '2' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'. +file.ts(98,100): error TS2322: Type '1 | 2' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'. + Type '1' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'. +file.ts(106,9): error TS2322: Type 'number' is not assignable to type 'SomeCond'. +file.ts(106,9): error TS2589: Type instantiation is excessively deep and possibly infinite. +file.ts(108,5): error TS2322: Type 'number' is not assignable to type 'SomeCond'. +file.ts(114,60): error TS2366: Function lacks ending return statement and return type does not include 'undefined'. +file.ts(116,9): error TS2322: Type '"one"' is not assignable to type 'OtherCond | OtherCond'. +file.ts(126,9): error TS2322: Type '"a"' is not assignable to type 'T extends (infer P)[] ? P : T extends number ? undefined : never'. +file.ts(128,5): error TS2322: Type 'undefined' is not assignable to type 'T extends (infer P)[] ? P : T extends number ? undefined : never'. + + +==== file.ts (16 errors) ==== + // Type parameter in outer scope + function outer(x: T): number { + return inner(); + + function inner(): T extends true ? 1 : T extends false ? 2 : never { + return x ? 1 : 2; + } + } + + // Overloads + function fun6(x: T, y: string): T extends true ? string : T extends false ? 2 : never; + function fun6(x: T, y: undefined): T extends true ? 1 : T extends false ? 2 : never; + function fun6(x: boolean): 1 | 2 | string; + function fun6(x: T, y?: string): T extends true ? 1 | string : T extends false ? 2 : never { + return x ? y !== undefined ? y : 1 : 2; + } + + // Indexed access with conditional inside + + // DOESN'T NARROW the nested conditional type of wrong shape + interface SomeInterfaceBad { + prop1: T extends 1 ? true : T extends 2 ? false : never; + prop2: T extends true ? 1 : T extends false ? 2 : never; + } + + function fun4bad>(x: T, y: U): SomeInterfaceBad[U] { + if (y === "prop1") { + return x === 1 ? true : false; + ~~~~ +!!! error TS2322: Type 'true' is not assignable to type 'SomeInterfaceBad[U]'. +!!! error TS2322: Type 'true' is not assignable to type 'T extends 1 ? true : T extends 2 ? false : never'. + ~~~~~ +!!! error TS2322: Type 'false' is not assignable to type 'SomeInterfaceBad[U]'. +!!! error TS2322: Type 'false' is not assignable to type 'T extends 1 ? true : T extends 2 ? false : never'. + } + return x ? 1 : 2; + ~ +!!! error TS2322: Type '1' is not assignable to type 'SomeInterfaceBad[U]'. +!!! error TS2322: Type '1' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'. + ~ +!!! error TS2322: Type '2' is not assignable to type 'SomeInterfaceBad[U]'. +!!! error TS2322: Type '2' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'. + } + + // Narrows nested conditional type of right shape + interface SomeInterfaceGood { + prop1: T extends true ? 2 : T extends false ? 1 : never; + prop2: T extends true ? 1 : T extends false ? 2 : never; + } + + function fun4good>(x: T, y: U): SomeInterfaceGood[U] { + if (y === "prop1") { + return x ? 2 : 1; + } + return x ? 1 : 2; + } + + // Indexed access with indexed access inside - OK, narrows + interface BB { + "a": number; + "b": string; + } + + interface AA { + "c": BB[T]; + "d": boolean, + } + + function reduction>(x: T, y: U): AA[U] { + if (x === "a" && y === "c") { + return 0; // Ok + } + + return undefined as never; + } + + // Conditional with indexed access inside - OK, narrows + function fun5(x: T, y: U): T extends 1 ? BB[U] : T extends 2 ? boolean : never { + if (x === 1) { + if (y === "a") { + return 0; + } + return ""; + } + return true; + } + + // `this` type parameter - Doesn't narrow + abstract class SomeClass { + fun3(this: Sub1 | Sub2): this extends Sub1 ? 1 : this extends Sub2 ? 2 : never { + if (this instanceof Sub1) { + return 1; + ~~~~~~ +!!! error TS2322: Type '1' is not assignable to type 'this extends Sub1 ? 1 : this extends Sub2 ? 2 : never'. + } + return 2; + ~~~~~~ +!!! error TS2322: Type '2' is not assignable to type 'this extends Sub1 ? 1 : this extends Sub2 ? 2 : never'. + } + } + class Sub1 extends SomeClass { + #sub1!: symbol; + }; + class Sub2 extends SomeClass { + #sub2!: symbol; + }; + + // Detection of type parameter reference in presence of typeof + function fun2(x: T, y: typeof x): T extends true ? 1 : T extends false ? 2 : never { + return x ? 1 : 2; + ~ +!!! error TS2322: Type '1' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'. + ~ +!!! error TS2322: Type '2' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'. + } + + // Contextually-typed lambdas + const fun1: (x: T) => T extends true ? 1 : T extends false ? 2 : never = (x) => x ? 1 : 2; + ~~~~~~~~~ +!!! error TS2322: Type '1 | 2' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'. +!!! error TS2322: Type '1' is not assignable to type 'T extends true ? 1 : T extends false ? 2 : never'. +!!! related TS6502 file.ts:98:13: The expected type comes from the return type of this signature. + + + // Circular conditionals + type SomeCond = T extends true ? 1 : T extends false ? SomeCond : never; + + function f7(x: T): SomeCond { + if (x) { + return 1; + ~~~~~~ +!!! error TS2322: Type 'number' is not assignable to type 'SomeCond'. + ~~~~~~ +!!! error TS2589: Type instantiation is excessively deep and possibly infinite. + } + return 2; + ~~~~~~ +!!! error TS2322: Type 'number' is not assignable to type 'SomeCond'. + } + + // Composite instantiation of conditional type + type OtherCond = T extends 1 ? "one" : T extends 2 ? "two" : T extends 3 ? "three" : T extends 4 ? "four" : never; + + function f8(x: U, y: V): OtherCond { + ~~~~~~~~~~~~~~~~ +!!! error TS2366: Function lacks ending return statement and return type does not include 'undefined'. + if (x === 1 && y === 3) { + return "one"; + ~~~~~~ +!!! error TS2322: Type '"one"' is not assignable to type 'OtherCond | OtherCond'. + } + } + + // Conditionals with `infer` - will not narrow, it is not safe to infer from the narrowed type into an `infer` type parameter + function f9(x: T): T extends Array ? P : T extends number ? undefined : never { + if (Array.isArray(x)) { + // If we allowed narrowing of the conditional return type, when resolving the conditional `T & ("a"[] | "b"[]) extends Array ? P : ...`, + // we could infer `"a" | "b"` for `P`, and allow "a" to be returned. However, when calling `f10`, `T` could be instantiated with `"b"[]`, and the return type would be `"b"`, + // so allowing an `"a"` return would be unsound. + return "a"; + ~~~~~~ +!!! error TS2322: Type '"a"' is not assignable to type 'T extends (infer P)[] ? P : T extends number ? undefined : never'. + } + return undefined; + ~~~~~~ +!!! error TS2322: Type 'undefined' is not assignable to type 'T extends (infer P)[] ? P : T extends number ? undefined : never'. + } + + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/dependentReturnType6.symbols b/tsc/testdata/baselines/reference/compiler/dependentReturnType6.symbols new file mode 100644 index 0000000000000..44fc28739e099 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/dependentReturnType6.symbols @@ -0,0 +1,367 @@ +//// [tests/cases/compiler/dependentReturnType6.ts] //// + +=== file.ts === +// Type parameter in outer scope +function outer(x: T): number { +>outer : Symbol(outer, Decl(file.ts, 0, 0)) +>T : Symbol(T, Decl(file.ts, 1, 15)) +>x : Symbol(x, Decl(file.ts, 1, 34)) +>T : Symbol(T, Decl(file.ts, 1, 15)) + + return inner(); +>inner : Symbol(inner, Decl(file.ts, 2, 19)) + + function inner(): T extends true ? 1 : T extends false ? 2 : never { +>inner : Symbol(inner, Decl(file.ts, 2, 19)) +>T : Symbol(T, Decl(file.ts, 1, 15)) +>T : Symbol(T, Decl(file.ts, 1, 15)) + + return x ? 1 : 2; +>x : Symbol(x, Decl(file.ts, 1, 34)) + } +} + +// Overloads +function fun6(x: T, y: string): T extends true ? string : T extends false ? 2 : never; +>fun6 : Symbol(fun6, Decl(file.ts, 7, 1), Decl(file.ts, 10, 105), Decl(file.ts, 11, 103), Decl(file.ts, 12, 42)) +>T : Symbol(T, Decl(file.ts, 10, 14)) +>x : Symbol(x, Decl(file.ts, 10, 33)) +>T : Symbol(T, Decl(file.ts, 10, 14)) +>y : Symbol(y, Decl(file.ts, 10, 38)) +>T : Symbol(T, Decl(file.ts, 10, 14)) +>T : Symbol(T, Decl(file.ts, 10, 14)) + +function fun6(x: T, y: undefined): T extends true ? 1 : T extends false ? 2 : never; +>fun6 : Symbol(fun6, Decl(file.ts, 7, 1), Decl(file.ts, 10, 105), Decl(file.ts, 11, 103), Decl(file.ts, 12, 42)) +>T : Symbol(T, Decl(file.ts, 11, 14)) +>x : Symbol(x, Decl(file.ts, 11, 33)) +>T : Symbol(T, Decl(file.ts, 11, 14)) +>y : Symbol(y, Decl(file.ts, 11, 38)) +>T : Symbol(T, Decl(file.ts, 11, 14)) +>T : Symbol(T, Decl(file.ts, 11, 14)) + +function fun6(x: boolean): 1 | 2 | string; +>fun6 : Symbol(fun6, Decl(file.ts, 7, 1), Decl(file.ts, 10, 105), Decl(file.ts, 11, 103), Decl(file.ts, 12, 42)) +>x : Symbol(x, Decl(file.ts, 12, 14)) + +function fun6(x: T, y?: string): T extends true ? 1 | string : T extends false ? 2 : never { +>fun6 : Symbol(fun6, Decl(file.ts, 7, 1), Decl(file.ts, 10, 105), Decl(file.ts, 11, 103), Decl(file.ts, 12, 42)) +>T : Symbol(T, Decl(file.ts, 13, 14)) +>x : Symbol(x, Decl(file.ts, 13, 33)) +>T : Symbol(T, Decl(file.ts, 13, 14)) +>y : Symbol(y, Decl(file.ts, 13, 38)) +>T : Symbol(T, Decl(file.ts, 13, 14)) +>T : Symbol(T, Decl(file.ts, 13, 14)) + + return x ? y !== undefined ? y : 1 : 2; +>x : Symbol(x, Decl(file.ts, 13, 33)) +>y : Symbol(y, Decl(file.ts, 13, 38)) +>undefined : Symbol(undefined) +>y : Symbol(y, Decl(file.ts, 13, 38)) +} + +// Indexed access with conditional inside + +// DOESN'T NARROW the nested conditional type of wrong shape +interface SomeInterfaceBad { +>SomeInterfaceBad : Symbol(SomeInterfaceBad, Decl(file.ts, 15, 1)) +>T : Symbol(T, Decl(file.ts, 20, 27)) + + prop1: T extends 1 ? true : T extends 2 ? false : never; +>prop1 : Symbol(SomeInterfaceBad.prop1, Decl(file.ts, 20, 31)) +>T : Symbol(T, Decl(file.ts, 20, 27)) +>T : Symbol(T, Decl(file.ts, 20, 27)) + + prop2: T extends true ? 1 : T extends false ? 2 : never; +>prop2 : Symbol(SomeInterfaceBad.prop2, Decl(file.ts, 21, 60)) +>T : Symbol(T, Decl(file.ts, 20, 27)) +>T : Symbol(T, Decl(file.ts, 20, 27)) +} + +function fun4bad>(x: T, y: U): SomeInterfaceBad[U] { +>fun4bad : Symbol(fun4bad, Decl(file.ts, 23, 1)) +>T : Symbol(T, Decl(file.ts, 25, 17)) +>U : Symbol(U, Decl(file.ts, 25, 19)) +>SomeInterfaceBad : Symbol(SomeInterfaceBad, Decl(file.ts, 15, 1)) +>x : Symbol(x, Decl(file.ts, 25, 63)) +>T : Symbol(T, Decl(file.ts, 25, 17)) +>y : Symbol(y, Decl(file.ts, 25, 68)) +>U : Symbol(U, Decl(file.ts, 25, 19)) +>SomeInterfaceBad : Symbol(SomeInterfaceBad, Decl(file.ts, 15, 1)) +>T : Symbol(T, Decl(file.ts, 25, 17)) +>U : Symbol(U, Decl(file.ts, 25, 19)) + + if (y === "prop1") { +>y : Symbol(y, Decl(file.ts, 25, 68)) + + return x === 1 ? true : false; +>x : Symbol(x, Decl(file.ts, 25, 63)) + } + return x ? 1 : 2; +>x : Symbol(x, Decl(file.ts, 25, 63)) +} + +// Narrows nested conditional type of right shape +interface SomeInterfaceGood { +>SomeInterfaceGood : Symbol(SomeInterfaceGood, Decl(file.ts, 30, 1)) +>T : Symbol(T, Decl(file.ts, 33, 28)) + + prop1: T extends true ? 2 : T extends false ? 1 : never; +>prop1 : Symbol(SomeInterfaceGood.prop1, Decl(file.ts, 33, 32)) +>T : Symbol(T, Decl(file.ts, 33, 28)) +>T : Symbol(T, Decl(file.ts, 33, 28)) + + prop2: T extends true ? 1 : T extends false ? 2 : never; +>prop2 : Symbol(SomeInterfaceGood.prop2, Decl(file.ts, 34, 60)) +>T : Symbol(T, Decl(file.ts, 33, 28)) +>T : Symbol(T, Decl(file.ts, 33, 28)) +} + +function fun4good>(x: T, y: U): SomeInterfaceGood[U] { +>fun4good : Symbol(fun4good, Decl(file.ts, 36, 1)) +>T : Symbol(T, Decl(file.ts, 38, 18)) +>U : Symbol(U, Decl(file.ts, 38, 36)) +>SomeInterfaceGood : Symbol(SomeInterfaceGood, Decl(file.ts, 30, 1)) +>x : Symbol(x, Decl(file.ts, 38, 81)) +>T : Symbol(T, Decl(file.ts, 38, 18)) +>y : Symbol(y, Decl(file.ts, 38, 86)) +>U : Symbol(U, Decl(file.ts, 38, 36)) +>SomeInterfaceGood : Symbol(SomeInterfaceGood, Decl(file.ts, 30, 1)) +>T : Symbol(T, Decl(file.ts, 38, 18)) +>U : Symbol(U, Decl(file.ts, 38, 36)) + + if (y === "prop1") { +>y : Symbol(y, Decl(file.ts, 38, 86)) + + return x ? 2 : 1; +>x : Symbol(x, Decl(file.ts, 38, 81)) + } + return x ? 1 : 2; +>x : Symbol(x, Decl(file.ts, 38, 81)) +} + +// Indexed access with indexed access inside - OK, narrows +interface BB { +>BB : Symbol(BB, Decl(file.ts, 43, 1)) + + "a": number; +>"a" : Symbol(BB["a"], Decl(file.ts, 46, 14)) + + "b": string; +>"b" : Symbol(BB["b"], Decl(file.ts, 47, 16)) +} + +interface AA { +>AA : Symbol(AA, Decl(file.ts, 49, 1)) +>T : Symbol(T, Decl(file.ts, 51, 13)) +>BB : Symbol(BB, Decl(file.ts, 43, 1)) + + "c": BB[T]; +>"c" : Symbol(AA["c"], Decl(file.ts, 51, 34)) +>BB : Symbol(BB, Decl(file.ts, 43, 1)) +>T : Symbol(T, Decl(file.ts, 51, 13)) + + "d": boolean, +>"d" : Symbol(AA["d"], Decl(file.ts, 52, 15)) +} + +function reduction>(x: T, y: U): AA[U] { +>reduction : Symbol(reduction, Decl(file.ts, 54, 1)) +>T : Symbol(T, Decl(file.ts, 56, 19)) +>BB : Symbol(BB, Decl(file.ts, 43, 1)) +>U : Symbol(U, Decl(file.ts, 56, 38)) +>AA : Symbol(AA, Decl(file.ts, 49, 1)) +>x : Symbol(x, Decl(file.ts, 56, 64)) +>T : Symbol(T, Decl(file.ts, 56, 19)) +>y : Symbol(y, Decl(file.ts, 56, 69)) +>U : Symbol(U, Decl(file.ts, 56, 38)) +>AA : Symbol(AA, Decl(file.ts, 49, 1)) +>T : Symbol(T, Decl(file.ts, 56, 19)) +>U : Symbol(U, Decl(file.ts, 56, 38)) + + if (x === "a" && y === "c") { +>x : Symbol(x, Decl(file.ts, 56, 64)) +>y : Symbol(y, Decl(file.ts, 56, 69)) + + return 0; // Ok + } + + return undefined as never; +>undefined : Symbol(undefined) +} + +// Conditional with indexed access inside - OK, narrows +function fun5(x: T, y: U): T extends 1 ? BB[U] : T extends 2 ? boolean : never { +>fun5 : Symbol(fun5, Decl(file.ts, 62, 1)) +>T : Symbol(T, Decl(file.ts, 65, 14)) +>U : Symbol(U, Decl(file.ts, 65, 30)) +>BB : Symbol(BB, Decl(file.ts, 43, 1)) +>x : Symbol(x, Decl(file.ts, 65, 51)) +>T : Symbol(T, Decl(file.ts, 65, 14)) +>y : Symbol(y, Decl(file.ts, 65, 56)) +>U : Symbol(U, Decl(file.ts, 65, 30)) +>T : Symbol(T, Decl(file.ts, 65, 14)) +>BB : Symbol(BB, Decl(file.ts, 43, 1)) +>U : Symbol(U, Decl(file.ts, 65, 30)) +>T : Symbol(T, Decl(file.ts, 65, 14)) + + if (x === 1) { +>x : Symbol(x, Decl(file.ts, 65, 51)) + + if (y === "a") { +>y : Symbol(y, Decl(file.ts, 65, 56)) + + return 0; + } + return ""; + } + return true; +} + +// `this` type parameter - Doesn't narrow +abstract class SomeClass { +>SomeClass : Symbol(SomeClass, Decl(file.ts, 73, 1)) + + fun3(this: Sub1 | Sub2): this extends Sub1 ? 1 : this extends Sub2 ? 2 : never { +>fun3 : Symbol(SomeClass.fun3, Decl(file.ts, 76, 26)) +>this : Symbol(this, Decl(file.ts, 77, 9)) +>Sub1 : Symbol(Sub1, Decl(file.ts, 83, 1)) +>Sub2 : Symbol(Sub2, Decl(file.ts, 86, 2)) +>Sub1 : Symbol(Sub1, Decl(file.ts, 83, 1)) +>Sub2 : Symbol(Sub2, Decl(file.ts, 86, 2)) + + if (this instanceof Sub1) { +>this : Symbol(this, Decl(file.ts, 77, 9)) +>Sub1 : Symbol(Sub1, Decl(file.ts, 83, 1)) + + return 1; + } + return 2; + } +} +class Sub1 extends SomeClass { +>Sub1 : Symbol(Sub1, Decl(file.ts, 83, 1)) +>SomeClass : Symbol(SomeClass, Decl(file.ts, 73, 1)) + + #sub1!: symbol; +>#sub1 : Symbol(Sub1.#sub1, Decl(file.ts, 84, 30)) + +}; +class Sub2 extends SomeClass { +>Sub2 : Symbol(Sub2, Decl(file.ts, 86, 2)) +>SomeClass : Symbol(SomeClass, Decl(file.ts, 73, 1)) + + #sub2!: symbol; +>#sub2 : Symbol(Sub2.#sub2, Decl(file.ts, 87, 30)) + +}; + +// Detection of type parameter reference in presence of typeof +function fun2(x: T, y: typeof x): T extends true ? 1 : T extends false ? 2 : never { +>fun2 : Symbol(fun2, Decl(file.ts, 89, 2)) +>T : Symbol(T, Decl(file.ts, 92, 14)) +>x : Symbol(x, Decl(file.ts, 92, 33)) +>T : Symbol(T, Decl(file.ts, 92, 14)) +>y : Symbol(y, Decl(file.ts, 92, 38)) +>x : Symbol(x, Decl(file.ts, 92, 33)) +>T : Symbol(T, Decl(file.ts, 92, 14)) +>T : Symbol(T, Decl(file.ts, 92, 14)) + + return x ? 1 : 2; +>x : Symbol(x, Decl(file.ts, 92, 33)) +} + +// Contextually-typed lambdas +const fun1: (x: T) => T extends true ? 1 : T extends false ? 2 : never = (x) => x ? 1 : 2; +>fun1 : Symbol(fun1, Decl(file.ts, 97, 5)) +>T : Symbol(T, Decl(file.ts, 97, 13)) +>x : Symbol(x, Decl(file.ts, 97, 32)) +>T : Symbol(T, Decl(file.ts, 97, 13)) +>T : Symbol(T, Decl(file.ts, 97, 13)) +>T : Symbol(T, Decl(file.ts, 97, 13)) +>x : Symbol(x, Decl(file.ts, 97, 93)) +>x : Symbol(x, Decl(file.ts, 97, 93)) + + +// Circular conditionals +type SomeCond = T extends true ? 1 : T extends false ? SomeCond : never; +>SomeCond : Symbol(SomeCond, Decl(file.ts, 97, 109)) +>T : Symbol(T, Decl(file.ts, 101, 14)) +>T : Symbol(T, Decl(file.ts, 101, 14)) +>T : Symbol(T, Decl(file.ts, 101, 14)) +>SomeCond : Symbol(SomeCond, Decl(file.ts, 97, 109)) +>T : Symbol(T, Decl(file.ts, 101, 14)) + +function f7(x: T): SomeCond { +>f7 : Symbol(f7, Decl(file.ts, 101, 78)) +>T : Symbol(T, Decl(file.ts, 103, 12)) +>x : Symbol(x, Decl(file.ts, 103, 31)) +>T : Symbol(T, Decl(file.ts, 103, 12)) +>SomeCond : Symbol(SomeCond, Decl(file.ts, 97, 109)) +>T : Symbol(T, Decl(file.ts, 103, 12)) + + if (x) { +>x : Symbol(x, Decl(file.ts, 103, 31)) + + return 1; + } + return 2; +} + +// Composite instantiation of conditional type +type OtherCond = T extends 1 ? "one" : T extends 2 ? "two" : T extends 3 ? "three" : T extends 4 ? "four" : never; +>OtherCond : Symbol(OtherCond, Decl(file.ts, 108, 1)) +>T : Symbol(T, Decl(file.ts, 111, 15)) +>T : Symbol(T, Decl(file.ts, 111, 15)) +>T : Symbol(T, Decl(file.ts, 111, 15)) +>T : Symbol(T, Decl(file.ts, 111, 15)) +>T : Symbol(T, Decl(file.ts, 111, 15)) + +function f8(x: U, y: V): OtherCond { +>f8 : Symbol(f8, Decl(file.ts, 111, 117)) +>U : Symbol(U, Decl(file.ts, 113, 12)) +>V : Symbol(V, Decl(file.ts, 113, 28)) +>x : Symbol(x, Decl(file.ts, 113, 46)) +>U : Symbol(U, Decl(file.ts, 113, 12)) +>y : Symbol(y, Decl(file.ts, 113, 51)) +>V : Symbol(V, Decl(file.ts, 113, 28)) +>OtherCond : Symbol(OtherCond, Decl(file.ts, 108, 1)) +>U : Symbol(U, Decl(file.ts, 113, 12)) +>V : Symbol(V, Decl(file.ts, 113, 28)) + + if (x === 1 && y === 3) { +>x : Symbol(x, Decl(file.ts, 113, 46)) +>y : Symbol(y, Decl(file.ts, 113, 51)) + + return "one"; + } +} + +// Conditionals with `infer` - will not narrow, it is not safe to infer from the narrowed type into an `infer` type parameter +function f9(x: T): T extends Array ? P : T extends number ? undefined : never { +>f9 : Symbol(f9, Decl(file.ts, 117, 1)) +>T : Symbol(T, Decl(file.ts, 120, 12)) +>x : Symbol(x, Decl(file.ts, 120, 46)) +>T : Symbol(T, Decl(file.ts, 120, 12)) +>T : Symbol(T, Decl(file.ts, 120, 12)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more) +>P : Symbol(P, Decl(file.ts, 120, 74)) +>P : Symbol(P, Decl(file.ts, 120, 74)) +>T : Symbol(T, Decl(file.ts, 120, 12)) + + if (Array.isArray(x)) { +>Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more) +>isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(file.ts, 120, 46)) + + // If we allowed narrowing of the conditional return type, when resolving the conditional `T & ("a"[] | "b"[]) extends Array ? P : ...`, + // we could infer `"a" | "b"` for `P`, and allow "a" to be returned. However, when calling `f10`, `T` could be instantiated with `"b"[]`, and the return type would be `"b"`, + // so allowing an `"a"` return would be unsound. + return "a"; + } + return undefined; +>undefined : Symbol(undefined) +} + + diff --git a/tsc/testdata/baselines/reference/compiler/dependentReturnType6.types b/tsc/testdata/baselines/reference/compiler/dependentReturnType6.types new file mode 100644 index 0000000000000..bf2b16e7550c4 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/dependentReturnType6.types @@ -0,0 +1,338 @@ +//// [tests/cases/compiler/dependentReturnType6.ts] //// + +=== file.ts === +// Type parameter in outer scope +function outer(x: T): number { +>outer : (x: T) => number +>x : T + + return inner(); +>inner() : T extends true ? 1 : T extends false ? 2 : never +>inner : () => T extends true ? 1 : T extends false ? 2 : never + + function inner(): T extends true ? 1 : T extends false ? 2 : never { +>inner : () => T extends true ? 1 : T extends false ? 2 : never +>true : true +>false : false + + return x ? 1 : 2; +>x ? 1 : 2 : 1 | 2 +>x : T +>1 : 1 +>2 : 2 + } +} + +// Overloads +function fun6(x: T, y: string): T extends true ? string : T extends false ? 2 : never; +>fun6 : { (x: T, y: string): T extends true ? string : T extends false ? 2 : never; (x: T_1, y: undefined): T_1 extends true ? 1 : T_1 extends false ? 2 : never; (x: boolean): 1 | 2 | string; } +>x : T +>y : string +>true : true +>false : false + +function fun6(x: T, y: undefined): T extends true ? 1 : T extends false ? 2 : never; +>fun6 : { (x: T_1, y: string): T_1 extends true ? string : T_1 extends false ? 2 : never; (x: T, y: undefined): T extends true ? 1 : T extends false ? 2 : never; (x: boolean): 1 | 2 | string; } +>x : T +>y : undefined +>true : true +>false : false + +function fun6(x: boolean): 1 | 2 | string; +>fun6 : { (x: T, y: string): T extends true ? string : T extends false ? 2 : never; (x: T, y: undefined): T extends true ? 1 : T extends false ? 2 : never; (x: boolean): 1 | 2 | string; } +>x : boolean + +function fun6(x: T, y?: string): T extends true ? 1 | string : T extends false ? 2 : never { +>fun6 : { (x: T_1, y: string): T_1 extends true ? string : T_1 extends false ? 2 : never; (x: T_1, y: undefined): T_1 extends true ? 1 : T_1 extends false ? 2 : never; (x: boolean): 1 | 2 | string; } +>x : T +>y : string | undefined +>true : true +>false : false + + return x ? y !== undefined ? y : 1 : 2; +>x ? y !== undefined ? y : 1 : 2 : string | 1 | 2 +>x : T +>y !== undefined ? y : 1 : string | 1 +>y !== undefined : boolean +>y : string | undefined +>undefined : undefined +>y : string +>1 : 1 +>2 : 2 +} + +// Indexed access with conditional inside + +// DOESN'T NARROW the nested conditional type of wrong shape +interface SomeInterfaceBad { + prop1: T extends 1 ? true : T extends 2 ? false : never; +>prop1 : T extends 1 ? true : T extends 2 ? false : never +>true : true +>false : false + + prop2: T extends true ? 1 : T extends false ? 2 : never; +>prop2 : T extends true ? 1 : T extends false ? 2 : never +>true : true +>false : false +} + +function fun4bad>(x: T, y: U): SomeInterfaceBad[U] { +>fun4bad : >(x: T, y: U) => SomeInterfaceBad[U] +>x : T +>y : U + + if (y === "prop1") { +>y === "prop1" : boolean +>y : U +>"prop1" : "prop1" + + return x === 1 ? true : false; +>x === 1 ? true : false : boolean +>x === 1 : boolean +>x : T +>1 : 1 +>true : true +>false : false + } + return x ? 1 : 2; +>x ? 1 : 2 : 1 | 2 +>x : T +>1 : 1 +>2 : 2 +} + +// Narrows nested conditional type of right shape +interface SomeInterfaceGood { + prop1: T extends true ? 2 : T extends false ? 1 : never; +>prop1 : T extends true ? 2 : T extends false ? 1 : never +>true : true +>false : false + + prop2: T extends true ? 1 : T extends false ? 2 : never; +>prop2 : T extends true ? 1 : T extends false ? 2 : never +>true : true +>false : false +} + +function fun4good>(x: T, y: U): SomeInterfaceGood[U] { +>fun4good : >(x: T, y: U) => SomeInterfaceGood[U] +>x : T +>y : U + + if (y === "prop1") { +>y === "prop1" : boolean +>y : U +>"prop1" : "prop1" + + return x ? 2 : 1; +>x ? 2 : 1 : 1 | 2 +>x : T +>2 : 2 +>1 : 1 + } + return x ? 1 : 2; +>x ? 1 : 2 : 1 | 2 +>x : T +>1 : 1 +>2 : 2 +} + +// Indexed access with indexed access inside - OK, narrows +interface BB { + "a": number; +>"a" : number + + "b": string; +>"b" : string +} + +interface AA { + "c": BB[T]; +>"c" : BB[T] + + "d": boolean, +>"d" : boolean +} + +function reduction>(x: T, y: U): AA[U] { +>reduction : >(x: T, y: U) => AA[U] +>x : T +>y : U + + if (x === "a" && y === "c") { +>x === "a" && y === "c" : boolean +>x === "a" : boolean +>x : T +>"a" : "a" +>y === "c" : boolean +>y : U +>"c" : "c" + + return 0; // Ok +>0 : 0 + } + + return undefined as never; +>undefined as never : never +>undefined : undefined +} + +// Conditional with indexed access inside - OK, narrows +function fun5(x: T, y: U): T extends 1 ? BB[U] : T extends 2 ? boolean : never { +>fun5 : (x: T, y: U) => T extends 1 ? BB[U] : T extends 2 ? boolean : never +>x : T +>y : U + + if (x === 1) { +>x === 1 : boolean +>x : T +>1 : 1 + + if (y === "a") { +>y === "a" : boolean +>y : U +>"a" : "a" + + return 0; +>0 : 0 + } + return ""; +>"" : "" + } + return true; +>true : true +} + +// `this` type parameter - Doesn't narrow +abstract class SomeClass { +>SomeClass : SomeClass + + fun3(this: Sub1 | Sub2): this extends Sub1 ? 1 : this extends Sub2 ? 2 : never { +>fun3 : (this: Sub1 | Sub2) => this extends Sub1 ? 1 : this extends Sub2 ? 2 : never +>this : Sub1 | Sub2 + + if (this instanceof Sub1) { +>this instanceof Sub1 : boolean +>this : Sub1 | Sub2 +>Sub1 : typeof Sub1 + + return 1; +>1 : 1 + } + return 2; +>2 : 2 + } +} +class Sub1 extends SomeClass { +>Sub1 : Sub1 +>SomeClass : SomeClass + + #sub1!: symbol; +>#sub1 : symbol + +}; +class Sub2 extends SomeClass { +>Sub2 : Sub2 +>SomeClass : SomeClass + + #sub2!: symbol; +>#sub2 : symbol + +}; + +// Detection of type parameter reference in presence of typeof +function fun2(x: T, y: typeof x): T extends true ? 1 : T extends false ? 2 : never { +>fun2 : (x: T, y: typeof x) => T extends true ? 1 : T extends false ? 2 : never +>x : T +>y : T +>x : T +>true : true +>false : false + + return x ? 1 : 2; +>x ? 1 : 2 : 1 | 2 +>x : T +>1 : 1 +>2 : 2 +} + +// Contextually-typed lambdas +const fun1: (x: T) => T extends true ? 1 : T extends false ? 2 : never = (x) => x ? 1 : 2; +>fun1 : (x: T) => T extends true ? 1 : T extends false ? 2 : never +>x : T +>true : true +>false : false +>(x) => x ? 1 : 2 : (x: T) => 1 | 2 +>x : T +>x ? 1 : 2 : 1 | 2 +>x : T +>1 : 1 +>2 : 2 + + +// Circular conditionals +type SomeCond = T extends true ? 1 : T extends false ? SomeCond : never; +>SomeCond : SomeCond +>true : true +>false : false + +function f7(x: T): SomeCond { +>f7 : (x: T) => SomeCond +>x : T + + if (x) { +>x : T + + return 1; +>1 : 1 + } + return 2; +>2 : 2 +} + +// Composite instantiation of conditional type +type OtherCond = T extends 1 ? "one" : T extends 2 ? "two" : T extends 3 ? "three" : T extends 4 ? "four" : never; +>OtherCond : OtherCond + +function f8(x: U, y: V): OtherCond { +>f8 : (x: U, y: V) => OtherCond +>x : U +>y : V + + if (x === 1 && y === 3) { +>x === 1 && y === 3 : boolean +>x === 1 : boolean +>x : U +>1 : 1 +>y === 3 : boolean +>y : V +>3 : 3 + + return "one"; +>"one" : "one" + } +} + +// Conditionals with `infer` - will not narrow, it is not safe to infer from the narrowed type into an `infer` type parameter +function f9(x: T): T extends Array ? P : T extends number ? undefined : never { +>f9 : (x: T) => T extends Array ? P : T extends number ? undefined : never +>x : T + + if (Array.isArray(x)) { +>Array.isArray(x) : boolean +>Array.isArray : (arg: any) => arg is any[] +>Array : ArrayConstructor +>isArray : (arg: any) => arg is any[] +>x : number | "a"[] | "b"[] + + // If we allowed narrowing of the conditional return type, when resolving the conditional `T & ("a"[] | "b"[]) extends Array ? P : ...`, + // we could infer `"a" | "b"` for `P`, and allow "a" to be returned. However, when calling `f10`, `T` could be instantiated with `"b"[]`, and the return type would be `"b"`, + // so allowing an `"a"` return would be unsound. + return "a"; +>"a" : "a" + } + return undefined; +>undefined : undefined +} + + diff --git a/tsc/testdata/baselines/reference/compiler/dependentReturnType8.symbols b/tsc/testdata/baselines/reference/compiler/dependentReturnType8.symbols new file mode 100644 index 0000000000000..9151c3c4dde80 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/dependentReturnType8.symbols @@ -0,0 +1,30 @@ +//// [tests/cases/compiler/dependentReturnType8.ts] //// + +=== dependentReturnType8.ts === +export {}; + +declare const record: Record; +>record : Symbol(record, Decl(dependentReturnType8.ts, 2, 13)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) + +declare const array: string[]; +>array : Symbol(array, Decl(dependentReturnType8.ts, 3, 13)) + +// Arrow function with expression body +const getObject = +>getObject : Symbol(getObject, Decl(dependentReturnType8.ts, 6, 5)) + + (group: T): T extends string ? string[] : T extends undefined ? Record : never => +>T : Symbol(T, Decl(dependentReturnType8.ts, 7, 5)) +>group : Symbol(group, Decl(dependentReturnType8.ts, 7, 35)) +>T : Symbol(T, Decl(dependentReturnType8.ts, 7, 5)) +>T : Symbol(T, Decl(dependentReturnType8.ts, 7, 5)) +>T : Symbol(T, Decl(dependentReturnType8.ts, 7, 5)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) + + group === undefined ? record : array; +>group : Symbol(group, Decl(dependentReturnType8.ts, 7, 35)) +>undefined : Symbol(undefined) +>record : Symbol(record, Decl(dependentReturnType8.ts, 2, 13)) +>array : Symbol(array, Decl(dependentReturnType8.ts, 3, 13)) + diff --git a/tsc/testdata/baselines/reference/compiler/dependentReturnType8.types b/tsc/testdata/baselines/reference/compiler/dependentReturnType8.types new file mode 100644 index 0000000000000..a77a1df6a8db5 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/dependentReturnType8.types @@ -0,0 +1,27 @@ +//// [tests/cases/compiler/dependentReturnType8.ts] //// + +=== dependentReturnType8.ts === +export {}; + +declare const record: Record; +>record : Record + +declare const array: string[]; +>array : string[] + +// Arrow function with expression body +const getObject = +>getObject : (group: T) => T extends string ? string[] : T extends undefined ? Record : never + + (group: T): T extends string ? string[] : T extends undefined ? Record : never => +>(group: T): T extends string ? string[] : T extends undefined ? Record : never => group === undefined ? record : array : (group: T) => T extends string ? string[] : T extends undefined ? Record : never +>group : T + + group === undefined ? record : array; +>group === undefined ? record : array : string[] | Record +>group === undefined : boolean +>group : T +>undefined : undefined +>record : Record +>array : string[] + diff --git a/tsc/testdata/baselines/reference/compiler/dependentReturnType9.errors.txt b/tsc/testdata/baselines/reference/compiler/dependentReturnType9.errors.txt new file mode 100644 index 0000000000000..da12eeaab2eb4 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/dependentReturnType9.errors.txt @@ -0,0 +1,71 @@ +dependentReturnType9.ts(57,4): error TS2366: Function lacks ending return statement and return type does not include 'undefined'. + + +==== dependentReturnType9.ts (1 errors) ==== + type Payload = + | { _tag: "auth"; username: string; password: string } + | { _tag: "cart"; items: Array<{ id: string; quantity: number }> } + | { _tag: "person"; name: string; age: number }; + + type PayloadContent = { + [P in Payload as P["_tag"]]: Omit; + }; + + // ok, exhaustive cases and default case with throw + function mockPayload( + str: P_TAG, + ): PayloadContent[P_TAG] { + switch (str) { + case "auth": + return { username: "test", password: "admin" }; + case "cart": + return { items: [{ id: "123", quantity: 123 }] }; + case "person": + return { name: "andrea", age: 27 }; + default: + throw new Error("unknown tag"); + } + } + + // ok, non-exhaustive cases but default case with throw + function mockPayload2( + str: P_TAG, + ): PayloadContent[P_TAG] { + switch (str) { + case "auth": + return { username: "test", password: "admin" }; + case "cart": + return { items: [{ id: "123", quantity: 123 }] }; + default: + throw new Error("unhandled tag"); + } + } + + // ok, exhaustive cases + function mockPayload3( + str: P_TAG, + ): PayloadContent[P_TAG] { + switch (str) { + case "auth": + return { username: "test", password: "admin" }; + case "cart": + return { items: [{ id: "123", quantity: 123 }] }; + case "person": + return { name: "andrea", age: 27 }; + } + } + + // error, non-exhaustive cases + function mockPayload4( + str: P_TAG, + ): PayloadContent[P_TAG] { + ~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2366: Function lacks ending return statement and return type does not include 'undefined'. + switch (str) { + case "auth": + return { username: "test", password: "admin" }; + case "cart": + return { items: [{ id: "123", quantity: 123 }] }; + } + } + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/dependentReturnType9.symbols b/tsc/testdata/baselines/reference/compiler/dependentReturnType9.symbols new file mode 100644 index 0000000000000..35f20fd06d304 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/dependentReturnType9.symbols @@ -0,0 +1,173 @@ +//// [tests/cases/compiler/dependentReturnType9.ts] //// + +=== dependentReturnType9.ts === +type Payload = +>Payload : Symbol(Payload, Decl(dependentReturnType9.ts, 0, 0)) + + | { _tag: "auth"; username: string; password: string } +>_tag : Symbol(_tag, Decl(dependentReturnType9.ts, 1, 5)) +>username : Symbol(username, Decl(dependentReturnType9.ts, 1, 19)) +>password : Symbol(password, Decl(dependentReturnType9.ts, 1, 37)) + + | { _tag: "cart"; items: Array<{ id: string; quantity: number }> } +>_tag : Symbol(_tag, Decl(dependentReturnType9.ts, 2, 5)) +>items : Symbol(items, Decl(dependentReturnType9.ts, 2, 19)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more) +>id : Symbol(id, Decl(dependentReturnType9.ts, 2, 34)) +>quantity : Symbol(quantity, Decl(dependentReturnType9.ts, 2, 46)) + + | { _tag: "person"; name: string; age: number }; +>_tag : Symbol(_tag, Decl(dependentReturnType9.ts, 3, 5)) +>name : Symbol(name, Decl(dependentReturnType9.ts, 3, 21)) +>age : Symbol(age, Decl(dependentReturnType9.ts, 3, 35)) + +type PayloadContent = { +>PayloadContent : Symbol(PayloadContent, Decl(dependentReturnType9.ts, 3, 50)) + + [P in Payload as P["_tag"]]: Omit; +>P : Symbol(P, Decl(dependentReturnType9.ts, 6, 3)) +>Payload : Symbol(Payload, Decl(dependentReturnType9.ts, 0, 0)) +>P : Symbol(P, Decl(dependentReturnType9.ts, 6, 3)) +>Omit : Symbol(Omit, Decl(lib.es5.d.ts, --, --)) +>P : Symbol(P, Decl(dependentReturnType9.ts, 6, 3)) + +}; + +// ok, exhaustive cases and default case with throw +function mockPayload( +>mockPayload : Symbol(mockPayload, Decl(dependentReturnType9.ts, 7, 2)) +>P_TAG : Symbol(P_TAG, Decl(dependentReturnType9.ts, 10, 21)) +>Payload : Symbol(Payload, Decl(dependentReturnType9.ts, 0, 0)) + + str: P_TAG, +>str : Symbol(str, Decl(dependentReturnType9.ts, 10, 52)) +>P_TAG : Symbol(P_TAG, Decl(dependentReturnType9.ts, 10, 21)) + +): PayloadContent[P_TAG] { +>PayloadContent : Symbol(PayloadContent, Decl(dependentReturnType9.ts, 3, 50)) +>P_TAG : Symbol(P_TAG, Decl(dependentReturnType9.ts, 10, 21)) + + switch (str) { +>str : Symbol(str, Decl(dependentReturnType9.ts, 10, 52)) + + case "auth": + return { username: "test", password: "admin" }; +>username : Symbol(username, Decl(dependentReturnType9.ts, 15, 14)) +>password : Symbol(password, Decl(dependentReturnType9.ts, 15, 32)) + + case "cart": + return { items: [{ id: "123", quantity: 123 }] }; +>items : Symbol(items, Decl(dependentReturnType9.ts, 17, 14)) +>id : Symbol(id, Decl(dependentReturnType9.ts, 17, 24)) +>quantity : Symbol(quantity, Decl(dependentReturnType9.ts, 17, 35)) + + case "person": + return { name: "andrea", age: 27 }; +>name : Symbol(name, Decl(dependentReturnType9.ts, 19, 14)) +>age : Symbol(age, Decl(dependentReturnType9.ts, 19, 30)) + + default: + throw new Error("unknown tag"); +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2022.error.d.ts, --, --)) + } +} + +// ok, non-exhaustive cases but default case with throw +function mockPayload2( +>mockPayload2 : Symbol(mockPayload2, Decl(dependentReturnType9.ts, 23, 1)) +>P_TAG : Symbol(P_TAG, Decl(dependentReturnType9.ts, 26, 22)) +>Payload : Symbol(Payload, Decl(dependentReturnType9.ts, 0, 0)) + + str: P_TAG, +>str : Symbol(str, Decl(dependentReturnType9.ts, 26, 53)) +>P_TAG : Symbol(P_TAG, Decl(dependentReturnType9.ts, 26, 22)) + +): PayloadContent[P_TAG] { +>PayloadContent : Symbol(PayloadContent, Decl(dependentReturnType9.ts, 3, 50)) +>P_TAG : Symbol(P_TAG, Decl(dependentReturnType9.ts, 26, 22)) + + switch (str) { +>str : Symbol(str, Decl(dependentReturnType9.ts, 26, 53)) + + case "auth": + return { username: "test", password: "admin" }; +>username : Symbol(username, Decl(dependentReturnType9.ts, 31, 14)) +>password : Symbol(password, Decl(dependentReturnType9.ts, 31, 32)) + + case "cart": + return { items: [{ id: "123", quantity: 123 }] }; +>items : Symbol(items, Decl(dependentReturnType9.ts, 33, 14)) +>id : Symbol(id, Decl(dependentReturnType9.ts, 33, 24)) +>quantity : Symbol(quantity, Decl(dependentReturnType9.ts, 33, 35)) + + default: + throw new Error("unhandled tag"); +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2022.error.d.ts, --, --)) + } +} + +// ok, exhaustive cases +function mockPayload3( +>mockPayload3 : Symbol(mockPayload3, Decl(dependentReturnType9.ts, 37, 1)) +>P_TAG : Symbol(P_TAG, Decl(dependentReturnType9.ts, 40, 22)) +>Payload : Symbol(Payload, Decl(dependentReturnType9.ts, 0, 0)) + + str: P_TAG, +>str : Symbol(str, Decl(dependentReturnType9.ts, 40, 53)) +>P_TAG : Symbol(P_TAG, Decl(dependentReturnType9.ts, 40, 22)) + +): PayloadContent[P_TAG] { +>PayloadContent : Symbol(PayloadContent, Decl(dependentReturnType9.ts, 3, 50)) +>P_TAG : Symbol(P_TAG, Decl(dependentReturnType9.ts, 40, 22)) + + switch (str) { +>str : Symbol(str, Decl(dependentReturnType9.ts, 40, 53)) + + case "auth": + return { username: "test", password: "admin" }; +>username : Symbol(username, Decl(dependentReturnType9.ts, 45, 14)) +>password : Symbol(password, Decl(dependentReturnType9.ts, 45, 32)) + + case "cart": + return { items: [{ id: "123", quantity: 123 }] }; +>items : Symbol(items, Decl(dependentReturnType9.ts, 47, 14)) +>id : Symbol(id, Decl(dependentReturnType9.ts, 47, 24)) +>quantity : Symbol(quantity, Decl(dependentReturnType9.ts, 47, 35)) + + case "person": + return { name: "andrea", age: 27 }; +>name : Symbol(name, Decl(dependentReturnType9.ts, 49, 14)) +>age : Symbol(age, Decl(dependentReturnType9.ts, 49, 30)) + } +} + +// error, non-exhaustive cases +function mockPayload4( +>mockPayload4 : Symbol(mockPayload4, Decl(dependentReturnType9.ts, 51, 1)) +>P_TAG : Symbol(P_TAG, Decl(dependentReturnType9.ts, 54, 22)) +>Payload : Symbol(Payload, Decl(dependentReturnType9.ts, 0, 0)) + + str: P_TAG, +>str : Symbol(str, Decl(dependentReturnType9.ts, 54, 53)) +>P_TAG : Symbol(P_TAG, Decl(dependentReturnType9.ts, 54, 22)) + +): PayloadContent[P_TAG] { +>PayloadContent : Symbol(PayloadContent, Decl(dependentReturnType9.ts, 3, 50)) +>P_TAG : Symbol(P_TAG, Decl(dependentReturnType9.ts, 54, 22)) + + switch (str) { +>str : Symbol(str, Decl(dependentReturnType9.ts, 54, 53)) + + case "auth": + return { username: "test", password: "admin" }; +>username : Symbol(username, Decl(dependentReturnType9.ts, 59, 14)) +>password : Symbol(password, Decl(dependentReturnType9.ts, 59, 32)) + + case "cart": + return { items: [{ id: "123", quantity: 123 }] }; +>items : Symbol(items, Decl(dependentReturnType9.ts, 61, 14)) +>id : Symbol(id, Decl(dependentReturnType9.ts, 61, 24)) +>quantity : Symbol(quantity, Decl(dependentReturnType9.ts, 61, 35)) + } +} + diff --git a/tsc/testdata/baselines/reference/compiler/dependentReturnType9.types b/tsc/testdata/baselines/reference/compiler/dependentReturnType9.types new file mode 100644 index 0000000000000..b0f99fe5c35b8 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/dependentReturnType9.types @@ -0,0 +1,204 @@ +//// [tests/cases/compiler/dependentReturnType9.ts] //// + +=== dependentReturnType9.ts === +type Payload = +>Payload : Payload + + | { _tag: "auth"; username: string; password: string } +>_tag : "auth" +>username : string +>password : string + + | { _tag: "cart"; items: Array<{ id: string; quantity: number }> } +>_tag : "cart" +>items : { id: string; quantity: number; }[] +>id : string +>quantity : number + + | { _tag: "person"; name: string; age: number }; +>_tag : "person" +>name : string +>age : number + +type PayloadContent = { +>PayloadContent : PayloadContent + + [P in Payload as P["_tag"]]: Omit; +}; + +// ok, exhaustive cases and default case with throw +function mockPayload( +>mockPayload : (str: P_TAG) => PayloadContent[P_TAG] + + str: P_TAG, +>str : P_TAG + +): PayloadContent[P_TAG] { + switch (str) { +>str : P_TAG + + case "auth": +>"auth" : "auth" + + return { username: "test", password: "admin" }; +>{ username: "test", password: "admin" } : { username: string; password: string; } +>username : string +>"test" : "test" +>password : string +>"admin" : "admin" + + case "cart": +>"cart" : "cart" + + return { items: [{ id: "123", quantity: 123 }] }; +>{ items: [{ id: "123", quantity: 123 }] } : { items: { id: string; quantity: number; }[]; } +>items : { id: string; quantity: number; }[] +>[{ id: "123", quantity: 123 }] : { id: string; quantity: number; }[] +>{ id: "123", quantity: 123 } : { id: string; quantity: number; } +>id : string +>"123" : "123" +>quantity : number +>123 : 123 + + case "person": +>"person" : "person" + + return { name: "andrea", age: 27 }; +>{ name: "andrea", age: 27 } : { name: string; age: number; } +>name : string +>"andrea" : "andrea" +>age : number +>27 : 27 + + default: + throw new Error("unknown tag"); +>new Error("unknown tag") : Error +>Error : ErrorConstructor +>"unknown tag" : "unknown tag" + } +} + +// ok, non-exhaustive cases but default case with throw +function mockPayload2( +>mockPayload2 : (str: P_TAG) => PayloadContent[P_TAG] + + str: P_TAG, +>str : P_TAG + +): PayloadContent[P_TAG] { + switch (str) { +>str : P_TAG + + case "auth": +>"auth" : "auth" + + return { username: "test", password: "admin" }; +>{ username: "test", password: "admin" } : { username: string; password: string; } +>username : string +>"test" : "test" +>password : string +>"admin" : "admin" + + case "cart": +>"cart" : "cart" + + return { items: [{ id: "123", quantity: 123 }] }; +>{ items: [{ id: "123", quantity: 123 }] } : { items: { id: string; quantity: number; }[]; } +>items : { id: string; quantity: number; }[] +>[{ id: "123", quantity: 123 }] : { id: string; quantity: number; }[] +>{ id: "123", quantity: 123 } : { id: string; quantity: number; } +>id : string +>"123" : "123" +>quantity : number +>123 : 123 + + default: + throw new Error("unhandled tag"); +>new Error("unhandled tag") : Error +>Error : ErrorConstructor +>"unhandled tag" : "unhandled tag" + } +} + +// ok, exhaustive cases +function mockPayload3( +>mockPayload3 : (str: P_TAG) => PayloadContent[P_TAG] + + str: P_TAG, +>str : P_TAG + +): PayloadContent[P_TAG] { + switch (str) { +>str : P_TAG + + case "auth": +>"auth" : "auth" + + return { username: "test", password: "admin" }; +>{ username: "test", password: "admin" } : { username: string; password: string; } +>username : string +>"test" : "test" +>password : string +>"admin" : "admin" + + case "cart": +>"cart" : "cart" + + return { items: [{ id: "123", quantity: 123 }] }; +>{ items: [{ id: "123", quantity: 123 }] } : { items: { id: string; quantity: number; }[]; } +>items : { id: string; quantity: number; }[] +>[{ id: "123", quantity: 123 }] : { id: string; quantity: number; }[] +>{ id: "123", quantity: 123 } : { id: string; quantity: number; } +>id : string +>"123" : "123" +>quantity : number +>123 : 123 + + case "person": +>"person" : "person" + + return { name: "andrea", age: 27 }; +>{ name: "andrea", age: 27 } : { name: string; age: number; } +>name : string +>"andrea" : "andrea" +>age : number +>27 : 27 + } +} + +// error, non-exhaustive cases +function mockPayload4( +>mockPayload4 : (str: P_TAG) => PayloadContent[P_TAG] + + str: P_TAG, +>str : P_TAG + +): PayloadContent[P_TAG] { + switch (str) { +>str : P_TAG + + case "auth": +>"auth" : "auth" + + return { username: "test", password: "admin" }; +>{ username: "test", password: "admin" } : { username: string; password: string; } +>username : string +>"test" : "test" +>password : string +>"admin" : "admin" + + case "cart": +>"cart" : "cart" + + return { items: [{ id: "123", quantity: 123 }] }; +>{ items: [{ id: "123", quantity: 123 }] } : { items: { id: string; quantity: number; }[]; } +>items : { id: string; quantity: number; }[] +>[{ id: "123", quantity: 123 }] : { id: string; quantity: number; }[] +>{ id: "123", quantity: 123 } : { id: string; quantity: number; } +>id : string +>"123" : "123" +>quantity : number +>123 : 123 + } +} + diff --git a/tsc/testdata/tests/cases/compiler/dependentReturnType1.ts b/tsc/testdata/tests/cases/compiler/dependentReturnType1.ts new file mode 100644 index 0000000000000..7527d3015bc3b --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/dependentReturnType1.ts @@ -0,0 +1,519 @@ +// @strict: true +// @noEmit: true +// @target: esnext + +interface A { + 1: number; + 2: string; +} + +function f1(x: T): A[T] { + if (x === 1) { + return 0; // Ok + } + else { + return 1; // Error + } +} + +interface C { + 1: number; + 2: string; + 3: boolean; +} + +function f2(x: T): C[T] { + if (x === 1) { + return 0; // Ok + } + else { + return ""; // Error, returned expression needs to have type string & boolean (= never) + } +} + +function f3(x: T): T extends 1 ? number : T extends 2 ? string : T extends 3 ? boolean : never { + if (x === 1) { + return 0; // Ok + } + else { + return ""; // Error, returned expression needs to have type string & boolean (= never) + } +} + +interface One { + a: "a"; + b: "b"; + c: "c"; + d: "d"; +} + +interface Two { + a: "a"; + b: "b"; + e: "e"; + f: "f"; +} + +interface Three { + a: "a"; + c: "c"; + e: "e"; + g: "g"; +} + +interface Four { + a: "a"; + d: "d"; + f: "f"; + g: "g"; +} +// Badly written conditional return type, will not trigger narrowing +function f10(x: T): T extends 1 ? One : T extends 2 ? Two : T extends 3 ? Three : Four { + if (x === 1 || x === 2) { + return { a: "a", b: "b", c: "c", d: "d", e: "e", f: "f" }; // Error + } + return { a: "a", b: "b", c: "c", d: "d", e: "e", f: "f", g: "g" }; // Error +} +// Well written conditional +function f101(x: T): T extends 1 ? One : T extends 2 ? Two : T extends 3 ? Three : T extends 4 ? Four : never { + if (x === 1 || x === 2) { + return { a: "a", b: "b", c: "c", d: "d", e: "e", f: "f" }; // Ok + } + // Excess property becomes a problem with the change, + // because we now check assignability to a narrower type... + return { a: "a", b: "b", c: "c", d: "d", e: "e", f: "f", g: "g" }; // EPC Error +} + +// This will not work for several reasons: +// - first because the constraint of type parameter `Arg` is generic, +// so attempting to narrow the type of `arg` in the `if` would result in type `Arg & LeftIn`, +// which when substituted in the conditional return type, would not further resolve that conditional type +// - second because the `else` branch would never work because we don't narrow the type of `arg` to `Arg & RightIn` +function conditionalProducingIf( + arg: Arg, + cond: (arg: LeftIn | RightIn) => arg is LeftIn, + produceLeftOut: (arg: LeftIn) => LeftOut, + produceRightOut: (arg: RightIn) => RightOut): + Arg extends LeftIn ? LeftOut : Arg extends RightIn ? RightOut : never +{ + if (cond(arg)) { + return produceLeftOut(arg); + } else { + return produceRightOut(arg as RightIn); + } +} + +interface Animal { + name: string; +} + +interface Dog extends Animal { + bark: () => string; +} + +// This would be unsafe to narrow. +declare function isDog(x: Animal): x is Dog; +declare function doggy(x: Dog): number; +function f12(x: T): T extends Dog ? number : string { + if (isDog(x)) { // `x` has type `T & Dog` here + return doggy(x); + } + return ""; // Error: Should not work because we can't express "not a Dog" in the type system +} + +// Cannot narrow `keyof` too eagerly or something like the below breaks +function f(entry: EntryId): Entry[EntryId] { + const entries = {} as Entry; + return entries[entry]; +} + +// Works the same as before +declare function takeA(val: 'A'): void; +export function bounceAndTakeIfA(value: AB): AB { + if (value === 'A') { + takeA(value); + takeAB(value); + return value; + } + + return value; + function takeAB(val: AB): void {} +} + +// Works the same as before +export function bbb(value: AB): "a" { + if (value === "a") { + return value; + } + return "a"; +} + +class Unnamed { + root!: { name: string }; + // Error: No narrowing because parameter is optional but `T` doesn't allow for undefined + name(name?: T): T extends string ? this : T extends undefined ? string : never { + if (typeof name === 'undefined') { + return this.root.name; + } + return this; + } + + // Good conditional + name2(name?: T): T extends string ? this : T extends undefined ? string : never { + if (typeof name === 'undefined') { + return this.root.name; // Ok + } + this.root.name = name; + return this; // Ok + } + + // Good conditional, wrong return expressions + name3(name?: T): T extends string ? this : T extends undefined ? string : never { + if (typeof name === 'undefined') { + return this; // Error + } + this.root.name = name; + return name; // Error + } +} + +// Conditional expressions +interface Aa { + 1: number; + 2: string; + 3: boolean; +} + +function trivialConditional(x: T): Aa[T] { + if (x !== 1) { + return x === 2 ? "" : true; + } + else { + return 0; + } +} + +function conditional(x: T): + T extends true ? 1 : T extends false ? 2 : never { + return x ? 1 : 2; // Ok +} + +function contextualConditional( + x: T +): T extends "a" ? "a" : T extends "b" ? number : never { + return x === "a" ? x : parseInt(x); // Ok +} + +function conditionalWithError( + x: T +): T extends "a" ? number : T extends "b" ? string : never { + return x === "a" ? x : parseInt(x); // Error +} + +// Multiple indexed type reductions +interface BB { + "a": number; + [y: number]: string; +} + +interface AA { + "c": BB[T]; + "d": boolean, +} + +function reduction(x: T, y: U): AA[U] { + if (y === "c" && x === "a") { + // AA[U='c'] -> BB[T] + // BB[T='a'] -> number + return 0; // Ok + } + + return undefined as never; +} + +// Substitution types are not narrowed +function subsCond( + x: T, +): T extends 1 | 2 + ? T extends 1 + ? string + : T extends 2 + ? boolean + : never + : T extends 3 + ? number + : never { + if (x === 1) { + return ""; + } else if (x == 2) { + return true; + } + return 3; +} + + +// Unsafe: check types overlap +declare function q(x: object): x is { b: number }; +function foo( + x: T, +): T extends { a: string } ? number : T extends { b: number } ? string : never { + if (q(x)) { + x.b; + return ""; + } + x.a; + return 1; +} + +let y = { a: "", b: 1 } +const r = foo<{ a: string }>(y); // type says number but actually string + +type HelperCond = T extends A ? R1 : T extends B ? R2 : never; + +// We don't narrow the return type because the conditionals are not distributive +function foo2(x: U, y: V): + HelperCond<{ x: U, y: V }, + { x: string, y: true }, 1, + { x: number, y: false }, 2> { + if (typeof x === "string" && y === true) { + return 1; // Error + } + if (typeof x === "number" && y === false) { + return 2; // Error + } + return 0; // Error +} + +// From https://github.com/microsoft/TypeScript/issues/24929#issue-332087943 +declare function isString(s: unknown): s is string; +// capitalize a string or each element of an array of strings +function capitalize( + input: T +): T extends string[] ? string[] : T extends string ? string : never { + if (isString(input)) { + return input[0].toUpperCase() + input.slice(1); // Ok + } else { + return input.map(elt => capitalize(elt)); // Ok + } +} + +function badCapitalize( + input: T +): T extends string[] ? string[] : T extends string ? string : never { + if (isString(input)) { + return input[0].toUpperCase() + input.slice(1); // Ok + } else { + return input[0].toUpperCase() + input.slice(1); // Bad, error + } +} + +// No narrowing because conditional's extends type is different from type parameter constraint types +function voidRet( + x: T +): T extends {} ? void : T extends undefined ? number : never { + if (x) { + return; + } + return 1; +} + +// Multiple type parameters at once +function woo( + x: T, + y: U, +): T extends string + ? U extends string + ? 1 + : U extends number + ? 2 + : never + : T extends number + ? U extends number + ? 3 + : U extends string + ? 4 + : never + : never { + if (typeof x === "number" && typeof y === "string") { + return 1; // Good error + } + return undefined as any; +} + +function ttt( + x: T, + y: U, +): T extends string + ? U extends string + ? 1 + : U extends number + ? 2 + : never + : T extends number + ? U extends number + ? 3 + : U extends string + ? 4 + : never + : never { + if (typeof x === "number" && typeof y === "string") { + return 4; // Ok + } + + return undefined as any; +} + +// Shadowing of the narrowed reference +function shadowing(x: T): T extends 1 ? number : T extends 2 ? string : never { + if (true) { + let x: number = Math.random() ? 1 : 2; + if (x === 1) { + return 1; // Error + } + return ""; // Error + } +} + +function noShadowing(x: T): T extends 1 ? number : T extends 2 ? string : never { + if (true) { + if (x === 1) { + return 1; // Ok + } + return ""; // Ok + } +} + +// If the narrowing reference is out of scope, we simply won't narrow its type +declare let someX: boolean; +function scope2(a: T): T extends true ? 1 : T extends false ? 2 : never { + if ((true)) { + const someX = a; + if (someX) { // We narrow `someX` and the return type here + return 1; + } + } + if (!someX) { // This is a different `someX`, so we don't narrow here + return 2; + } + + return undefined as any; +} + +function moreShadowing(x: T): T extends 1 ? number : T extends 2 ? string : never { + if (x === 2) { + let x: number = Math.random() ? 1 : 2; + if (x === 1) { + return 1; // Error + } + return ""; // Ok + } + return 0; // Ok +} + +// This would be unsafe to narrow due to `infer` type. +function withInfer(x: T): T extends [infer R] ? R : T extends number ? boolean : never { + if (typeof x === "number") { + return true; + } + return ""; +} + +const withInferResult = withInfer(["a"] as const); // The type says it returns `"a"`, but the function actually returns `""`. + +// Ok +async function abool(x: T): Promise { + if (x) { + return 1; + } + return 2; +} + +// Ok +function* bbool(x: T): Generator { + yield 3; + if (x) { + return 1; + } + return 2; +} + +// We don't do the same type of narrowing for `yield` statements +function* cbool(x: T): Generator { + if (x) { + yield 1; + } + yield 2; + return 0; +} + +// From #33912 +abstract class Operation { + abstract perform(t: T): R; +} + +type ConditionalReturnType | undefined> = + EOp extends Operation ? R : EOp extends undefined ? T | R : never; + + +class ConditionalOperation< + T, + R, + EOp extends Operation | undefined, +> extends Operation> { + constructor( + private predicate: (value: T) => boolean, + private thenOp: Operation, + private elseOp?: EOp, + ) { + super(); + } + + // We won't try to narrow the return type because `T` is declared on the class and we don't analyze this case. + perform(t: T): ConditionalReturnType { + if (this.predicate(t)) { + return this.thenOp.perform(t); // Bad: this is assignable to all of the branches of the conditional, but we still can't return it + } else if (typeof this.elseOp !== "undefined") { + return this.elseOp.perform(t); // Would be ok + } else { + return t; // Would be ok + } + } +} + +// Like the version above, we will not attempt to narrow because there's more than one reference to `T`, +// because `T` shows up in the type of `predicate`. +function perform | undefined>( + t: T, + predicate: (value: T) => boolean, + thenOp: Operation, + elseOp?: EOp, + ): ConditionalReturnType { + if (predicate(t)) { + return thenOp.perform(t); // Bad: this is assignable to all of the branches of the conditional, but we still can't return it + } else if (elseOp !== undefined) { + return elseOp.perform(t); // Would be ok + } else { + return t; // Would be ok + } +} + +// Return conditional expressions with parentheses +function returnStuff1(x: T ): T extends true ? 1 : T extends false ? 2 : never { + return (x ? (1) : 2); +} + +function returnStuff2(x: T ): + T extends 1 ? "one" : T extends 2 ? "two" : T extends "a" ? 0 : never { + return (typeof x === "string" ? 0 : (x === 1 ? ("one") : "two")); +} + +// If the conditional type's input is `never`, then it resolves to `never`: +function neverOk(x: T): T extends true ? 1 : T extends false ? 2 : never { + if (x === true) { + return 1; + } + if (x === false) { + return 2; + } + return 1; +} \ No newline at end of file diff --git a/tsc/testdata/tests/cases/compiler/dependentReturnType10.ts b/tsc/testdata/tests/cases/compiler/dependentReturnType10.ts new file mode 100644 index 0000000000000..df2ee64d825a6 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/dependentReturnType10.ts @@ -0,0 +1,22 @@ +// @noEmit: true + +interface Animal { + name: string; + species: string; +} + +interface Dog extends Animal { + breed: string; +} + +type GreetRet = + T extends string ? string : + T extends { name: string } ? { greeting: string, breed: string } : + never; + +function greet(animal: T): GreetRet { + if (typeof animal === "string") { + return `hello, ${animal}` + } + return { greeting: `woof, ${animal.name}`, breed: animal.breed } +} \ No newline at end of file diff --git a/tsc/testdata/tests/cases/compiler/dependentReturnType11.ts b/tsc/testdata/tests/cases/compiler/dependentReturnType11.ts new file mode 100644 index 0000000000000..fccaea1af1acc --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/dependentReturnType11.ts @@ -0,0 +1,286 @@ +// @noEmit: true +// @strict: true + +type Ret = + T extends true ? 1 : + T extends false ? 2 : + never; + +// Tests for constructing narrowable reference. + +function f1(param: T): Ret { + return param ? 1 : 2; +} + +function f2(param: { prop: T }): Ret { + return param.prop ? 1 : 2; +} + +function f3({ prop }: { prop: T }): Ret { + return prop ? 1 : 2; +} + +function f4({ prop1 }: { prop1: { prop2: T } }): Ret { + return prop1.prop2 ? 1 : 2; +} + +function f5(param: { prop1: { prop2: T } }): Ret { + return param.prop1.prop2 ? 1 : 2; +} + +function f6({ prop1: { prop2: a } }: { prop1: { prop2: T } }): Ret { + return a ? 1 : 2; +} + +function f7({ prop1: { prop2 } }: { prop1: { prop2: T } }): Ret { + return prop2 ? 1 : 2; +} + +function f8({ prop1 }: T): Ret { // Bad. + return prop1 ? 1 : 2; +} + +function f9(param: { "some prop": T }): Ret { + return param["some prop"] ? 1 : 2; +} + +// Tests for detection of valid narrowable type parameter references. + +function g1(param: T): Ret { + return param ? 1 : 2; +} + +function g2(param: { prop: T }): Ret { + return param.prop ? 1 : 2; +} + +class Dog { + bark(): void {} +} + +class Cat { + meow(): void {} + +} + +type Type1 = { prop: T, prop2: Cat | Dog } + +function g3(param: Type1): Ret { + return param.prop ? 1 : 2; +} + +type TypeUnused = string; + +function g4(param: Type1, other: TypeUnused): Ret { // Bad. + return param.prop ? 1 : 2; +} + +interface Type2 { + prop: T, + [s: string]: boolean | undefined, +} + +function g5(param: Type2): Ret { + return param.prop ? 1 : 2; +} + +interface Type3 { + prop: T, +} + +interface Type3 { + prop2: Cat | Dog, +} + +function g6(param: Type3): Ret { // Unsupported for now: interface merging. + return param.prop ? 1 : 2; +} + +type Type4 = { + prop: T, + prop2: S[], +} + +function g7(param: Type4): Ret { + return param.prop ? 1 : 2; +} + +function g8(param: Type1 & { prop2: Cat | Dog }): Ret { + return param.prop ? 1 : 2; +} + +function g9([ prop ]: [T]): Ret { // Unsupported. + return prop ? 1 : 2; +} + +function g10(param: [T]): Ret { // Unsupported. + return param[0] ? 1 : 2; +} + +function g11(param: T[]): Ret { // Bad. + return param[0] ? 1 : 2; +} + +type Type5 = { + prop: S, + prop2: string[], +} + +function g12(param: Type5): Ret { + return param.prop ? 1 : 2; +} + +type Type6 = { + prop: S, + prop2: S[], +} + +function g13(param: Type6): Ret { // Bad. + return param.prop ? 1 : 2; +} + +type Type7 = { + [Key in "prop"]: T +} + +function g14(param: Type7): Ret { // Unsupported. + return param.prop ? 1 : 2; +} + +class Class1 { + prop!: T; +} + +function g15(param: Class1): Ret { + return param.prop ? 1 : 2; +} + +type Type8 = { + prop: T, +} + +type Type9 = { + prop2: T, +} + +function g16(param: Type8["prop2"]>): Ret { // Unsupported. + return param.prop ? 1 : 2; +} + +// Tests for shadowing and resolving the constructed narrowable reference. + +function h1(param: T): Ret { + if (param) { + const param = false; + return 1; + } + return 2; +} + +function h2({ prop }: { prop: T}): Ret { + if (prop) { + const prop = false; + return 1; + } + return 2; +} + +function h3(param: { prop: T}): Ret { + if (param.prop) { + const param = { prop: false }; + return 1; + } + return 2; +} + +function h4(param: { "some prop": T }): Ret { + if (param["some prop"]) { + const param = { "some prop": false }; + return 1; + } + return 2; +} + +function h5(param: T): Ret { + { + const param = true; + if (param) { + return 1; // Bad. + } + return 2; + } +} + +// Tests for optionality of parameters and properties. + +type RetU = + T extends true ? 1 : + T extends false ? 2 : + T extends undefined ? 3 : + never; + +function fn1(param?: T): RetU { // Bad. + if (param == undefined) { + return 3; + } + if (param) { + return 1; + } + return 2; +} + +function fn2(param?: T): RetU { + if (param == undefined) { + return 3; + } + if (param) { + return 1; + } + return 2; +} + +function fn3(param: { prop?: T }): RetU { + if (param.prop == undefined) { + return 3; + } + if (param.prop) { + return 1; + } + return 2; +} + +function fn4({ prop }: { prop?: T }): RetU { + if (prop == undefined) { + return 3; + } + if (prop) { + return 1; + } + return 2; +} + +function fn5(param?: { prop: T }): RetU { // Bad. + if (param.prop == undefined) { + return 3; + } + if (param.prop) { + return 1; + } + return 2; +} + +function fn6(param: { prop1?: { prop?: T } }): RetU { // Bad. + if (param.prop1.prop == undefined) { + return 3; + } + if (param.prop1.prop) { + return 1; + } + return 2; +} + +type Not = T extends true ? false : true; + +function transformedTypeArgument(param: Type5>): Ret { // Bad. + return param.prop ? 1 : 2; +} \ No newline at end of file diff --git a/tsc/testdata/tests/cases/compiler/dependentReturnType12.ts b/tsc/testdata/tests/cases/compiler/dependentReturnType12.ts new file mode 100644 index 0000000000000..dd02e96177e33 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/dependentReturnType12.ts @@ -0,0 +1,45 @@ +// @noEmit: true +// @strict: false + + +// Tests for optionality of parameters and properties. + +type RetU = + T extends string ? 2 : + T extends number ? 1 : + never; + +function fn1(param?: T): RetU { + if (typeof param === "number") { + return 1; + } + return 2; +} + +function fn3(param: { prop?: T }): RetU { + if (typeof param.prop === "number") { + return 1; + } + return 2; +} + +function fn4({ prop }: { prop?: T }): RetU { + if (typeof prop === "number") { + return 1; + } + return 2; +} + +function fn5(param?: { prop: T }): RetU { // Bad. + if (typeof param.prop === "number") { + return 1; + } + return 2; +} + +function fn6(param: { prop1?: { prop?: T } }): RetU { // Bad. + if (typeof param.prop1.prop === "number") { + return 1; + } + return 2; +} \ No newline at end of file diff --git a/tsc/testdata/tests/cases/compiler/dependentReturnType13.ts b/tsc/testdata/tests/cases/compiler/dependentReturnType13.ts new file mode 100644 index 0000000000000..7278050ce4a0c --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/dependentReturnType13.ts @@ -0,0 +1,122 @@ +// @noEmit: true +// @strict: true + + +// Restrictions on what kind of union types can be narrowed. + +function f1(param: T): + T extends string ? 1 : + T extends string[] ? 2 : + never { + if (Array.isArray(param)) { + return 2; + } + return 1; +} + +function f2(param: T): + T extends string ? 1 : + T extends string[] | number[] ? 2 : + never { + if (Array.isArray(param)) { + return 2; + } + return 1; +} + +declare function isNumberArray(x: unknown): x is number[]; + +function f3(param: T): // Bad. + T extends string ? 1 : + T extends string[] ? 2 : + T extends number[] ? 3 : + never { + if (isNumberArray(param)) { + return 3; + } + if (Array.isArray(param)) { + return 2; + } + return 1; +} + +class Dog { + bark(): void {} +} + +class Cat { + meow(): void {} + +} + +function f4(param: T): // Bad. + T extends Cat ? 1 : + T extends Dog ? 2 : + never { + if ('bark' in param) { + const _: Dog = param; + return 2; + } + const _: Cat = param; + return 1; +} + +function f5(param: T): + T extends string ? 1 : + T extends number | string[] ? 2 : + never { + if (Array.isArray(param) || typeof param === "number") { + const _: string[] | number = param; + return 2; + } + const _: string = param; + return 1; +} + +function f6(param: T, other: U): + T extends number[] ? 2 : + U extends true ? + T extends string[] ? 3 : + T extends string ? 1 : + never : + U extends false ? + T extends string[] ? 4 : + T extends string ? 5 : + never : + never { + if (isNumberArray(param)) { + return 2; + } + if (other) { + if (Array.isArray(param)) { + return 3; + } + return 1; + } + if (Array.isArray(param)) { + return 4; + } + return 5; +} + +function f7(param: T, other: U): + U extends number ? + T extends string[] ? 2 : + T extends string ? 1 : + never : + U extends number[] ? + T extends string[] ? 4 : + T extends string ? 3 : + never : + never { + if (Array.isArray(other)) { + if (Array.isArray(param)) { + return 4; + } + return 3; + } + if (Array.isArray(param)) { + return 2; + } + return 1; +} \ No newline at end of file diff --git a/tsc/testdata/tests/cases/compiler/dependentReturnType2.ts b/tsc/testdata/tests/cases/compiler/dependentReturnType2.ts new file mode 100644 index 0000000000000..0f14e3f7faa87 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/dependentReturnType2.ts @@ -0,0 +1,307 @@ +// @strict: true +// @noEmit: true +// @target: esnext +// @checkJs: true +// @filename: file.js + +// Adapted from ts-error-deltas repos + +/** + * @template T + * @template A + * @template R1 + * @template B + * @template R2 + * @typedef {T extends A ? R1 : T extends B ? R2 : never} HelperCond + */ + +/** + * @typedef IMessage + * @property {string} [html] + * @property {Object[]} [tokens] + */ + +class NewKatex { + /** + * @param {string} s + * @returns {string} + */ + render(s) { + return ""; + } + + /** + * @template {string | IMessage} T + * @param {T} message + * @returns {T extends string ? string : T extends IMessage ? IMessage : never} + */ + renderMessage(message) { + if (typeof message === 'string') { + return this.render(message); // Ok + } + + if (!message.html?.trim()) { + return message; // Ok + } + + if (!message.tokens) { + message.tokens = []; + } + + message.html = this.render(message.html); + return message; // Ok + } +} + +/** + * @template {true | false} T + * @param {{ dollarSyntax: boolean; parenthesisSyntax: boolean; }} options + * @param {T} _isMessage + * @returns {T extends true ? (message: IMessage) => IMessage : T extends false ? (message: string) => string : never} + */ +function createKatexMessageRendering(options, _isMessage) { + const instance = new NewKatex(); + if (_isMessage) { + return (/** @type {IMessage} */ message) => instance.renderMessage(message); // Ok + } + return (/** @type {string} */ message) => instance.renderMessage(message); // Ok +} + +// File: Rocket.Chat/apps/meteor/app/settings/lib/settings.ts + +/** + * @typedef {Record} MyObj + */ + + +/** + * @typedef {MyObj} SettingValue + */ + +/** + * @template {SettingValue} T + * @typedef {Object} SettingComposedValue + * @property {string} key + * @property {SettingValue} value + */ + +/** + * @callback SettingCallback + * @param {string} key + * @param {SettingValue} value + * @param {boolean} [initialLoad] + * @returns {void} + */ + +/** @type {{ settings: { [s: string]: any } }} */ +const Meteor = /** @type {any} */ (undefined); +/** @type {{ isRegExp(x: unknown): x is RegExp; }} */ +const _ = /** @type {any} */ (undefined); + +/** + * @param {RegExp} x + * @returns {void} + */ +function takesRegExp(x) { + return /** @type {any} */ undefined; +} +/** + * @param {string} x + * @returns {void} + */ +function takesString(x) { + return /** @type {any} */ undefined; +} + +/** + * @class NewSettingsBase + */ +class NewSettingsBase { + /** + * @template {SettingCallback | undefined} C + * @template {string | RegExp} I + * @template {SettingValue} T + * @param {I} _id + * @param {C} [callback] + * @returns {HelperCond[]>>} + */ + newGet(_id, callback) { + if (callback !== undefined) { + if (!Meteor.settings) { + return; // Ok + } + if (_id === '*') { + return Object.keys(Meteor.settings).forEach((key) => { + const value = Meteor.settings[key]; + callback(key, value); + }); + } + if (_.isRegExp(_id) && Meteor.settings) { + return Object.keys(Meteor.settings).forEach((key) => { + if (!_id.test(key)) { + return; + } + const value = Meteor.settings[key]; + callback(key, value); + }); + } + + if (typeof _id === 'string') { + const value = Meteor.settings[_id]; + if (value != null) { + callback(_id, Meteor.settings[_id]); + } + return; // Ok + } + + return; // Ok, needed for exhaustiveness check + } + + if (!Meteor.settings) { + return undefined; // Error + } + + if (_.isRegExp(_id)) { + return Object.keys(Meteor.settings).reduce((/** @type {SettingComposedValue[]} */ items, key) => { + const value = Meteor.settings[key]; + if (_id.test(key)) { + items.push({ key, value }); + } + return items; + }, []); // Ok + } + + return Meteor.settings?.[_id]; // Error + } +} + +// File: Rocket.Chat/apps/meteor/app/ui-utils/client/lib/messageBox.ts + +/** + * @typedef {MyObj} MessageBoxAction + */ + +/** + * @template {string | undefined} T + * @param {T} group + * @returns {HelperCond>} + */ +function getWithBug(group) { + if (!group) { + return /** @type {Record} */({}); // Error + } + return /** @type {MessageBoxAction[]} */([]); // Ok +} + +/** + * @template {string | undefined} T + * @param {T} group + * @returns {HelperCond>} + */ +function getWithoutBug(group) { + if (group === undefined) { + return /** @type {Record} */({}); // Ok + } + return /** @type {MessageBoxAction[]} */([]); // Ok +} + +// File: Rocket.Chat/apps/meteor/ee/server/lib/engagementDashboard/date.ts + +/** + * @param {string} x + * @returns {Date} + */ +function mapDateForAPI(x) { + return /** @type {any} */ (undefined); +} + +/** + * @template {string | undefined} T + * @param {string} start + * @param {T} [end] + * @returns {HelperCond} + */ +function transformDatesForAPI(start, end) { + return end !== undefined ? + { + start: mapDateForAPI(start), + end: mapDateForAPI(end), + } : + { + start: mapDateForAPI(start), + end: undefined + }; +} + +// File: Rocket.Chat/packages/agenda/src/Agenda.ts + +/** + * @typedef {MyObj} RepeatOptions + */ + +/** + * @typedef {MyObj} Job + */ + +/** + * @typedef {Object} IJob + * @property {MyObj} data + */ +class NewAgenda { + /** + * @param {string | number} interval + * @param {string} name + * @param {IJob['data']} data + * @param {RepeatOptions} options + * @returns {Promise} + */ + async _createIntervalJob(interval, name, data, options) { + return /** @type {any} */ (undefined); + } + + /** + * @param {string | number} interval + * @param {string[]} names + * @param {IJob['data']} data + * @param {RepeatOptions} options + * @returns {Promise | undefined} + */ + _createIntervalJobs(interval, names, data, options) { + return undefined; + } + + /** + * @template {string | string[]} T + * @param {string | number} interval + * @param {T} name + * @param {IJob['data']} data + * @param {RepeatOptions} options + * @returns {Promise>} + */ + async newEvery(interval, name, data, options) { + if (typeof name === 'string') { + return this._createIntervalJob(interval, name, data, options); // Ok + } + + if (Array.isArray(name)) { + return this._createIntervalJobs(interval, name, data, options); // Ok + } + + throw new Error('Unexpected error: Invalid job name(s)'); + } +} + +// File: angular/packages/common/src/pipes/case_conversion_pipes.ts + +/** + * @template {string | null | undefined} T + * @param {T} value + * @returns {HelperCond} + */ +function transform1(value) { + if (value == null) return null; // Ok + if (typeof value !== 'string') { + throw new Error(); + } + return value.toLowerCase(); // Ok +} diff --git a/tsc/testdata/tests/cases/compiler/dependentReturnType3.ts b/tsc/testdata/tests/cases/compiler/dependentReturnType3.ts new file mode 100644 index 0000000000000..df60c0677fd5d --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/dependentReturnType3.ts @@ -0,0 +1,216 @@ +// @strict: true +// @noEmit: true +// @target: ES6 + +// Adapted from ts-error-deltas repos + +type HelperCond = + T extends A + ? R1 + : T extends B + ? R2 + : never; + + +// File: Rocket.Chat/apps/meteor/app/katex/client/index.ts +interface IMessage { + html?: string; + tokens?: {}[]; +} + +class NewKatex { + render(s: string): string { + return ""; + } + + renderMessage(message: T): + T extends string + ? string + : T extends IMessage + ? IMessage + : never { + if (typeof message === 'string') { + return this.render(message); // Ok + } + + if (!message.html?.trim()) { + return message; // Ok + } + + if (!message.tokens) { + message.tokens = []; + } + + message.html = this.render(message.html); + return message; // Ok + } +} + +export function createKatexMessageRendering( + options: { + dollarSyntax: boolean; + parenthesisSyntax: boolean; + }, + _isMessage: T, +): T extends true + ? (message: IMessage) => IMessage + : T extends false + ? (message: string) => string + : never { + const instance = new NewKatex(); + if (_isMessage) { + return (message: IMessage): IMessage => instance.renderMessage(message); // Ok + } + return (message: string): string => instance.renderMessage(message); // Ok +} + +// File: Rocket.Chat/apps/meteor/app/settings/lib/settings.ts +type SettingComposedValue = { key: string; value: T }; +type SettingCallback = (key: string, value: SettingValue, initialLoad?: boolean) => void; + +type SettingValue = object; +declare const Meteor: { settings: { [s: string]: any } }; +declare const _: { isRegExp(x: unknown): x is RegExp; }; +declare function takesRegExp(x: RegExp): void; +declare function takesString(x: string): void; + +class NewSettingsBase { + public newGet( + _id: I, + callback?: C, + ): HelperCond[]>> { + if (callback !== undefined) { + if (!Meteor.settings) { + return; // Ok + } + if (_id === '*') { + return Object.keys(Meteor.settings).forEach((key) => { // Ok + const value = Meteor.settings[key]; + callback(key, value); + }); + } + if (_.isRegExp(_id) && Meteor.settings) { + return Object.keys(Meteor.settings).forEach((key) => { // Ok + if (!_id.test(key)) { + return; + } + const value = Meteor.settings[key]; + callback(key, value); + }); + } + + if (typeof _id === 'string') { + const value = Meteor.settings[_id]; + if (value != null) { + callback(_id, Meteor.settings[_id]); + } + return; // Ok + } + + return; // Ok, needed for exhaustiveness check + } + + if (!Meteor.settings) { // Wrong: we don't know that _id is string here, cannot return undefined + return undefined; // Error + } + + if (_.isRegExp(_id)) { + return Object.keys(Meteor.settings).reduce((items: SettingComposedValue[], key) => { + const value = Meteor.settings[key]; + if (_id.test(key)) { + items.push({ + key, + value, + }); + } + return items; + }, []); // Ok + } + + return Meteor.settings?.[_id]; // Error + // The indexing currently doesn't work because it doesn't use the narrowed type of `_id`. + } +} + +// File: Rocket.Chat/apps/meteor/app/ui-utils/client/lib/messageBox.ts +type MessageBoxAction = object; + +function getWithBug(group: T): +HelperCond> { + if (!group) { + return {} as Record; // Error, could fall into this branch when group is empty string + } + + return [] as MessageBoxAction[]; // Ok +} + +function getWithoutBug(group: T): +HelperCond> { + if (group === undefined) { + return {} as Record; // Ok + } + + return [] as MessageBoxAction[]; // Ok +} + +// File: Rocket.Chat/apps/meteor/ee/server/lib/engagementDashboard/date.ts +declare function mapDateForAPI(x: string): Date; +export function transformDatesForAPI( + start: string, + end?: T +): HelperCond { + return end !== undefined ? // Ok + { + start: mapDateForAPI(start), + end: mapDateForAPI(end), + } : + { + start: mapDateForAPI(start), + end: undefined + }; +} + +// File: Rocket.Chat/packages/agenda/src/Agenda.ts +type RepeatOptions = object; +type Job = object; +type IJob = { data: object }; +class NewAgenda { + public async _createIntervalJob(interval: string | number, name: string, data: IJob['data'], options: RepeatOptions): Promise { return undefined as any; } + private _createIntervalJobs( + interval: string | number, + names: string[], + data: IJob['data'], + options: RepeatOptions, + ): Promise | undefined { return undefined as any; } + + public async newEvery( + interval: string | number, + name: T, + data: IJob['data'], + options: RepeatOptions): Promise> { + if (typeof name === 'string') { + return this._createIntervalJob(interval, name, data, options); // Ok + } + + if (Array.isArray(name)) { + return this._createIntervalJobs(interval, name, data, options); // Ok + // Possible bug in original: createIntervalJobs can return undefined, but the original overload did not acount for that. + } + + throw new Error('Unexpected error: Invalid job name(s)'); + } +} + +// File: angular/packages/common/src/pipes/case_conversion_pipes.ts + +function transform1(value: T): HelperCond { + if (value == null) return null; // Ok + if (typeof value !== 'string') { + throw new Error(); + } + return value.toLowerCase(); // Ok +} \ No newline at end of file diff --git a/tsc/testdata/tests/cases/compiler/dependentReturnType4.ts b/tsc/testdata/tests/cases/compiler/dependentReturnType4.ts new file mode 100644 index 0000000000000..166b8497fa3eb --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/dependentReturnType4.ts @@ -0,0 +1,36 @@ +// @strict: true +// @noEmit: true +// @target: ES2022 +// @exactOptionalPropertyTypes: true + +declare const rand: { a?: never }; +type Missing = typeof rand.a; + +// Detection of valid optional parameter references + +// Ok, will narrow return type +function bar1(x?: T): + T extends Missing ? 0 : T extends string ? 1 : never { + if (x === undefined) { + return 0; + } + return 1; +} + +// Ok, will narrow return type +function bar2(x?: T): + T extends undefined ? 0 : T extends string ? 1 : never { + if (x === undefined) { + return 0; + } + return 1; +} + +// Not ok, will not narrow return type +function bar3(x?: T): + T extends undefined ? 0 : T extends string ? 1 : never { + if (x === undefined) { + return 0; + } + return 1; +} \ No newline at end of file diff --git a/tsc/testdata/tests/cases/compiler/dependentReturnType5.ts b/tsc/testdata/tests/cases/compiler/dependentReturnType5.ts new file mode 100644 index 0000000000000..34c831a821662 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/dependentReturnType5.ts @@ -0,0 +1,99 @@ +// @strict: true +// @noEmit: true + +// Indexed access return type +interface A1 { + "prop": true; + [s: string]: boolean; +} + +// This was already allowed but is unsound. +function foo1(x: T): A1[T] { + return false; +} +const rfoo1 = foo1("prop"); // Type says true, but actually returns false. + +interface A2 { + "prop": true; + [n: number]: string; +} + +// We could soundly allow that, because `"prop"` and `[n: number]` are disjoint types. +function foo2(x: T): A2[T] { + if (x === "prop") { + return true; + } + return "some string"; +} +const rfoo2 = foo2("prop"); +const rfoo22 = foo2(34); +const rfoo222 = foo2(Math.random() ? "prop" : 34); + +interface A3 { + [s: string]: boolean; +} + +// No need for return type narrowing. +function foo3(x: T): A3[T] { + if (Math.random()) return true; + return false; +} + +interface Comp { + foo: 2; + [n: number]: 3; + [s: string]: 2 | 3 | 4; +} + +function indexedComp(x: T): Comp[T] { + if (x === "foo") { + if (Math.random()) { + return 3; // Should be error, but already allowed + } + return 2; // Ok + } + if (typeof x === "number") { + if (Math.random()) { + return 2; // Error + } + return 3; // // Should be error, but already allowed + } + return 4; // Ok +} + +function indexedComp2(x: T): Comp[T] { + if (Math.random()) { + return 3; // Bad, unsound + } + return 2; // Error +} + + +// Most common case supported: +interface F { + "t": number, + "f": boolean, +} + +// Ok +function depLikeFun(str: T): F[T] { + if (str === "t") { + return 1; + } else { + return true; + } +} + +depLikeFun("t"); // has type number +depLikeFun("f"); // has type boolean + +type IndirectF = F[T]; + +// Ok +function depLikeFun2(str: T): IndirectF { + if (str === "t") { + return 1; + } else { + return true; + } +} \ No newline at end of file diff --git a/tsc/testdata/tests/cases/compiler/dependentReturnType6.ts b/tsc/testdata/tests/cases/compiler/dependentReturnType6.ts new file mode 100644 index 0000000000000..4a319fa7f401a --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/dependentReturnType6.ts @@ -0,0 +1,137 @@ +// @strict: true +// @noEmit: true +// @target: esnext + +// Tests for when return type narrowing can and cannot happen + +// @filename: file.ts +// Type parameter in outer scope +function outer(x: T): number { + return inner(); + + function inner(): T extends true ? 1 : T extends false ? 2 : never { + return x ? 1 : 2; + } +} + +// Overloads +function fun6(x: T, y: string): T extends true ? string : T extends false ? 2 : never; +function fun6(x: T, y: undefined): T extends true ? 1 : T extends false ? 2 : never; +function fun6(x: boolean): 1 | 2 | string; +function fun6(x: T, y?: string): T extends true ? 1 | string : T extends false ? 2 : never { + return x ? y !== undefined ? y : 1 : 2; +} + +// Indexed access with conditional inside + +// DOESN'T NARROW the nested conditional type of wrong shape +interface SomeInterfaceBad { + prop1: T extends 1 ? true : T extends 2 ? false : never; + prop2: T extends true ? 1 : T extends false ? 2 : never; +} + +function fun4bad>(x: T, y: U): SomeInterfaceBad[U] { + if (y === "prop1") { + return x === 1 ? true : false; + } + return x ? 1 : 2; +} + +// Narrows nested conditional type of right shape +interface SomeInterfaceGood { + prop1: T extends true ? 2 : T extends false ? 1 : never; + prop2: T extends true ? 1 : T extends false ? 2 : never; +} + +function fun4good>(x: T, y: U): SomeInterfaceGood[U] { + if (y === "prop1") { + return x ? 2 : 1; + } + return x ? 1 : 2; +} + +// Indexed access with indexed access inside - OK, narrows +interface BB { + "a": number; + "b": string; +} + +interface AA { + "c": BB[T]; + "d": boolean, +} + +function reduction>(x: T, y: U): AA[U] { + if (x === "a" && y === "c") { + return 0; // Ok + } + + return undefined as never; +} + +// Conditional with indexed access inside - OK, narrows +function fun5(x: T, y: U): T extends 1 ? BB[U] : T extends 2 ? boolean : never { + if (x === 1) { + if (y === "a") { + return 0; + } + return ""; + } + return true; +} + +// `this` type parameter - Doesn't narrow +abstract class SomeClass { + fun3(this: Sub1 | Sub2): this extends Sub1 ? 1 : this extends Sub2 ? 2 : never { + if (this instanceof Sub1) { + return 1; + } + return 2; + } +} +class Sub1 extends SomeClass { + #sub1!: symbol; +}; +class Sub2 extends SomeClass { + #sub2!: symbol; +}; + +// Detection of type parameter reference in presence of typeof +function fun2(x: T, y: typeof x): T extends true ? 1 : T extends false ? 2 : never { + return x ? 1 : 2; +} + +// Contextually-typed lambdas +const fun1: (x: T) => T extends true ? 1 : T extends false ? 2 : never = (x) => x ? 1 : 2; + + +// Circular conditionals +type SomeCond = T extends true ? 1 : T extends false ? SomeCond : never; + +function f7(x: T): SomeCond { + if (x) { + return 1; + } + return 2; +} + +// Composite instantiation of conditional type +type OtherCond = T extends 1 ? "one" : T extends 2 ? "two" : T extends 3 ? "three" : T extends 4 ? "four" : never; + +function f8(x: U, y: V): OtherCond { + if (x === 1 && y === 3) { + return "one"; + } +} + +// Conditionals with `infer` - will not narrow, it is not safe to infer from the narrowed type into an `infer` type parameter +function f9(x: T): T extends Array ? P : T extends number ? undefined : never { + if (Array.isArray(x)) { + // If we allowed narrowing of the conditional return type, when resolving the conditional `T & ("a"[] | "b"[]) extends Array ? P : ...`, + // we could infer `"a" | "b"` for `P`, and allow "a" to be returned. However, when calling `f10`, `T` could be instantiated with `"b"[]`, and the return type would be `"b"`, + // so allowing an `"a"` return would be unsound. + return "a"; + } + return undefined; +} + diff --git a/tsc/testdata/tests/cases/compiler/dependentReturnType8.ts b/tsc/testdata/tests/cases/compiler/dependentReturnType8.ts new file mode 100644 index 0000000000000..169d5f540c2b6 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/dependentReturnType8.ts @@ -0,0 +1,13 @@ +// @strict: true +// @noEmit: true + + +export {}; + +declare const record: Record; +declare const array: string[]; + +// Arrow function with expression body +const getObject = + (group: T): T extends string ? string[] : T extends undefined ? Record : never => + group === undefined ? record : array; \ No newline at end of file diff --git a/tsc/testdata/tests/cases/compiler/dependentReturnType9.ts b/tsc/testdata/tests/cases/compiler/dependentReturnType9.ts new file mode 100644 index 0000000000000..65437a8aea1c8 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/dependentReturnType9.ts @@ -0,0 +1,67 @@ +// @strict: true +// @noEmit: true + +type Payload = + | { _tag: "auth"; username: string; password: string } + | { _tag: "cart"; items: Array<{ id: string; quantity: number }> } + | { _tag: "person"; name: string; age: number }; + +type PayloadContent = { + [P in Payload as P["_tag"]]: Omit; +}; + +// ok, exhaustive cases and default case with throw +function mockPayload( + str: P_TAG, +): PayloadContent[P_TAG] { + switch (str) { + case "auth": + return { username: "test", password: "admin" }; + case "cart": + return { items: [{ id: "123", quantity: 123 }] }; + case "person": + return { name: "andrea", age: 27 }; + default: + throw new Error("unknown tag"); + } +} + +// ok, non-exhaustive cases but default case with throw +function mockPayload2( + str: P_TAG, +): PayloadContent[P_TAG] { + switch (str) { + case "auth": + return { username: "test", password: "admin" }; + case "cart": + return { items: [{ id: "123", quantity: 123 }] }; + default: + throw new Error("unhandled tag"); + } +} + +// ok, exhaustive cases +function mockPayload3( + str: P_TAG, +): PayloadContent[P_TAG] { + switch (str) { + case "auth": + return { username: "test", password: "admin" }; + case "cart": + return { items: [{ id: "123", quantity: 123 }] }; + case "person": + return { name: "andrea", age: 27 }; + } +} + +// error, non-exhaustive cases +function mockPayload4( + str: P_TAG, +): PayloadContent[P_TAG] { + switch (str) { + case "auth": + return { username: "test", password: "admin" }; + case "cart": + return { items: [{ id: "123", quantity: 123 }] }; + } +}