Merge pull request #9163 from Microsoft/typeGuardDiscriminants

Discriminated union types
This commit is contained in:
Anders Hejlsberg
2016-06-17 14:37:35 -07:00
committed by GitHub
7 changed files with 1524 additions and 81 deletions
+90 -48
View File
@@ -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((<PropertyAccessExpression>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(<CallExpression>expr);
case SyntaxKind.ParenthesizedExpression:
return isNarrowingExpression((<ParenthesizedExpression>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((<PropertyAccessExpression>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((<PropertyAccessExpression>expr.expression).expression)) {
return true;
}
return false;
}
function isNarrowingNullCheckOperands(expr1: Expression, expr2: Expression) {
return (expr1.kind === SyntaxKind.NullKeyword || expr1.kind === SyntaxKind.Identifier && (<Identifier>expr1).text === "undefined") && isNarrowableOperand(expr2);
}
function isNarrowingTypeofOperands(expr1: Expression, expr2: Expression) {
return expr1.kind === SyntaxKind.TypeOfExpression && isNarrowableOperand((<TypeOfExpression>expr1).expression) && expr2.kind === SyntaxKind.StringLiteral;
}
function isNarrowingDiscriminant(expr: Expression) {
return expr.kind === SyntaxKind.PropertyAccessExpression && isNarrowableReference((<PropertyAccessExpression>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;
function isNarrowableOperand(expr: Expression): boolean {
switch (expr.kind) {
case SyntaxKind.ParenthesizedExpression:
return isNarrowableOperand((<ParenthesizedExpression>expr).expression);
case SyntaxKind.BinaryExpression:
switch ((<BinaryExpression>expr).operatorToken.kind) {
case SyntaxKind.EqualsToken:
return isNarrowableOperand((<BinaryExpression>expr).left);
case SyntaxKind.CommaToken:
return isNarrowableOperand((<BinaryExpression>expr).right);
}
}
else if (expr.right.kind === SyntaxKind.StringLiteral) {
typeOf = expr.left;
}
else {
typeOf = undefined;
}
return typeOf && typeOf.kind === SyntaxKind.TypeOfExpression && isNarrowingExpression((<TypeOfExpression>typeOf).expression);
return isNarrowableReference(expr);
}
function isNarrowingSwitchStatement(switchStatement: SwitchStatement) {
const expr = switchStatement.expression;
return expr.kind === SyntaxKind.PropertyAccessExpression && isNarrowableReference((<PropertyAccessExpression>expr).expression);
}
function createBranchLabel(): FlowLabel {
@@ -683,8 +711,22 @@ namespace ts {
setFlowNodeReferenced(antecedent);
return <FlowCondition>{
flags,
antecedent,
expression,
antecedent
};
}
function createFlowSwitchClause(antecedent: FlowNode, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number): FlowNode {
if (!isNarrowingSwitchStatement(switchStatement)) {
return antecedent;
}
setFlowNodeReferenced(antecedent);
return <FlowSwitchClause>{
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, <SwitchStatement>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);
}
}
}
+161 -31
View File
@@ -5186,7 +5186,6 @@ namespace ts {
if (hasProperty(stringLiteralTypes, text)) {
return stringLiteralTypes[text];
}
const type = stringLiteralTypes[text] = <StringLiteralType>createType(TypeFlags.StringLiteral);
type.text = text;
return type;
@@ -5651,6 +5650,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);
}
@@ -6831,8 +6834,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((<UnionType>type).types, isStringLiteralUnionType) :
false;
}
/**
@@ -7697,6 +7702,35 @@ namespace ts {
return node;
}
function getTypeOfSwitchClause(clause: CaseClause | DefaultClause) {
if (clause.kind === SyntaxKind.CaseClause) {
const expr = (<CaseClause>clause).expression;
return expr.kind === SyntaxKind.StringLiteral ? getStringLiteralTypeForText((<StringLiteral>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((<UnionType>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((<UnionType>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)) {
@@ -7734,6 +7768,9 @@ namespace ts {
else if (flow.flags & FlowFlags.Condition) {
type = getTypeAtFlowCondition(<FlowCondition>flow);
}
else if (flow.flags & FlowFlags.SwitchClause) {
type = getTypeAtSwitchClause(<FlowSwitchClause>flow);
}
else if (flow.flags & FlowFlags.Label) {
if ((<FlowLabel>flow).antecedents.length === 1) {
flow = (<FlowLabel>flow).antecedents[0];
@@ -7817,6 +7854,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) {
@@ -7896,12 +7938,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, <TypeOfExpression>left, operator, <LiteralExpression>right, assumeTrue);
}
if (right.kind === SyntaxKind.TypeOfExpression && left.kind === SyntaxKind.StringLiteral) {
return narrowTypeByTypeof(type, <TypeOfExpression>right, operator, <LiteralExpression>left, assumeTrue);
}
if (left.kind === SyntaxKind.PropertyAccessExpression) {
return narrowTypeByDiscriminant(type, <PropertyAccessExpression>left, operator, right, assumeTrue);
}
if (right.kind === SyntaxKind.PropertyAccessExpression) {
return narrowTypeByDiscriminant(type, <PropertyAccessExpression>right, operator, left, assumeTrue);
}
break;
case SyntaxKind.InstanceOfKeyword:
@@ -7912,41 +7968,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((<TypeOfExpression>(expr.left.kind === SyntaxKind.TypeOfExpression ? expr.left : expr.right)).expression);
const literal = <LiteralExpression>(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)) {
@@ -7964,6 +8014,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((<StringLiteral>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, (<PropertyAccessExpression>switchStatement.expression).expression)) {
return type;
}
const propName = (<PropertyAccessExpression>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)) {
@@ -8934,10 +9036,6 @@ namespace ts {
return applyToContextualType(type, t => getIndexTypeOfStructuredType(t, kind));
}
function contextualTypeIsStringLiteralType(type: Type): boolean {
return !!(type.flags & TypeFlags.Union ? forEach((<UnionType>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((<UnionType>type).types, isTupleLikeType) : isTupleLikeType(type));
@@ -11768,10 +11866,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((<PropertyAccessExpression>expr).expression);
if (!(type.flags & TypeFlags.Union)) {
return false;
}
const propName = (<PropertyAccessExpression>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((<Block>func.body).statements);
if (lastStatement && lastStatement.kind === SyntaxKind.SwitchStatement && isExhaustiveSwitchStatement(<SwitchStatement>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(<Block>func.body, returnStatement => {
const expr = returnStatement.expression;
@@ -11828,7 +11958,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;
}
@@ -12606,7 +12736,7 @@ namespace ts {
function checkStringLiteralExpression(node: StringLiteral): Type {
const contextualType = getContextualType(node);
if (contextualType && contextualTypeIsStringLiteralType(contextualType)) {
if (contextualType && isStringLiteralUnionType(contextualType)) {
return getStringLiteralTypeForText(node.text);
}
+12 -2
View File
@@ -1187,6 +1187,7 @@ namespace ts {
export interface SwitchStatement extends Statement {
expression: Expression;
caseBlock: CaseBlock;
possiblyExhaustive?: boolean;
}
// @kind(SyntaxKind.CaseBlock)
@@ -1553,8 +1554,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
}
@@ -1590,6 +1592,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;
@@ -2184,6 +2193,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 {
@@ -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 }
}
@@ -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))
}
@@ -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; }
}
@@ -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 }
}