@@ -122,7 +122,7 @@ namespace ts {
122
122
const unknownType = createIntrinsicType(TypeFlags.Any, "unknown");
123
123
124
124
const emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
125
- const emptyUnionType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
125
+ const nothingType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
126
126
const emptyGenericType = <GenericType><ObjectType>createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
127
127
emptyGenericType.instantiations = {};
128
128
@@ -2030,7 +2030,7 @@ namespace ts {
2030
2030
writeUnionOrIntersectionType(<UnionOrIntersectionType>type, flags);
2031
2031
}
2032
2032
else if (type.flags & TypeFlags.Anonymous) {
2033
- if (type === emptyUnionType ) {
2033
+ if (type === nothingType ) {
2034
2034
writer.writeKeyword("nothing");
2035
2035
}
2036
2036
else {
@@ -5006,7 +5006,7 @@ namespace ts {
5006
5006
if (type.flags & TypeFlags.Undefined) typeSet.containsUndefined = true;
5007
5007
if (type.flags & TypeFlags.Null) typeSet.containsNull = true;
5008
5008
}
5009
- else if (type !== emptyUnionType && !contains(typeSet, type)) {
5009
+ else if (type !== nothingType && !contains(typeSet, type)) {
5010
5010
typeSet.push(type);
5011
5011
}
5012
5012
}
@@ -5047,7 +5047,10 @@ namespace ts {
5047
5047
// a named type that circularly references itself.
5048
5048
function getUnionType(types: Type[], noSubtypeReduction?: boolean): Type {
5049
5049
if (types.length === 0) {
5050
- return emptyUnionType;
5050
+ return nothingType;
5051
+ }
5052
+ if (types.length === 1) {
5053
+ return types[0];
5051
5054
}
5052
5055
const typeSet = [] as TypeSet;
5053
5056
addTypesToSet(typeSet, types, TypeFlags.Union);
@@ -5064,7 +5067,7 @@ namespace ts {
5064
5067
if (typeSet.length === 0) {
5065
5068
return typeSet.containsNull ? nullType :
5066
5069
typeSet.containsUndefined ? undefinedType :
5067
- emptyUnionType ;
5070
+ nothingType ;
5068
5071
}
5069
5072
else if (typeSet.length === 1) {
5070
5073
return typeSet[0];
@@ -7483,7 +7486,7 @@ namespace ts {
7483
7486
7484
7487
function getTypeWithFacts(type: Type, include: TypeFacts) {
7485
7488
if (!(type.flags & TypeFlags.Union)) {
7486
- return getTypeFacts(type) & include ? type : emptyUnionType ;
7489
+ return getTypeFacts(type) & include ? type : nothingType ;
7487
7490
}
7488
7491
let firstType: Type;
7489
7492
let types: Type[];
@@ -7500,7 +7503,7 @@ namespace ts {
7500
7503
}
7501
7504
}
7502
7505
}
7503
- return firstType ? types ? getUnionType(types, /*noSubtypeReduction*/ true) : firstType : emptyUnionType ;
7506
+ return firstType ? types ? getUnionType(types, /*noSubtypeReduction*/ true) : firstType : nothingType ;
7504
7507
}
7505
7508
7506
7509
function getTypeWithDefault(type: Type, defaultExpression: Expression) {
@@ -7618,6 +7621,9 @@ namespace ts {
7618
7621
const visitedFlowStart = visitedFlowCount;
7619
7622
const result = getTypeAtFlowNode(reference.flowNode);
7620
7623
visitedFlowCount = visitedFlowStart;
7624
+ if (reference.parent.kind === SyntaxKind.NonNullExpression && getTypeWithFacts(result, TypeFacts.NEUndefinedOrNull) === nothingType) {
7625
+ return declaredType;
7626
+ }
7621
7627
return result;
7622
7628
7623
7629
function getTypeAtFlowNode(flow: FlowNode): Type {
@@ -7702,7 +7708,22 @@ namespace ts {
7702
7708
}
7703
7709
7704
7710
function getTypeAtFlowCondition(flow: FlowCondition) {
7705
- return narrowType(getTypeAtFlowNode(flow.antecedent), flow.expression, (flow.flags & FlowFlags.TrueCondition) !== 0);
7711
+ let type = getTypeAtFlowNode(flow.antecedent);
7712
+ if (type !== nothingType) {
7713
+ // If we have an antecedent type (meaning we're reachable in some way), we first
7714
+ // attempt to narrow the antecedent type. If that produces the nothing type, then
7715
+ // we take the type guard as an indication that control could reach here in a
7716
+ // manner not understood by the control flow analyzer (e.g. a function argument
7717
+ // has an invalid type, or a nested function has possibly made an assignment to a
7718
+ // captured variable). We proceed by reverting to the declared type and then
7719
+ // narrow that.
7720
+ const assumeTrue = (flow.flags & FlowFlags.TrueCondition) !== 0;
7721
+ type = narrowType(type, flow.expression, assumeTrue);
7722
+ if (type === nothingType) {
7723
+ type = narrowType(declaredType, flow.expression, assumeTrue);
7724
+ }
7725
+ }
7726
+ return type;
7706
7727
}
7707
7728
7708
7729
function getTypeAtFlowBranchLabel(flow: FlowLabel) {
@@ -7720,7 +7741,7 @@ namespace ts {
7720
7741
antecedentTypes.push(type);
7721
7742
}
7722
7743
}
7723
- return antecedentTypes.length === 1 ? antecedentTypes[0] : getUnionType(antecedentTypes);
7744
+ return getUnionType(antecedentTypes);
7724
7745
}
7725
7746
7726
7747
function getTypeAtFlowLoopLabel(flow: FlowLabel) {
@@ -7770,7 +7791,7 @@ namespace ts {
7770
7791
antecedentTypes.push(type);
7771
7792
}
7772
7793
}
7773
- return cache[key] = antecedentTypes.length === 1 ? antecedentTypes[0] : getUnionType(antecedentTypes);
7794
+ return cache[key] = getUnionType(antecedentTypes);
7774
7795
}
7775
7796
7776
7797
function narrowTypeByTruthiness(type: Type, expr: Expression, assumeTrue: boolean): Type {
@@ -7921,7 +7942,7 @@ namespace ts {
7921
7942
const targetType = type.flags & TypeFlags.TypeParameter ? getApparentType(type) : type;
7922
7943
return isTypeAssignableTo(candidate, targetType) ? candidate :
7923
7944
isTypeAssignableTo(type, candidate) ? type :
7924
- emptyUnionType ;
7945
+ nothingType ;
7925
7946
}
7926
7947
7927
7948
function narrowTypeByTypePredicate(type: Type, callExpression: CallExpression, assumeTrue: boolean): Type {
@@ -14726,7 +14747,7 @@ namespace ts {
14726
14747
arrayType = getUnionType(filter((arrayOrStringType as UnionType).types, t => !(t.flags & TypeFlags.StringLike)));
14727
14748
}
14728
14749
else if (arrayOrStringType.flags & TypeFlags.StringLike) {
14729
- arrayType = emptyUnionType ;
14750
+ arrayType = nothingType ;
14730
14751
}
14731
14752
const hasStringConstituent = arrayOrStringType !== arrayType;
14732
14753
let reportedError = false;
@@ -14738,7 +14759,7 @@ namespace ts {
14738
14759
14739
14760
// Now that we've removed all the StringLike types, if no constituents remain, then the entire
14740
14761
// arrayOrStringType was a string.
14741
- if (arrayType === emptyUnionType ) {
14762
+ if (arrayType === nothingType ) {
14742
14763
return stringType;
14743
14764
}
14744
14765
}
0 commit comments