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 {