diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index fafa67c5121dd..402af17c5edab 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -578,12 +578,6 @@ namespace ts { } } - function isNarrowableReference(expr: Expression): boolean { - return expr.kind === SyntaxKind.Identifier || - expr.kind === SyntaxKind.ThisKeyword || - expr.kind === SyntaxKind.PropertyAccessExpression && isNarrowableReference((expr).expression); - } - function isNarrowingExpression(expr: Expression): boolean { switch (expr.kind) { case SyntaxKind.Identifier: @@ -591,7 +585,7 @@ namespace ts { case SyntaxKind.PropertyAccessExpression: return isNarrowableReference(expr); case SyntaxKind.CallExpression: - return true; + return hasNarrowableArgument(expr); case SyntaxKind.ParenthesizedExpression: return isNarrowingExpression((expr).expression); case SyntaxKind.BinaryExpression: @@ -602,6 +596,39 @@ namespace ts { return false; } + function isNarrowableReference(expr: Expression): boolean { + return expr.kind === SyntaxKind.Identifier || + expr.kind === SyntaxKind.ThisKeyword || + expr.kind === SyntaxKind.PropertyAccessExpression && isNarrowableReference((expr).expression); + } + + function hasNarrowableArgument(expr: CallExpression) { + if (expr.arguments) { + for (const argument of expr.arguments) { + if (isNarrowableReference(argument)) { + return true; + } + } + } + if (expr.expression.kind === SyntaxKind.PropertyAccessExpression && + isNarrowableReference((expr.expression).expression)) { + return true; + } + return false; + } + + function isNarrowingNullCheckOperands(expr1: Expression, expr2: Expression) { + return (expr1.kind === SyntaxKind.NullKeyword || expr1.kind === SyntaxKind.Identifier && (expr1).text === "undefined") && isNarrowableOperand(expr2); + } + + function isNarrowingTypeofOperands(expr1: Expression, expr2: Expression) { + return expr1.kind === SyntaxKind.TypeOfExpression && isNarrowableOperand((expr1).expression) && expr2.kind === SyntaxKind.StringLiteral; + } + + function isNarrowingDiscriminant(expr: Expression) { + return expr.kind === SyntaxKind.PropertyAccessExpression && isNarrowableReference((expr).expression); + } + function isNarrowingBinaryExpression(expr: BinaryExpression) { switch (expr.operatorToken.kind) { case SyntaxKind.EqualsToken: @@ -610,34 +637,35 @@ namespace ts { case SyntaxKind.ExclamationEqualsToken: case SyntaxKind.EqualsEqualsEqualsToken: case SyntaxKind.ExclamationEqualsEqualsToken: - if ((isNarrowingExpression(expr.left) && (expr.right.kind === SyntaxKind.NullKeyword || expr.right.kind === SyntaxKind.Identifier)) || - (isNarrowingExpression(expr.right) && (expr.left.kind === SyntaxKind.NullKeyword || expr.left.kind === SyntaxKind.Identifier))) { - return true; - } - if (isTypeOfNarrowingBinaryExpression(expr)) { - return true; - } - return false; + return isNarrowingNullCheckOperands(expr.right, expr.left) || isNarrowingNullCheckOperands(expr.left, expr.right) || + isNarrowingTypeofOperands(expr.right, expr.left) || isNarrowingTypeofOperands(expr.left, expr.right) || + isNarrowingDiscriminant(expr.left) || isNarrowingDiscriminant(expr.right); case SyntaxKind.InstanceOfKeyword: - return isNarrowingExpression(expr.left); + return isNarrowableOperand(expr.left); case SyntaxKind.CommaToken: return isNarrowingExpression(expr.right); } return false; } - function isTypeOfNarrowingBinaryExpression(expr: BinaryExpression) { - let typeOf: Expression; - if (expr.left.kind === SyntaxKind.StringLiteral) { - typeOf = expr.right; - } - else if (expr.right.kind === SyntaxKind.StringLiteral) { - typeOf = expr.left; - } - else { - typeOf = undefined; + function isNarrowableOperand(expr: Expression): boolean { + switch (expr.kind) { + case SyntaxKind.ParenthesizedExpression: + return isNarrowableOperand((expr).expression); + case SyntaxKind.BinaryExpression: + switch ((expr).operatorToken.kind) { + case SyntaxKind.EqualsToken: + return isNarrowableOperand((expr).left); + case SyntaxKind.CommaToken: + return isNarrowableOperand((expr).right); + } } - return typeOf && typeOf.kind === SyntaxKind.TypeOfExpression && isNarrowingExpression((typeOf).expression); + return isNarrowableReference(expr); + } + + function isNarrowingSwitchStatement(switchStatement: SwitchStatement) { + const expr = switchStatement.expression; + return expr.kind === SyntaxKind.PropertyAccessExpression && isNarrowableReference((expr).expression); } function createBranchLabel(): FlowLabel { @@ -683,8 +711,22 @@ namespace ts { setFlowNodeReferenced(antecedent); return { flags, - antecedent, expression, + antecedent + }; + } + + function createFlowSwitchClause(antecedent: FlowNode, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number): FlowNode { + if (!isNarrowingSwitchStatement(switchStatement)) { + return antecedent; + } + setFlowNodeReferenced(antecedent); + return { + flags: FlowFlags.SwitchClause, + switchStatement, + clauseStart, + clauseEnd, + antecedent }; } @@ -913,9 +955,12 @@ namespace ts { preSwitchCaseFlow = currentFlow; bind(node.caseBlock); addAntecedent(postSwitchLabel, currentFlow); - const hasNonEmptyDefault = forEach(node.caseBlock.clauses, c => c.kind === SyntaxKind.DefaultClause && c.statements.length); - if (!hasNonEmptyDefault) { - addAntecedent(postSwitchLabel, preSwitchCaseFlow); + const hasDefault = forEach(node.caseBlock.clauses, c => c.kind === SyntaxKind.DefaultClause); + // We mark a switch statement as possibly exhaustive if it has no default clause and if all + // case clauses have unreachable end points (e.g. they all return). + node.possiblyExhaustive = !hasDefault && !postSwitchLabel.antecedents; + if (!hasDefault) { + addAntecedent(postSwitchLabel, createFlowSwitchClause(preSwitchCaseFlow, node, 0, 0)); } currentBreakTarget = saveBreakTarget; preSwitchCaseFlow = savePreSwitchCaseFlow; @@ -924,25 +969,22 @@ namespace ts { function bindCaseBlock(node: CaseBlock): void { const clauses = node.clauses; + let fallthroughFlow = unreachableFlow; for (let i = 0; i < clauses.length; i++) { - const clause = clauses[i]; - if (clause.statements.length) { - if (currentFlow.flags & FlowFlags.Unreachable) { - currentFlow = preSwitchCaseFlow; - } - else { - const preCaseLabel = createBranchLabel(); - addAntecedent(preCaseLabel, preSwitchCaseFlow); - addAntecedent(preCaseLabel, currentFlow); - currentFlow = finishFlowLabel(preCaseLabel); - } - bind(clause); - if (!(currentFlow.flags & FlowFlags.Unreachable) && i !== clauses.length - 1 && options.noFallthroughCasesInSwitch) { - errorOnFirstToken(clause, Diagnostics.Fallthrough_case_in_switch); - } + const clauseStart = i; + while (!clauses[i].statements.length && i + 1 < clauses.length) { + bind(clauses[i]); + i++; } - else { - bind(clause); + const preCaseLabel = createBranchLabel(); + addAntecedent(preCaseLabel, createFlowSwitchClause(preSwitchCaseFlow, node.parent, clauseStart, i + 1)); + addAntecedent(preCaseLabel, fallthroughFlow); + currentFlow = finishFlowLabel(preCaseLabel); + const clause = clauses[i]; + bind(clause); + fallthroughFlow = currentFlow; + if (!(currentFlow.flags & FlowFlags.Unreachable) && i !== clauses.length - 1 && options.noFallthroughCasesInSwitch) { + errorOnFirstToken(clause, Diagnostics.Fallthrough_case_in_switch); } } } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 47b0a53a8c31c..1c582804d4a20 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5185,7 +5185,6 @@ namespace ts { if (hasProperty(stringLiteralTypes, text)) { return stringLiteralTypes[text]; } - const type = stringLiteralTypes[text] = createType(TypeFlags.StringLiteral); type.text = text; return type; @@ -5650,6 +5649,10 @@ namespace ts { return checkTypeComparableTo(source, target, /*errorNode*/ undefined); } + function areTypesComparable(type1: Type, type2: Type): boolean { + return isTypeComparableTo(type1, type2) || isTypeComparableTo(type2, type1); + } + function checkTypeSubtypeOf(source: Type, target: Type, errorNode: Node, headMessage?: DiagnosticMessage, containingMessageChain?: DiagnosticMessageChain): boolean { return checkTypeRelatedTo(source, target, subtypeRelation, errorNode, headMessage, containingMessageChain); } @@ -6830,8 +6833,10 @@ namespace ts { return !!getPropertyOfType(type, "0"); } - function isStringLiteralType(type: Type) { - return type.flags & TypeFlags.StringLiteral; + function isStringLiteralUnionType(type: Type): boolean { + return type.flags & TypeFlags.StringLiteral ? true : + type.flags & TypeFlags.Union ? forEach((type).types, isStringLiteralUnionType) : + false; } /** @@ -7696,6 +7701,35 @@ namespace ts { return node; } + function getTypeOfSwitchClause(clause: CaseClause | DefaultClause) { + if (clause.kind === SyntaxKind.CaseClause) { + const expr = (clause).expression; + return expr.kind === SyntaxKind.StringLiteral ? getStringLiteralTypeForText((expr).text) : checkExpression(expr); + } + return undefined; + } + + function getSwitchClauseTypes(switchStatement: SwitchStatement): Type[] { + const links = getNodeLinks(switchStatement); + if (!links.switchTypes) { + // If all case clauses specify expressions that have unit types, we return an array + // of those unit types. Otherwise we return an empty array. + const types = map(switchStatement.caseBlock.clauses, getTypeOfSwitchClause); + links.switchTypes = forEach(types, t => !t || t.flags & TypeFlags.StringLiteral) ? types : emptyArray; + } + return links.switchTypes; + } + + function eachTypeContainedIn(source: Type, types: Type[]) { + return source.flags & TypeFlags.Union ? !forEach((source).types, t => !contains(types, t)) : contains(types, source); + } + + function filterType(type: Type, f: (t: Type) => boolean): Type { + return type.flags & TypeFlags.Union ? + getUnionType(filter((type).types, f)) : + f(type) ? type : neverType; + } + function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean, includeOuterFunctions: boolean) { let key: string; if (!reference.flowNode || assumeInitialized && !(declaredType.flags & TypeFlags.Narrowable)) { @@ -7733,6 +7767,9 @@ namespace ts { else if (flow.flags & FlowFlags.Condition) { type = getTypeAtFlowCondition(flow); } + else if (flow.flags & FlowFlags.SwitchClause) { + type = getTypeAtSwitchClause(flow); + } else if (flow.flags & FlowFlags.Label) { if ((flow).antecedents.length === 1) { flow = (flow).antecedents[0]; @@ -7816,6 +7853,11 @@ namespace ts { return type; } + function getTypeAtSwitchClause(flow: FlowSwitchClause) { + const type = getTypeAtFlowNode(flow.antecedent); + return narrowTypeBySwitchOnDiscriminant(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd); + } + function getTypeAtFlowBranchLabel(flow: FlowLabel) { const antecedentTypes: Type[] = []; for (const antecedent of flow.antecedents) { @@ -7895,12 +7937,26 @@ namespace ts { case SyntaxKind.ExclamationEqualsToken: case SyntaxKind.EqualsEqualsEqualsToken: case SyntaxKind.ExclamationEqualsEqualsToken: - if (isNullOrUndefinedLiteral(expr.left) || isNullOrUndefinedLiteral(expr.right)) { - return narrowTypeByNullCheck(type, expr, assumeTrue); + const left = expr.left; + const operator = expr.operatorToken.kind; + const right = expr.right; + if (isNullOrUndefinedLiteral(right)) { + return narrowTypeByNullCheck(type, left, operator, right, assumeTrue); } - if (expr.left.kind === SyntaxKind.TypeOfExpression && expr.right.kind === SyntaxKind.StringLiteral || - expr.left.kind === SyntaxKind.StringLiteral && expr.right.kind === SyntaxKind.TypeOfExpression) { - return narrowTypeByTypeof(type, expr, assumeTrue); + if (isNullOrUndefinedLiteral(left)) { + return narrowTypeByNullCheck(type, right, operator, left, assumeTrue); + } + if (left.kind === SyntaxKind.TypeOfExpression && right.kind === SyntaxKind.StringLiteral) { + return narrowTypeByTypeof(type, left, operator, right, assumeTrue); + } + if (right.kind === SyntaxKind.TypeOfExpression && left.kind === SyntaxKind.StringLiteral) { + return narrowTypeByTypeof(type, right, operator, left, assumeTrue); + } + if (left.kind === SyntaxKind.PropertyAccessExpression) { + return narrowTypeByDiscriminant(type, left, operator, right, assumeTrue); + } + if (right.kind === SyntaxKind.PropertyAccessExpression) { + return narrowTypeByDiscriminant(type, right, operator, left, assumeTrue); } break; case SyntaxKind.InstanceOfKeyword: @@ -7911,41 +7967,35 @@ namespace ts { return type; } - function narrowTypeByNullCheck(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { - // We have '==', '!=', '===', or '!==' operator with 'null' or 'undefined' on one side - const operator = expr.operatorToken.kind; - const nullLike = isNullOrUndefinedLiteral(expr.left) ? expr.left : expr.right; - const narrowed = isNullOrUndefinedLiteral(expr.left) ? expr.right : expr.left; + function narrowTypeByNullCheck(type: Type, target: Expression, operator: SyntaxKind, literal: Expression, assumeTrue: boolean): Type { + // We have '==', '!=', '===', or '!==' operator with 'null' or 'undefined' as value if (operator === SyntaxKind.ExclamationEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) { assumeTrue = !assumeTrue; } - if (!strictNullChecks || !isMatchingReference(reference, getReferenceFromExpression(narrowed))) { + if (!strictNullChecks || !isMatchingReference(reference, getReferenceFromExpression(target))) { return type; } const doubleEquals = operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsToken; const facts = doubleEquals ? assumeTrue ? TypeFacts.EQUndefinedOrNull : TypeFacts.NEUndefinedOrNull : - nullLike.kind === SyntaxKind.NullKeyword ? + literal.kind === SyntaxKind.NullKeyword ? assumeTrue ? TypeFacts.EQNull : TypeFacts.NENull : assumeTrue ? TypeFacts.EQUndefined : TypeFacts.NEUndefined; return getTypeWithFacts(type, facts); } - function narrowTypeByTypeof(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { - // We have '==', '!=', '====', or !==' operator with 'typeof xxx' on the left - // and string literal on the right - const narrowed = getReferenceFromExpression(((expr.left.kind === SyntaxKind.TypeOfExpression ? expr.left : expr.right)).expression); - const literal = (expr.right.kind === SyntaxKind.StringLiteral ? expr.right : expr.left); - if (!isMatchingReference(reference, narrowed)) { + function narrowTypeByTypeof(type: Type, typeOfExpr: TypeOfExpression, operator: SyntaxKind, literal: LiteralExpression, assumeTrue: boolean): Type { + // We have '==', '!=', '====', or !==' operator with 'typeof xxx' and string literal operands + const target = getReferenceFromExpression(typeOfExpr.expression); + if (!isMatchingReference(reference, target)) { // For a reference of the form 'x.y', a 'typeof x === ...' type guard resets the // narrowed type of 'y' to its declared type. - if (containsMatchingReference(reference, narrowed)) { + if (containsMatchingReference(reference, target)) { return declaredType; } return type; } - if (expr.operatorToken.kind === SyntaxKind.ExclamationEqualsToken || - expr.operatorToken.kind === SyntaxKind.ExclamationEqualsEqualsToken) { + if (operator === SyntaxKind.ExclamationEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) { assumeTrue = !assumeTrue; } if (assumeTrue && !(type.flags & TypeFlags.Union)) { @@ -7963,6 +8013,58 @@ namespace ts { return getTypeWithFacts(type, facts); } + function narrowTypeByDiscriminant(type: Type, propAccess: PropertyAccessExpression, operator: SyntaxKind, value: Expression, assumeTrue: boolean): Type { + // We have '==', '!=', '===', or '!==' operator with property access as target + if (!isMatchingReference(reference, propAccess.expression)) { + return type; + } + const propName = propAccess.name.text; + const propType = getTypeOfPropertyOfType(type, propName); + if (!propType || !isStringLiteralUnionType(propType)) { + return type; + } + const discriminantType = value.kind === SyntaxKind.StringLiteral ? getStringLiteralTypeForText((value).text) : checkExpression(value); + if (!isStringLiteralUnionType(discriminantType)) { + return type; + } + if (operator === SyntaxKind.ExclamationEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) { + assumeTrue = !assumeTrue; + } + if (assumeTrue) { + return filterType(type, t => areTypesComparable(getTypeOfPropertyOfType(t, propName), discriminantType)); + } + if (discriminantType.flags & TypeFlags.StringLiteral) { + return filterType(type, t => getTypeOfPropertyOfType(t, propName) !== discriminantType); + } + return type; + } + + function narrowTypeBySwitchOnDiscriminant(type: Type, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number) { + // We have switch statement with property access expression + if (!isMatchingReference(reference, (switchStatement.expression).expression)) { + return type; + } + const propName = (switchStatement.expression).name.text; + const propType = getTypeOfPropertyOfType(type, propName); + if (!propType || !isStringLiteralUnionType(propType)) { + return type; + } + const switchTypes = getSwitchClauseTypes(switchStatement); + if (!switchTypes.length) { + return type; + } + const clauseTypes = switchTypes.slice(clauseStart, clauseEnd); + const hasDefaultClause = clauseStart === clauseEnd || contains(clauseTypes, undefined); + const caseTypes = hasDefaultClause ? filter(clauseTypes, t => !!t) : clauseTypes; + const discriminantType = caseTypes.length ? getUnionType(caseTypes) : undefined; + const caseType = discriminantType && filterType(type, t => isTypeComparableTo(discriminantType, getTypeOfPropertyOfType(t, propName))); + if (!hasDefaultClause) { + return caseType; + } + const defaultType = filterType(type, t => !eachTypeContainedIn(getTypeOfPropertyOfType(t, propName), switchTypes)); + return caseType ? getUnionType([caseType, defaultType]) : defaultType; + } + function narrowTypeByInstanceof(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { const left = getReferenceFromExpression(expr.left); if (!isMatchingReference(reference, left)) { @@ -8933,10 +9035,6 @@ namespace ts { return applyToContextualType(type, t => getIndexTypeOfStructuredType(t, kind)); } - function contextualTypeIsStringLiteralType(type: Type): boolean { - return !!(type.flags & TypeFlags.Union ? forEach((type).types, isStringLiteralType) : isStringLiteralType(type)); - } - // Return true if the given contextual type is a tuple-like type function contextualTypeIsTupleLikeType(type: Type): boolean { return !!(type.flags & TypeFlags.Union ? forEach((type).types, isTupleLikeType) : isTupleLikeType(type)); @@ -11760,10 +11858,42 @@ namespace ts { return aggregatedTypes; } + function isExhaustiveSwitchStatement(node: SwitchStatement): boolean { + const expr = node.expression; + if (!node.possiblyExhaustive || expr.kind !== SyntaxKind.PropertyAccessExpression) { + return false; + } + const type = checkExpression((expr).expression); + if (!(type.flags & TypeFlags.Union)) { + return false; + } + const propName = (expr).name.text; + const propType = getTypeOfPropertyOfType(type, propName); + if (!propType || !isStringLiteralUnionType(propType)) { + return false; + } + const switchTypes = getSwitchClauseTypes(node); + if (!switchTypes.length) { + return false; + } + return eachTypeContainedIn(propType, switchTypes); + } + + function functionHasImplicitReturn(func: FunctionLikeDeclaration) { + if (!(func.flags & NodeFlags.HasImplicitReturn)) { + return false; + } + const lastStatement = lastOrUndefined((func.body).statements); + if (lastStatement && lastStatement.kind === SyntaxKind.SwitchStatement && isExhaustiveSwitchStatement(lastStatement)) { + return false; + } + return true; + } + function checkAndAggregateReturnExpressionTypes(func: FunctionLikeDeclaration, contextualMapper: TypeMapper): Type[] { const isAsync = isAsyncFunctionLike(func); const aggregatedTypes: Type[] = []; - let hasReturnWithNoExpression = !!(func.flags & NodeFlags.HasImplicitReturn); + let hasReturnWithNoExpression = functionHasImplicitReturn(func); let hasReturnOfTypeNever = false; forEachReturnStatement(func.body, returnStatement => { const expr = returnStatement.expression; @@ -11820,7 +11950,7 @@ namespace ts { // If all we have is a function signature, or an arrow function with an expression body, then there is nothing to check. // also if HasImplicitReturn flag is not set this means that all codepaths in function body end with return or throw - if (nodeIsMissing(func.body) || func.body.kind !== SyntaxKind.Block || !(func.flags & NodeFlags.HasImplicitReturn)) { + if (nodeIsMissing(func.body) || func.body.kind !== SyntaxKind.Block || !functionHasImplicitReturn(func)) { return; } @@ -12598,7 +12728,7 @@ namespace ts { function checkStringLiteralExpression(node: StringLiteral): Type { const contextualType = getContextualType(node); - if (contextualType && contextualTypeIsStringLiteralType(contextualType)) { + if (contextualType && isStringLiteralUnionType(contextualType)) { return getStringLiteralTypeForText(node.text); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index db34bb7c6e861..5ef88df3f731f 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1188,6 +1188,7 @@ namespace ts { export interface SwitchStatement extends Statement { expression: Expression; caseBlock: CaseBlock; + possiblyExhaustive?: boolean; } // @kind(SyntaxKind.CaseBlock) @@ -1554,8 +1555,9 @@ namespace ts { Assignment = 1 << 4, // Assignment TrueCondition = 1 << 5, // Condition known to be true FalseCondition = 1 << 6, // Condition known to be false - Referenced = 1 << 7, // Referenced as antecedent once - Shared = 1 << 8, // Referenced as antecedent more than once + SwitchClause = 1 << 7, // Switch statement clause + Referenced = 1 << 8, // Referenced as antecedent once + Shared = 1 << 9, // Referenced as antecedent more than once Label = BranchLabel | LoopLabel, Condition = TrueCondition | FalseCondition } @@ -1591,6 +1593,13 @@ namespace ts { antecedent: FlowNode; } + export interface FlowSwitchClause extends FlowNode { + switchStatement: SwitchStatement; + clauseStart: number; // Start index of case/default clause range + clauseEnd: number; // End index of case/default clause range + antecedent: FlowNode; + } + export interface AmdDependency { path: string; name: string; @@ -2185,6 +2194,7 @@ namespace ts { resolvedJsxType?: Type; // resolved element attributes type of a JSX openinglike element hasSuperCall?: boolean; // recorded result when we try to find super-call. We only try to find one if this flag is undefined, indicating that we haven't made an attempt. superCall?: ExpressionStatement; // Cached first super-call found in the constructor. Used in checking whether super is called before this-accessing + switchTypes?: Type[]; // Cached array of switch case expression types } export const enum TypeFlags { diff --git a/tests/baselines/reference/discriminatedUnionTypes1.js b/tests/baselines/reference/discriminatedUnionTypes1.js new file mode 100644 index 0000000000000..8679689569a02 --- /dev/null +++ b/tests/baselines/reference/discriminatedUnionTypes1.js @@ -0,0 +1,252 @@ +//// [discriminatedUnionTypes1.ts] +interface Square { + kind: "square"; + size: number; +} + +interface Rectangle { + kind: "rectangle"; + width: number; + height: number; +} + +interface Circle { + kind: "circle"; + radius: number; +} + +type Shape = Square | Rectangle | Circle; + +function area1(s: Shape) { + if (s.kind === "square") { + return s.size * s.size; + } + else if (s.kind === "circle") { + return Math.PI * s.radius * s.radius; + } + else if (s.kind === "rectangle") { + return s.width * s.height; + } + else { + return 0; + } +} + +function area2(s: Shape) { + switch (s.kind) { + case "square": return s.size * s.size; + case "rectangle": return s.width * s.height; + case "circle": return Math.PI * s.radius * s.radius; + } +} + +function assertNever(x: never): never { + throw new Error("Unexpected object: " + x); +} + +function area3(s: Shape) { + switch (s.kind) { + case "square": return s.size * s.size; + case "rectangle": return s.width * s.height; + case "circle": return Math.PI * s.radius * s.radius; + default: return assertNever(s); + } +} + +function area4(s: Shape) { + switch (s.kind) { + case "square": return s.size * s.size; + case "rectangle": return s.width * s.height; + case "circle": return Math.PI * s.radius * s.radius; + } + return assertNever(s); +} + +type Message = + { kind: "A", x: string } | + { kind: "B" | "C", y: number } | + { kind: "D" }; + +function f1(m: Message) { + if (m.kind === "A") { + m; // { kind: "A", x: string } + } + else if (m.kind === "D") { + m; // { kind: "D" } + } + else { + m; // { kind: "B" | "C", y: number } + } +} + +function f2(m: Message) { + if (m.kind === "A") { + return; + } + m; // { kind: "B" | "C", y: number } | { kind: "D" } +} + +function f3(m: Message) { + if (m.kind === "X") { + m; // never + } +} + +function f4(m: Message, x: "A" | "D") { + if (m.kind == x) { + m; // { kind: "A", x: string } | { kind: "D" } + } +} + +function f5(m: Message) { + switch (m.kind) { + case "A": + m; // { kind: "A", x: string } + break; + case "D": + m; // { kind: "D" } + break; + default: + m; // { kind: "B" | "C", y: number } + } +} + +function f6(m: Message) { + switch (m.kind) { + case "A": + m; // { kind: "A", x: string } + case "D": + m; // { kind: "A", x: string } | { kind: "D" } + break; + default: + m; // { kind: "B" | "C", y: number } + } +} + +function f7(m: Message) { + switch (m.kind) { + case "A": + case "B": + return; + } + m; // { kind: "B" | "C", y: number } | { kind: "D" } +} + +function f8(m: Message) { + switch (m.kind) { + case "A": + return; + case "D": + throw new Error(); + } + m; // { kind: "B" | "C", y: number } +} + +//// [discriminatedUnionTypes1.js] +function area1(s) { + if (s.kind === "square") { + return s.size * s.size; + } + else if (s.kind === "circle") { + return Math.PI * s.radius * s.radius; + } + else if (s.kind === "rectangle") { + return s.width * s.height; + } + else { + return 0; + } +} +function area2(s) { + switch (s.kind) { + case "square": return s.size * s.size; + case "rectangle": return s.width * s.height; + case "circle": return Math.PI * s.radius * s.radius; + } +} +function assertNever(x) { + throw new Error("Unexpected object: " + x); +} +function area3(s) { + switch (s.kind) { + case "square": return s.size * s.size; + case "rectangle": return s.width * s.height; + case "circle": return Math.PI * s.radius * s.radius; + default: return assertNever(s); + } +} +function area4(s) { + switch (s.kind) { + case "square": return s.size * s.size; + case "rectangle": return s.width * s.height; + case "circle": return Math.PI * s.radius * s.radius; + } + return assertNever(s); +} +function f1(m) { + if (m.kind === "A") { + m; // { kind: "A", x: string } + } + else if (m.kind === "D") { + m; // { kind: "D" } + } + else { + m; // { kind: "B" | "C", y: number } + } +} +function f2(m) { + if (m.kind === "A") { + return; + } + m; // { kind: "B" | "C", y: number } | { kind: "D" } +} +function f3(m) { + if (m.kind === "X") { + m; // never + } +} +function f4(m, x) { + if (m.kind == x) { + m; // { kind: "A", x: string } | { kind: "D" } + } +} +function f5(m) { + switch (m.kind) { + case "A": + m; // { kind: "A", x: string } + break; + case "D": + m; // { kind: "D" } + break; + default: + m; // { kind: "B" | "C", y: number } + } +} +function f6(m) { + switch (m.kind) { + case "A": + m; // { kind: "A", x: string } + case "D": + m; // { kind: "A", x: string } | { kind: "D" } + break; + default: + m; // { kind: "B" | "C", y: number } + } +} +function f7(m) { + switch (m.kind) { + case "A": + case "B": + return; + } + m; // { kind: "B" | "C", y: number } | { kind: "D" } +} +function f8(m) { + switch (m.kind) { + case "A": + return; + case "D": + throw new Error(); + } + m; // { kind: "B" | "C", y: number } +} diff --git a/tests/baselines/reference/discriminatedUnionTypes1.symbols b/tests/baselines/reference/discriminatedUnionTypes1.symbols new file mode 100644 index 0000000000000..380694474239a --- /dev/null +++ b/tests/baselines/reference/discriminatedUnionTypes1.symbols @@ -0,0 +1,402 @@ +=== tests/cases/conformance/types/union/discriminatedUnionTypes1.ts === +interface Square { +>Square : Symbol(Square, Decl(discriminatedUnionTypes1.ts, 0, 0)) + + kind: "square"; +>kind : Symbol(Square.kind, Decl(discriminatedUnionTypes1.ts, 0, 18)) + + size: number; +>size : Symbol(Square.size, Decl(discriminatedUnionTypes1.ts, 1, 19)) +} + +interface Rectangle { +>Rectangle : Symbol(Rectangle, Decl(discriminatedUnionTypes1.ts, 3, 1)) + + kind: "rectangle"; +>kind : Symbol(Rectangle.kind, Decl(discriminatedUnionTypes1.ts, 5, 21)) + + width: number; +>width : Symbol(Rectangle.width, Decl(discriminatedUnionTypes1.ts, 6, 22)) + + height: number; +>height : Symbol(Rectangle.height, Decl(discriminatedUnionTypes1.ts, 7, 18)) +} + +interface Circle { +>Circle : Symbol(Circle, Decl(discriminatedUnionTypes1.ts, 9, 1)) + + kind: "circle"; +>kind : Symbol(Circle.kind, Decl(discriminatedUnionTypes1.ts, 11, 18)) + + radius: number; +>radius : Symbol(Circle.radius, Decl(discriminatedUnionTypes1.ts, 12, 19)) +} + +type Shape = Square | Rectangle | Circle; +>Shape : Symbol(Shape, Decl(discriminatedUnionTypes1.ts, 14, 1)) +>Square : Symbol(Square, Decl(discriminatedUnionTypes1.ts, 0, 0)) +>Rectangle : Symbol(Rectangle, Decl(discriminatedUnionTypes1.ts, 3, 1)) +>Circle : Symbol(Circle, Decl(discriminatedUnionTypes1.ts, 9, 1)) + +function area1(s: Shape) { +>area1 : Symbol(area1, Decl(discriminatedUnionTypes1.ts, 16, 41)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 18, 15)) +>Shape : Symbol(Shape, Decl(discriminatedUnionTypes1.ts, 14, 1)) + + if (s.kind === "square") { +>s.kind : Symbol(kind, Decl(discriminatedUnionTypes1.ts, 0, 18), Decl(discriminatedUnionTypes1.ts, 5, 21), Decl(discriminatedUnionTypes1.ts, 11, 18)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 18, 15)) +>kind : Symbol(kind, Decl(discriminatedUnionTypes1.ts, 0, 18), Decl(discriminatedUnionTypes1.ts, 5, 21), Decl(discriminatedUnionTypes1.ts, 11, 18)) + + return s.size * s.size; +>s.size : Symbol(Square.size, Decl(discriminatedUnionTypes1.ts, 1, 19)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 18, 15)) +>size : Symbol(Square.size, Decl(discriminatedUnionTypes1.ts, 1, 19)) +>s.size : Symbol(Square.size, Decl(discriminatedUnionTypes1.ts, 1, 19)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 18, 15)) +>size : Symbol(Square.size, Decl(discriminatedUnionTypes1.ts, 1, 19)) + } + else if (s.kind === "circle") { +>s.kind : Symbol(kind, Decl(discriminatedUnionTypes1.ts, 5, 21), Decl(discriminatedUnionTypes1.ts, 11, 18)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 18, 15)) +>kind : Symbol(kind, Decl(discriminatedUnionTypes1.ts, 5, 21), Decl(discriminatedUnionTypes1.ts, 11, 18)) + + return Math.PI * s.radius * s.radius; +>Math.PI : Symbol(Math.PI, Decl(lib.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>PI : Symbol(Math.PI, Decl(lib.d.ts, --, --)) +>s.radius : Symbol(Circle.radius, Decl(discriminatedUnionTypes1.ts, 12, 19)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 18, 15)) +>radius : Symbol(Circle.radius, Decl(discriminatedUnionTypes1.ts, 12, 19)) +>s.radius : Symbol(Circle.radius, Decl(discriminatedUnionTypes1.ts, 12, 19)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 18, 15)) +>radius : Symbol(Circle.radius, Decl(discriminatedUnionTypes1.ts, 12, 19)) + } + else if (s.kind === "rectangle") { +>s.kind : Symbol(Rectangle.kind, Decl(discriminatedUnionTypes1.ts, 5, 21)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 18, 15)) +>kind : Symbol(Rectangle.kind, Decl(discriminatedUnionTypes1.ts, 5, 21)) + + return s.width * s.height; +>s.width : Symbol(Rectangle.width, Decl(discriminatedUnionTypes1.ts, 6, 22)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 18, 15)) +>width : Symbol(Rectangle.width, Decl(discriminatedUnionTypes1.ts, 6, 22)) +>s.height : Symbol(Rectangle.height, Decl(discriminatedUnionTypes1.ts, 7, 18)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 18, 15)) +>height : Symbol(Rectangle.height, Decl(discriminatedUnionTypes1.ts, 7, 18)) + } + else { + return 0; + } +} + +function area2(s: Shape) { +>area2 : Symbol(area2, Decl(discriminatedUnionTypes1.ts, 31, 1)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 33, 15)) +>Shape : Symbol(Shape, Decl(discriminatedUnionTypes1.ts, 14, 1)) + + switch (s.kind) { +>s.kind : Symbol(kind, Decl(discriminatedUnionTypes1.ts, 0, 18), Decl(discriminatedUnionTypes1.ts, 5, 21), Decl(discriminatedUnionTypes1.ts, 11, 18)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 33, 15)) +>kind : Symbol(kind, Decl(discriminatedUnionTypes1.ts, 0, 18), Decl(discriminatedUnionTypes1.ts, 5, 21), Decl(discriminatedUnionTypes1.ts, 11, 18)) + + case "square": return s.size * s.size; +>s.size : Symbol(Square.size, Decl(discriminatedUnionTypes1.ts, 1, 19)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 33, 15)) +>size : Symbol(Square.size, Decl(discriminatedUnionTypes1.ts, 1, 19)) +>s.size : Symbol(Square.size, Decl(discriminatedUnionTypes1.ts, 1, 19)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 33, 15)) +>size : Symbol(Square.size, Decl(discriminatedUnionTypes1.ts, 1, 19)) + + case "rectangle": return s.width * s.height; +>s.width : Symbol(Rectangle.width, Decl(discriminatedUnionTypes1.ts, 6, 22)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 33, 15)) +>width : Symbol(Rectangle.width, Decl(discriminatedUnionTypes1.ts, 6, 22)) +>s.height : Symbol(Rectangle.height, Decl(discriminatedUnionTypes1.ts, 7, 18)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 33, 15)) +>height : Symbol(Rectangle.height, Decl(discriminatedUnionTypes1.ts, 7, 18)) + + case "circle": return Math.PI * s.radius * s.radius; +>Math.PI : Symbol(Math.PI, Decl(lib.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>PI : Symbol(Math.PI, Decl(lib.d.ts, --, --)) +>s.radius : Symbol(Circle.radius, Decl(discriminatedUnionTypes1.ts, 12, 19)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 33, 15)) +>radius : Symbol(Circle.radius, Decl(discriminatedUnionTypes1.ts, 12, 19)) +>s.radius : Symbol(Circle.radius, Decl(discriminatedUnionTypes1.ts, 12, 19)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 33, 15)) +>radius : Symbol(Circle.radius, Decl(discriminatedUnionTypes1.ts, 12, 19)) + } +} + +function assertNever(x: never): never { +>assertNever : Symbol(assertNever, Decl(discriminatedUnionTypes1.ts, 39, 1)) +>x : Symbol(x, Decl(discriminatedUnionTypes1.ts, 41, 21)) + + throw new Error("Unexpected object: " + x); +>Error : Symbol(Error, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(discriminatedUnionTypes1.ts, 41, 21)) +} + +function area3(s: Shape) { +>area3 : Symbol(area3, Decl(discriminatedUnionTypes1.ts, 43, 1)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 45, 15)) +>Shape : Symbol(Shape, Decl(discriminatedUnionTypes1.ts, 14, 1)) + + switch (s.kind) { +>s.kind : Symbol(kind, Decl(discriminatedUnionTypes1.ts, 0, 18), Decl(discriminatedUnionTypes1.ts, 5, 21), Decl(discriminatedUnionTypes1.ts, 11, 18)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 45, 15)) +>kind : Symbol(kind, Decl(discriminatedUnionTypes1.ts, 0, 18), Decl(discriminatedUnionTypes1.ts, 5, 21), Decl(discriminatedUnionTypes1.ts, 11, 18)) + + case "square": return s.size * s.size; +>s.size : Symbol(Square.size, Decl(discriminatedUnionTypes1.ts, 1, 19)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 45, 15)) +>size : Symbol(Square.size, Decl(discriminatedUnionTypes1.ts, 1, 19)) +>s.size : Symbol(Square.size, Decl(discriminatedUnionTypes1.ts, 1, 19)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 45, 15)) +>size : Symbol(Square.size, Decl(discriminatedUnionTypes1.ts, 1, 19)) + + case "rectangle": return s.width * s.height; +>s.width : Symbol(Rectangle.width, Decl(discriminatedUnionTypes1.ts, 6, 22)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 45, 15)) +>width : Symbol(Rectangle.width, Decl(discriminatedUnionTypes1.ts, 6, 22)) +>s.height : Symbol(Rectangle.height, Decl(discriminatedUnionTypes1.ts, 7, 18)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 45, 15)) +>height : Symbol(Rectangle.height, Decl(discriminatedUnionTypes1.ts, 7, 18)) + + case "circle": return Math.PI * s.radius * s.radius; +>Math.PI : Symbol(Math.PI, Decl(lib.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>PI : Symbol(Math.PI, Decl(lib.d.ts, --, --)) +>s.radius : Symbol(Circle.radius, Decl(discriminatedUnionTypes1.ts, 12, 19)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 45, 15)) +>radius : Symbol(Circle.radius, Decl(discriminatedUnionTypes1.ts, 12, 19)) +>s.radius : Symbol(Circle.radius, Decl(discriminatedUnionTypes1.ts, 12, 19)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 45, 15)) +>radius : Symbol(Circle.radius, Decl(discriminatedUnionTypes1.ts, 12, 19)) + + default: return assertNever(s); +>assertNever : Symbol(assertNever, Decl(discriminatedUnionTypes1.ts, 39, 1)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 45, 15)) + } +} + +function area4(s: Shape) { +>area4 : Symbol(area4, Decl(discriminatedUnionTypes1.ts, 52, 1)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 54, 15)) +>Shape : Symbol(Shape, Decl(discriminatedUnionTypes1.ts, 14, 1)) + + switch (s.kind) { +>s.kind : Symbol(kind, Decl(discriminatedUnionTypes1.ts, 0, 18), Decl(discriminatedUnionTypes1.ts, 5, 21), Decl(discriminatedUnionTypes1.ts, 11, 18)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 54, 15)) +>kind : Symbol(kind, Decl(discriminatedUnionTypes1.ts, 0, 18), Decl(discriminatedUnionTypes1.ts, 5, 21), Decl(discriminatedUnionTypes1.ts, 11, 18)) + + case "square": return s.size * s.size; +>s.size : Symbol(Square.size, Decl(discriminatedUnionTypes1.ts, 1, 19)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 54, 15)) +>size : Symbol(Square.size, Decl(discriminatedUnionTypes1.ts, 1, 19)) +>s.size : Symbol(Square.size, Decl(discriminatedUnionTypes1.ts, 1, 19)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 54, 15)) +>size : Symbol(Square.size, Decl(discriminatedUnionTypes1.ts, 1, 19)) + + case "rectangle": return s.width * s.height; +>s.width : Symbol(Rectangle.width, Decl(discriminatedUnionTypes1.ts, 6, 22)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 54, 15)) +>width : Symbol(Rectangle.width, Decl(discriminatedUnionTypes1.ts, 6, 22)) +>s.height : Symbol(Rectangle.height, Decl(discriminatedUnionTypes1.ts, 7, 18)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 54, 15)) +>height : Symbol(Rectangle.height, Decl(discriminatedUnionTypes1.ts, 7, 18)) + + case "circle": return Math.PI * s.radius * s.radius; +>Math.PI : Symbol(Math.PI, Decl(lib.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>PI : Symbol(Math.PI, Decl(lib.d.ts, --, --)) +>s.radius : Symbol(Circle.radius, Decl(discriminatedUnionTypes1.ts, 12, 19)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 54, 15)) +>radius : Symbol(Circle.radius, Decl(discriminatedUnionTypes1.ts, 12, 19)) +>s.radius : Symbol(Circle.radius, Decl(discriminatedUnionTypes1.ts, 12, 19)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 54, 15)) +>radius : Symbol(Circle.radius, Decl(discriminatedUnionTypes1.ts, 12, 19)) + } + return assertNever(s); +>assertNever : Symbol(assertNever, Decl(discriminatedUnionTypes1.ts, 39, 1)) +>s : Symbol(s, Decl(discriminatedUnionTypes1.ts, 54, 15)) +} + +type Message = +>Message : Symbol(Message, Decl(discriminatedUnionTypes1.ts, 61, 1)) + + { kind: "A", x: string } | +>kind : Symbol(kind, Decl(discriminatedUnionTypes1.ts, 64, 5)) +>x : Symbol(x, Decl(discriminatedUnionTypes1.ts, 64, 16)) + + { kind: "B" | "C", y: number } | +>kind : Symbol(kind, Decl(discriminatedUnionTypes1.ts, 65, 5)) +>y : Symbol(y, Decl(discriminatedUnionTypes1.ts, 65, 22)) + + { kind: "D" }; +>kind : Symbol(kind, Decl(discriminatedUnionTypes1.ts, 66, 5)) + +function f1(m: Message) { +>f1 : Symbol(f1, Decl(discriminatedUnionTypes1.ts, 66, 18)) +>m : Symbol(m, Decl(discriminatedUnionTypes1.ts, 68, 12)) +>Message : Symbol(Message, Decl(discriminatedUnionTypes1.ts, 61, 1)) + + if (m.kind === "A") { +>m.kind : Symbol(kind, Decl(discriminatedUnionTypes1.ts, 64, 5), Decl(discriminatedUnionTypes1.ts, 65, 5), Decl(discriminatedUnionTypes1.ts, 66, 5)) +>m : Symbol(m, Decl(discriminatedUnionTypes1.ts, 68, 12)) +>kind : Symbol(kind, Decl(discriminatedUnionTypes1.ts, 64, 5), Decl(discriminatedUnionTypes1.ts, 65, 5), Decl(discriminatedUnionTypes1.ts, 66, 5)) + + m; // { kind: "A", x: string } +>m : Symbol(m, Decl(discriminatedUnionTypes1.ts, 68, 12)) + } + else if (m.kind === "D") { +>m.kind : Symbol(kind, Decl(discriminatedUnionTypes1.ts, 65, 5), Decl(discriminatedUnionTypes1.ts, 66, 5)) +>m : Symbol(m, Decl(discriminatedUnionTypes1.ts, 68, 12)) +>kind : Symbol(kind, Decl(discriminatedUnionTypes1.ts, 65, 5), Decl(discriminatedUnionTypes1.ts, 66, 5)) + + m; // { kind: "D" } +>m : Symbol(m, Decl(discriminatedUnionTypes1.ts, 68, 12)) + } + else { + m; // { kind: "B" | "C", y: number } +>m : Symbol(m, Decl(discriminatedUnionTypes1.ts, 68, 12)) + } +} + +function f2(m: Message) { +>f2 : Symbol(f2, Decl(discriminatedUnionTypes1.ts, 78, 1)) +>m : Symbol(m, Decl(discriminatedUnionTypes1.ts, 80, 12)) +>Message : Symbol(Message, Decl(discriminatedUnionTypes1.ts, 61, 1)) + + if (m.kind === "A") { +>m.kind : Symbol(kind, Decl(discriminatedUnionTypes1.ts, 64, 5), Decl(discriminatedUnionTypes1.ts, 65, 5), Decl(discriminatedUnionTypes1.ts, 66, 5)) +>m : Symbol(m, Decl(discriminatedUnionTypes1.ts, 80, 12)) +>kind : Symbol(kind, Decl(discriminatedUnionTypes1.ts, 64, 5), Decl(discriminatedUnionTypes1.ts, 65, 5), Decl(discriminatedUnionTypes1.ts, 66, 5)) + + return; + } + m; // { kind: "B" | "C", y: number } | { kind: "D" } +>m : Symbol(m, Decl(discriminatedUnionTypes1.ts, 80, 12)) +} + +function f3(m: Message) { +>f3 : Symbol(f3, Decl(discriminatedUnionTypes1.ts, 85, 1)) +>m : Symbol(m, Decl(discriminatedUnionTypes1.ts, 87, 12)) +>Message : Symbol(Message, Decl(discriminatedUnionTypes1.ts, 61, 1)) + + if (m.kind === "X") { +>m.kind : Symbol(kind, Decl(discriminatedUnionTypes1.ts, 64, 5), Decl(discriminatedUnionTypes1.ts, 65, 5), Decl(discriminatedUnionTypes1.ts, 66, 5)) +>m : Symbol(m, Decl(discriminatedUnionTypes1.ts, 87, 12)) +>kind : Symbol(kind, Decl(discriminatedUnionTypes1.ts, 64, 5), Decl(discriminatedUnionTypes1.ts, 65, 5), Decl(discriminatedUnionTypes1.ts, 66, 5)) + + m; // never +>m : Symbol(m, Decl(discriminatedUnionTypes1.ts, 87, 12)) + } +} + +function f4(m: Message, x: "A" | "D") { +>f4 : Symbol(f4, Decl(discriminatedUnionTypes1.ts, 91, 1)) +>m : Symbol(m, Decl(discriminatedUnionTypes1.ts, 93, 12)) +>Message : Symbol(Message, Decl(discriminatedUnionTypes1.ts, 61, 1)) +>x : Symbol(x, Decl(discriminatedUnionTypes1.ts, 93, 23)) + + if (m.kind == x) { +>m.kind : Symbol(kind, Decl(discriminatedUnionTypes1.ts, 64, 5), Decl(discriminatedUnionTypes1.ts, 65, 5), Decl(discriminatedUnionTypes1.ts, 66, 5)) +>m : Symbol(m, Decl(discriminatedUnionTypes1.ts, 93, 12)) +>kind : Symbol(kind, Decl(discriminatedUnionTypes1.ts, 64, 5), Decl(discriminatedUnionTypes1.ts, 65, 5), Decl(discriminatedUnionTypes1.ts, 66, 5)) +>x : Symbol(x, Decl(discriminatedUnionTypes1.ts, 93, 23)) + + m; // { kind: "A", x: string } | { kind: "D" } +>m : Symbol(m, Decl(discriminatedUnionTypes1.ts, 93, 12)) + } +} + +function f5(m: Message) { +>f5 : Symbol(f5, Decl(discriminatedUnionTypes1.ts, 97, 1)) +>m : Symbol(m, Decl(discriminatedUnionTypes1.ts, 99, 12)) +>Message : Symbol(Message, Decl(discriminatedUnionTypes1.ts, 61, 1)) + + switch (m.kind) { +>m.kind : Symbol(kind, Decl(discriminatedUnionTypes1.ts, 64, 5), Decl(discriminatedUnionTypes1.ts, 65, 5), Decl(discriminatedUnionTypes1.ts, 66, 5)) +>m : Symbol(m, Decl(discriminatedUnionTypes1.ts, 99, 12)) +>kind : Symbol(kind, Decl(discriminatedUnionTypes1.ts, 64, 5), Decl(discriminatedUnionTypes1.ts, 65, 5), Decl(discriminatedUnionTypes1.ts, 66, 5)) + + case "A": + m; // { kind: "A", x: string } +>m : Symbol(m, Decl(discriminatedUnionTypes1.ts, 99, 12)) + + break; + case "D": + m; // { kind: "D" } +>m : Symbol(m, Decl(discriminatedUnionTypes1.ts, 99, 12)) + + break; + default: + m; // { kind: "B" | "C", y: number } +>m : Symbol(m, Decl(discriminatedUnionTypes1.ts, 99, 12)) + } +} + +function f6(m: Message) { +>f6 : Symbol(f6, Decl(discriminatedUnionTypes1.ts, 110, 1)) +>m : Symbol(m, Decl(discriminatedUnionTypes1.ts, 112, 12)) +>Message : Symbol(Message, Decl(discriminatedUnionTypes1.ts, 61, 1)) + + switch (m.kind) { +>m.kind : Symbol(kind, Decl(discriminatedUnionTypes1.ts, 64, 5), Decl(discriminatedUnionTypes1.ts, 65, 5), Decl(discriminatedUnionTypes1.ts, 66, 5)) +>m : Symbol(m, Decl(discriminatedUnionTypes1.ts, 112, 12)) +>kind : Symbol(kind, Decl(discriminatedUnionTypes1.ts, 64, 5), Decl(discriminatedUnionTypes1.ts, 65, 5), Decl(discriminatedUnionTypes1.ts, 66, 5)) + + case "A": + m; // { kind: "A", x: string } +>m : Symbol(m, Decl(discriminatedUnionTypes1.ts, 112, 12)) + + case "D": + m; // { kind: "A", x: string } | { kind: "D" } +>m : Symbol(m, Decl(discriminatedUnionTypes1.ts, 112, 12)) + + break; + default: + m; // { kind: "B" | "C", y: number } +>m : Symbol(m, Decl(discriminatedUnionTypes1.ts, 112, 12)) + } +} + +function f7(m: Message) { +>f7 : Symbol(f7, Decl(discriminatedUnionTypes1.ts, 122, 1)) +>m : Symbol(m, Decl(discriminatedUnionTypes1.ts, 124, 12)) +>Message : Symbol(Message, Decl(discriminatedUnionTypes1.ts, 61, 1)) + + switch (m.kind) { +>m.kind : Symbol(kind, Decl(discriminatedUnionTypes1.ts, 64, 5), Decl(discriminatedUnionTypes1.ts, 65, 5), Decl(discriminatedUnionTypes1.ts, 66, 5)) +>m : Symbol(m, Decl(discriminatedUnionTypes1.ts, 124, 12)) +>kind : Symbol(kind, Decl(discriminatedUnionTypes1.ts, 64, 5), Decl(discriminatedUnionTypes1.ts, 65, 5), Decl(discriminatedUnionTypes1.ts, 66, 5)) + + case "A": + case "B": + return; + } + m; // { kind: "B" | "C", y: number } | { kind: "D" } +>m : Symbol(m, Decl(discriminatedUnionTypes1.ts, 124, 12)) +} + +function f8(m: Message) { +>f8 : Symbol(f8, Decl(discriminatedUnionTypes1.ts, 131, 1)) +>m : Symbol(m, Decl(discriminatedUnionTypes1.ts, 133, 12)) +>Message : Symbol(Message, Decl(discriminatedUnionTypes1.ts, 61, 1)) + + switch (m.kind) { +>m.kind : Symbol(kind, Decl(discriminatedUnionTypes1.ts, 64, 5), Decl(discriminatedUnionTypes1.ts, 65, 5), Decl(discriminatedUnionTypes1.ts, 66, 5)) +>m : Symbol(m, Decl(discriminatedUnionTypes1.ts, 133, 12)) +>kind : Symbol(kind, Decl(discriminatedUnionTypes1.ts, 64, 5), Decl(discriminatedUnionTypes1.ts, 65, 5), Decl(discriminatedUnionTypes1.ts, 66, 5)) + + case "A": + return; + case "D": + throw new Error(); +>Error : Symbol(Error, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + } + m; // { kind: "B" | "C", y: number } +>m : Symbol(m, Decl(discriminatedUnionTypes1.ts, 133, 12)) +} diff --git a/tests/baselines/reference/discriminatedUnionTypes1.types b/tests/baselines/reference/discriminatedUnionTypes1.types new file mode 100644 index 0000000000000..234de28eabae5 --- /dev/null +++ b/tests/baselines/reference/discriminatedUnionTypes1.types @@ -0,0 +1,465 @@ +=== tests/cases/conformance/types/union/discriminatedUnionTypes1.ts === +interface Square { +>Square : Square + + kind: "square"; +>kind : "square" + + size: number; +>size : number +} + +interface Rectangle { +>Rectangle : Rectangle + + kind: "rectangle"; +>kind : "rectangle" + + width: number; +>width : number + + height: number; +>height : number +} + +interface Circle { +>Circle : Circle + + kind: "circle"; +>kind : "circle" + + radius: number; +>radius : number +} + +type Shape = Square | Rectangle | Circle; +>Shape : Square | Rectangle | Circle +>Square : Square +>Rectangle : Rectangle +>Circle : Circle + +function area1(s: Shape) { +>area1 : (s: Square | Rectangle | Circle) => number +>s : Square | Rectangle | Circle +>Shape : Square | Rectangle | Circle + + if (s.kind === "square") { +>s.kind === "square" : boolean +>s.kind : "square" | "rectangle" | "circle" +>s : Square | Rectangle | Circle +>kind : "square" | "rectangle" | "circle" +>"square" : string + + return s.size * s.size; +>s.size * s.size : number +>s.size : number +>s : Square +>size : number +>s.size : number +>s : Square +>size : number + } + else if (s.kind === "circle") { +>s.kind === "circle" : boolean +>s.kind : "rectangle" | "circle" +>s : Rectangle | Circle +>kind : "rectangle" | "circle" +>"circle" : string + + return Math.PI * s.radius * s.radius; +>Math.PI * s.radius * s.radius : number +>Math.PI * s.radius : number +>Math.PI : number +>Math : Math +>PI : number +>s.radius : number +>s : Circle +>radius : number +>s.radius : number +>s : Circle +>radius : number + } + else if (s.kind === "rectangle") { +>s.kind === "rectangle" : boolean +>s.kind : "rectangle" +>s : Rectangle +>kind : "rectangle" +>"rectangle" : string + + return s.width * s.height; +>s.width * s.height : number +>s.width : number +>s : Rectangle +>width : number +>s.height : number +>s : Rectangle +>height : number + } + else { + return 0; +>0 : number + } +} + +function area2(s: Shape) { +>area2 : (s: Square | Rectangle | Circle) => number +>s : Square | Rectangle | Circle +>Shape : Square | Rectangle | Circle + + switch (s.kind) { +>s.kind : "square" | "rectangle" | "circle" +>s : Square | Rectangle | Circle +>kind : "square" | "rectangle" | "circle" + + case "square": return s.size * s.size; +>"square" : string +>s.size * s.size : number +>s.size : number +>s : Square +>size : number +>s.size : number +>s : Square +>size : number + + case "rectangle": return s.width * s.height; +>"rectangle" : string +>s.width * s.height : number +>s.width : number +>s : Rectangle +>width : number +>s.height : number +>s : Rectangle +>height : number + + case "circle": return Math.PI * s.radius * s.radius; +>"circle" : string +>Math.PI * s.radius * s.radius : number +>Math.PI * s.radius : number +>Math.PI : number +>Math : Math +>PI : number +>s.radius : number +>s : Circle +>radius : number +>s.radius : number +>s : Circle +>radius : number + } +} + +function assertNever(x: never): never { +>assertNever : (x: never) => never +>x : never + + throw new Error("Unexpected object: " + x); +>new Error("Unexpected object: " + x) : Error +>Error : ErrorConstructor +>"Unexpected object: " + x : string +>"Unexpected object: " : string +>x : never +} + +function area3(s: Shape) { +>area3 : (s: Square | Rectangle | Circle) => number +>s : Square | Rectangle | Circle +>Shape : Square | Rectangle | Circle + + switch (s.kind) { +>s.kind : "square" | "rectangle" | "circle" +>s : Square | Rectangle | Circle +>kind : "square" | "rectangle" | "circle" + + case "square": return s.size * s.size; +>"square" : string +>s.size * s.size : number +>s.size : number +>s : Square +>size : number +>s.size : number +>s : Square +>size : number + + case "rectangle": return s.width * s.height; +>"rectangle" : string +>s.width * s.height : number +>s.width : number +>s : Rectangle +>width : number +>s.height : number +>s : Rectangle +>height : number + + case "circle": return Math.PI * s.radius * s.radius; +>"circle" : string +>Math.PI * s.radius * s.radius : number +>Math.PI * s.radius : number +>Math.PI : number +>Math : Math +>PI : number +>s.radius : number +>s : Circle +>radius : number +>s.radius : number +>s : Circle +>radius : number + + default: return assertNever(s); +>assertNever(s) : never +>assertNever : (x: never) => never +>s : never + } +} + +function area4(s: Shape) { +>area4 : (s: Square | Rectangle | Circle) => number +>s : Square | Rectangle | Circle +>Shape : Square | Rectangle | Circle + + switch (s.kind) { +>s.kind : "square" | "rectangle" | "circle" +>s : Square | Rectangle | Circle +>kind : "square" | "rectangle" | "circle" + + case "square": return s.size * s.size; +>"square" : string +>s.size * s.size : number +>s.size : number +>s : Square +>size : number +>s.size : number +>s : Square +>size : number + + case "rectangle": return s.width * s.height; +>"rectangle" : string +>s.width * s.height : number +>s.width : number +>s : Rectangle +>width : number +>s.height : number +>s : Rectangle +>height : number + + case "circle": return Math.PI * s.radius * s.radius; +>"circle" : string +>Math.PI * s.radius * s.radius : number +>Math.PI * s.radius : number +>Math.PI : number +>Math : Math +>PI : number +>s.radius : number +>s : Circle +>radius : number +>s.radius : number +>s : Circle +>radius : number + } + return assertNever(s); +>assertNever(s) : never +>assertNever : (x: never) => never +>s : never +} + +type Message = +>Message : { kind: "A"; x: string; } | { kind: "B" | "C"; y: number; } | { kind: "D"; } + + { kind: "A", x: string } | +>kind : "A" +>x : string + + { kind: "B" | "C", y: number } | +>kind : "B" | "C" +>y : number + + { kind: "D" }; +>kind : "D" + +function f1(m: Message) { +>f1 : (m: { kind: "A"; x: string; } | { kind: "B" | "C"; y: number; } | { kind: "D"; }) => void +>m : { kind: "A"; x: string; } | { kind: "B" | "C"; y: number; } | { kind: "D"; } +>Message : { kind: "A"; x: string; } | { kind: "B" | "C"; y: number; } | { kind: "D"; } + + if (m.kind === "A") { +>m.kind === "A" : boolean +>m.kind : "A" | "B" | "C" | "D" +>m : { kind: "A"; x: string; } | { kind: "B" | "C"; y: number; } | { kind: "D"; } +>kind : "A" | "B" | "C" | "D" +>"A" : string + + m; // { kind: "A", x: string } +>m : { kind: "A"; x: string; } + } + else if (m.kind === "D") { +>m.kind === "D" : boolean +>m.kind : "B" | "C" | "D" +>m : { kind: "B" | "C"; y: number; } | { kind: "D"; } +>kind : "B" | "C" | "D" +>"D" : string + + m; // { kind: "D" } +>m : { kind: "D"; } + } + else { + m; // { kind: "B" | "C", y: number } +>m : { kind: "B" | "C"; y: number; } + } +} + +function f2(m: Message) { +>f2 : (m: { kind: "A"; x: string; } | { kind: "B" | "C"; y: number; } | { kind: "D"; }) => void +>m : { kind: "A"; x: string; } | { kind: "B" | "C"; y: number; } | { kind: "D"; } +>Message : { kind: "A"; x: string; } | { kind: "B" | "C"; y: number; } | { kind: "D"; } + + if (m.kind === "A") { +>m.kind === "A" : boolean +>m.kind : "A" | "B" | "C" | "D" +>m : { kind: "A"; x: string; } | { kind: "B" | "C"; y: number; } | { kind: "D"; } +>kind : "A" | "B" | "C" | "D" +>"A" : string + + return; + } + m; // { kind: "B" | "C", y: number } | { kind: "D" } +>m : { kind: "B" | "C"; y: number; } | { kind: "D"; } +} + +function f3(m: Message) { +>f3 : (m: { kind: "A"; x: string; } | { kind: "B" | "C"; y: number; } | { kind: "D"; }) => void +>m : { kind: "A"; x: string; } | { kind: "B" | "C"; y: number; } | { kind: "D"; } +>Message : { kind: "A"; x: string; } | { kind: "B" | "C"; y: number; } | { kind: "D"; } + + if (m.kind === "X") { +>m.kind === "X" : boolean +>m.kind : "A" | "B" | "C" | "D" +>m : { kind: "A"; x: string; } | { kind: "B" | "C"; y: number; } | { kind: "D"; } +>kind : "A" | "B" | "C" | "D" +>"X" : string + + m; // never +>m : never + } +} + +function f4(m: Message, x: "A" | "D") { +>f4 : (m: { kind: "A"; x: string; } | { kind: "B" | "C"; y: number; } | { kind: "D"; }, x: "A" | "D") => void +>m : { kind: "A"; x: string; } | { kind: "B" | "C"; y: number; } | { kind: "D"; } +>Message : { kind: "A"; x: string; } | { kind: "B" | "C"; y: number; } | { kind: "D"; } +>x : "A" | "D" + + if (m.kind == x) { +>m.kind == x : boolean +>m.kind : "A" | "B" | "C" | "D" +>m : { kind: "A"; x: string; } | { kind: "B" | "C"; y: number; } | { kind: "D"; } +>kind : "A" | "B" | "C" | "D" +>x : "A" | "D" + + m; // { kind: "A", x: string } | { kind: "D" } +>m : { kind: "A"; x: string; } | { kind: "D"; } + } +} + +function f5(m: Message) { +>f5 : (m: { kind: "A"; x: string; } | { kind: "B" | "C"; y: number; } | { kind: "D"; }) => void +>m : { kind: "A"; x: string; } | { kind: "B" | "C"; y: number; } | { kind: "D"; } +>Message : { kind: "A"; x: string; } | { kind: "B" | "C"; y: number; } | { kind: "D"; } + + switch (m.kind) { +>m.kind : "A" | "B" | "C" | "D" +>m : { kind: "A"; x: string; } | { kind: "B" | "C"; y: number; } | { kind: "D"; } +>kind : "A" | "B" | "C" | "D" + + case "A": +>"A" : string + + m; // { kind: "A", x: string } +>m : { kind: "A"; x: string; } + + break; + case "D": +>"D" : string + + m; // { kind: "D" } +>m : { kind: "D"; } + + break; + default: + m; // { kind: "B" | "C", y: number } +>m : { kind: "B" | "C"; y: number; } + } +} + +function f6(m: Message) { +>f6 : (m: { kind: "A"; x: string; } | { kind: "B" | "C"; y: number; } | { kind: "D"; }) => void +>m : { kind: "A"; x: string; } | { kind: "B" | "C"; y: number; } | { kind: "D"; } +>Message : { kind: "A"; x: string; } | { kind: "B" | "C"; y: number; } | { kind: "D"; } + + switch (m.kind) { +>m.kind : "A" | "B" | "C" | "D" +>m : { kind: "A"; x: string; } | { kind: "B" | "C"; y: number; } | { kind: "D"; } +>kind : "A" | "B" | "C" | "D" + + case "A": +>"A" : string + + m; // { kind: "A", x: string } +>m : { kind: "A"; x: string; } + + case "D": +>"D" : string + + m; // { kind: "A", x: string } | { kind: "D" } +>m : { kind: "D"; } | { kind: "A"; x: string; } + + break; + default: + m; // { kind: "B" | "C", y: number } +>m : { kind: "B" | "C"; y: number; } + } +} + +function f7(m: Message) { +>f7 : (m: { kind: "A"; x: string; } | { kind: "B" | "C"; y: number; } | { kind: "D"; }) => void +>m : { kind: "A"; x: string; } | { kind: "B" | "C"; y: number; } | { kind: "D"; } +>Message : { kind: "A"; x: string; } | { kind: "B" | "C"; y: number; } | { kind: "D"; } + + switch (m.kind) { +>m.kind : "A" | "B" | "C" | "D" +>m : { kind: "A"; x: string; } | { kind: "B" | "C"; y: number; } | { kind: "D"; } +>kind : "A" | "B" | "C" | "D" + + case "A": +>"A" : string + + case "B": +>"B" : string + + return; + } + m; // { kind: "B" | "C", y: number } | { kind: "D" } +>m : { kind: "B" | "C"; y: number; } | { kind: "D"; } +} + +function f8(m: Message) { +>f8 : (m: { kind: "A"; x: string; } | { kind: "B" | "C"; y: number; } | { kind: "D"; }) => void +>m : { kind: "A"; x: string; } | { kind: "B" | "C"; y: number; } | { kind: "D"; } +>Message : { kind: "A"; x: string; } | { kind: "B" | "C"; y: number; } | { kind: "D"; } + + switch (m.kind) { +>m.kind : "A" | "B" | "C" | "D" +>m : { kind: "A"; x: string; } | { kind: "B" | "C"; y: number; } | { kind: "D"; } +>kind : "A" | "B" | "C" | "D" + + case "A": +>"A" : string + + return; + case "D": +>"D" : string + + throw new Error(); +>new Error() : Error +>Error : ErrorConstructor + } + m; // { kind: "B" | "C", y: number } +>m : { kind: "B" | "C"; y: number; } +} diff --git a/tests/cases/conformance/types/union/discriminatedUnionTypes1.ts b/tests/cases/conformance/types/union/discriminatedUnionTypes1.ts new file mode 100644 index 0000000000000..404162308b97d --- /dev/null +++ b/tests/cases/conformance/types/union/discriminatedUnionTypes1.ts @@ -0,0 +1,142 @@ +interface Square { + kind: "square"; + size: number; +} + +interface Rectangle { + kind: "rectangle"; + width: number; + height: number; +} + +interface Circle { + kind: "circle"; + radius: number; +} + +type Shape = Square | Rectangle | Circle; + +function area1(s: Shape) { + if (s.kind === "square") { + return s.size * s.size; + } + else if (s.kind === "circle") { + return Math.PI * s.radius * s.radius; + } + else if (s.kind === "rectangle") { + return s.width * s.height; + } + else { + return 0; + } +} + +function area2(s: Shape) { + switch (s.kind) { + case "square": return s.size * s.size; + case "rectangle": return s.width * s.height; + case "circle": return Math.PI * s.radius * s.radius; + } +} + +function assertNever(x: never): never { + throw new Error("Unexpected object: " + x); +} + +function area3(s: Shape) { + switch (s.kind) { + case "square": return s.size * s.size; + case "rectangle": return s.width * s.height; + case "circle": return Math.PI * s.radius * s.radius; + default: return assertNever(s); + } +} + +function area4(s: Shape) { + switch (s.kind) { + case "square": return s.size * s.size; + case "rectangle": return s.width * s.height; + case "circle": return Math.PI * s.radius * s.radius; + } + return assertNever(s); +} + +type Message = + { kind: "A", x: string } | + { kind: "B" | "C", y: number } | + { kind: "D" }; + +function f1(m: Message) { + if (m.kind === "A") { + m; // { kind: "A", x: string } + } + else if (m.kind === "D") { + m; // { kind: "D" } + } + else { + m; // { kind: "B" | "C", y: number } + } +} + +function f2(m: Message) { + if (m.kind === "A") { + return; + } + m; // { kind: "B" | "C", y: number } | { kind: "D" } +} + +function f3(m: Message) { + if (m.kind === "X") { + m; // never + } +} + +function f4(m: Message, x: "A" | "D") { + if (m.kind == x) { + m; // { kind: "A", x: string } | { kind: "D" } + } +} + +function f5(m: Message) { + switch (m.kind) { + case "A": + m; // { kind: "A", x: string } + break; + case "D": + m; // { kind: "D" } + break; + default: + m; // { kind: "B" | "C", y: number } + } +} + +function f6(m: Message) { + switch (m.kind) { + case "A": + m; // { kind: "A", x: string } + case "D": + m; // { kind: "A", x: string } | { kind: "D" } + break; + default: + m; // { kind: "B" | "C", y: number } + } +} + +function f7(m: Message) { + switch (m.kind) { + case "A": + case "B": + return; + } + m; // { kind: "B" | "C", y: number } | { kind: "D" } +} + +function f8(m: Message) { + switch (m.kind) { + case "A": + return; + case "D": + throw new Error(); + } + m; // { kind: "B" | "C", y: number } +} \ No newline at end of file