mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge pull request #9163 from Microsoft/typeGuardDiscriminants
Discriminated union types
This commit is contained in:
+90
-48
@@ -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
@@ -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
@@ -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 }
|
||||
}
|
||||
Reference in New Issue
Block a user