@@ -118,18 +118,18 @@ namespace ts {
118
118
const resolvingSymbol = createSymbol(SymbolFlags.Transient, "__resolving__");
119
119
120
120
const anyType = createIntrinsicType(TypeFlags.Any, "any");
121
+ const unknownType = createIntrinsicType(TypeFlags.Any, "unknown");
122
+ const undefinedType = createIntrinsicType(TypeFlags.Undefined, "undefined");
123
+ const undefinedWideningType = strictNullChecks ? undefinedType : createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsWideningType, "undefined");
124
+ const nullType = createIntrinsicType(TypeFlags.Null, "null");
125
+ const nullWideningType = strictNullChecks ? nullType : createIntrinsicType(TypeFlags.Null | TypeFlags.ContainsWideningType, "null");
121
126
const stringType = createIntrinsicType(TypeFlags.String, "string");
122
127
const numberType = createIntrinsicType(TypeFlags.Number, "number");
123
128
const trueType = createIntrinsicType(TypeFlags.BooleanLiteral, "true");
124
129
const falseType = createIntrinsicType(TypeFlags.BooleanLiteral, "false");
125
130
const booleanType = createBooleanType([trueType, falseType]);
126
131
const esSymbolType = createIntrinsicType(TypeFlags.ESSymbol, "symbol");
127
132
const voidType = createIntrinsicType(TypeFlags.Void, "void");
128
- const undefinedType = createIntrinsicType(TypeFlags.Undefined, "undefined");
129
- const undefinedWideningType = strictNullChecks ? undefinedType : createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsWideningType, "undefined");
130
- const nullType = createIntrinsicType(TypeFlags.Null, "null");
131
- const nullWideningType = strictNullChecks ? nullType : createIntrinsicType(TypeFlags.Null | TypeFlags.ContainsWideningType, "null");
132
- const unknownType = createIntrinsicType(TypeFlags.Any, "unknown");
133
133
const neverType = createIntrinsicType(TypeFlags.Never, "never");
134
134
135
135
const emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
@@ -1928,23 +1928,27 @@ namespace ts {
1928
1928
return result;
1929
1929
}
1930
1930
1931
- function reduceLiteralTypes(types: Type[]): Type[] {
1932
- let result: Type[];
1931
+ function formatUnionTypes(types: Type[]): Type[] {
1932
+ const result: Type[] = [];
1933
+ let flags: TypeFlags = 0;
1933
1934
for (let i = 0; i < types.length; i++) {
1934
1935
const t = types[i];
1935
- if (t.flags & (TypeFlags.BooleanLiteral | TypeFlags.EnumLiteral)) {
1936
- const baseType = t.flags & TypeFlags.BooleanLiteral ? booleanType : (<EnumLiteralType>t).baseType;
1937
- const count = baseType.types.length;
1938
- if (i + count <= types.length && types[i + count - 1] === baseType.types[count - 1]) {
1939
- (result || (result = types.slice(0, i))).push(baseType);
1940
- i += count - 1;
1941
- continue;
1936
+ flags |= t.flags;
1937
+ if (!(t.flags & TypeFlags.Nullable)) {
1938
+ if (t.flags & (TypeFlags.BooleanLiteral | TypeFlags.EnumLiteral)) {
1939
+ const baseType = t.flags & TypeFlags.BooleanLiteral ? booleanType : (<EnumLiteralType>t).baseType;
1940
+ const count = baseType.types.length;
1941
+ if (i + count <= types.length && types[i + count - 1] === baseType.types[count - 1]) {
1942
+ result.push(baseType);
1943
+ i += count - 1;
1944
+ continue;
1945
+ }
1942
1946
}
1943
- }
1944
- if (result) {
1945
1947
result.push(t);
1946
1948
}
1947
1949
}
1950
+ if (flags & TypeFlags.Null) result.push(nullType);
1951
+ if (flags & TypeFlags.Undefined) result.push(undefinedType);
1948
1952
return result || types;
1949
1953
}
1950
1954
@@ -2246,7 +2250,7 @@ namespace ts {
2246
2250
writePunctuation(writer, SyntaxKind.OpenParenToken);
2247
2251
}
2248
2252
if (type.flags & TypeFlags.Union) {
2249
- writeTypeList(reduceLiteralTypes (type.types), SyntaxKind.BarToken);
2253
+ writeTypeList(formatUnionTypes (type.types), SyntaxKind.BarToken);
2250
2254
}
2251
2255
else {
2252
2256
writeTypeList(type.types, SyntaxKind.AmpersandToken);
@@ -5228,27 +5232,58 @@ namespace ts {
5228
5232
containsNonWideningType?: boolean;
5229
5233
}
5230
5234
5231
- function addTypeToSet(typeSet: TypeSet, type: Type, typeSetKind: TypeFlags) {
5232
- if (type.flags & typeSetKind) {
5233
- addTypesToSet(typeSet, (<UnionOrIntersectionType>type).types, typeSetKind);
5235
+ function binarySearchTypes(types: Type[], type: Type): number {
5236
+ let low = 0;
5237
+ let high = types.length - 1;
5238
+ const typeId = type.id;
5239
+ while (low <= high) {
5240
+ const middle = low + ((high - low) >> 1);
5241
+ const id = types[middle].id;
5242
+ if (id === typeId) {
5243
+ return middle;
5244
+ }
5245
+ else if (id > typeId) {
5246
+ high = middle - 1;
5247
+ }
5248
+ else {
5249
+ low = middle + 1;
5250
+ }
5251
+ }
5252
+ return ~low;
5253
+ }
5254
+
5255
+ function containsType(types: Type[], type: Type): boolean {
5256
+ return binarySearchTypes(types, type) >= 0;
5257
+ }
5258
+
5259
+ function addTypeToUnion(typeSet: TypeSet, type: Type) {
5260
+ if (type.flags & TypeFlags.Union) {
5261
+ addTypesToUnion(typeSet, (<UnionType>type).types);
5234
5262
}
5235
- else if (type.flags & (TypeFlags.Any | TypeFlags.Undefined | TypeFlags.Null)) {
5236
- if (type.flags & TypeFlags.Any) typeSet.containsAny = true;
5263
+ else if (type.flags & TypeFlags.Any) {
5264
+ typeSet.containsAny = true;
5265
+ }
5266
+ else if (!strictNullChecks && type.flags & TypeFlags.Nullable) {
5237
5267
if (type.flags & TypeFlags.Undefined) typeSet.containsUndefined = true;
5238
5268
if (type.flags & TypeFlags.Null) typeSet.containsNull = true;
5239
5269
if (!(type.flags & TypeFlags.ContainsWideningType)) typeSet.containsNonWideningType = true;
5240
5270
}
5241
- else if (type !== neverType && !contains(typeSet, type) &&
5242
- !(type.flags & TypeFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && containsIdenticalType(typeSet, type))) {
5243
- typeSet.push(type);
5271
+ else if (!(type.flags & TypeFlags.Never)) {
5272
+ const len = typeSet.length;
5273
+ const index = len && type.id > typeSet[len - 1].id ? ~len : binarySearchTypes(typeSet, type);
5274
+ if (index < 0) {
5275
+ if (!(type.flags & TypeFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && containsIdenticalType(typeSet, type))) {
5276
+ typeSet.splice(~index, 0, type);
5277
+ }
5278
+ }
5244
5279
}
5245
5280
}
5246
5281
5247
5282
// Add the given types to the given type set. Order is preserved, duplicates are removed,
5248
5283
// and nested types of the given kind are flattened into the set.
5249
- function addTypesToSet (typeSet: TypeSet, types: Type[], typeSetKind: TypeFlags ) {
5284
+ function addTypesToUnion (typeSet: TypeSet, types: Type[]) {
5250
5285
for (const type of types) {
5251
- addTypeToSet (typeSet, type, typeSetKind );
5286
+ addTypeToUnion (typeSet, type);
5252
5287
}
5253
5288
}
5254
5289
@@ -5280,10 +5315,6 @@ namespace ts {
5280
5315
}
5281
5316
}
5282
5317
5283
- function compareTypeIds(type1: Type, type2: Type): number {
5284
- return type1.id - type2.id;
5285
- }
5286
-
5287
5318
// We deduplicate the constituent types based on object identity. If the subtypeReduction flag is
5288
5319
// specified we also reduce the constituent type set to only include types that aren't subtypes of
5289
5320
// other types. Subtype reduction is expensive for large union types and is possible only when union
@@ -5299,15 +5330,10 @@ namespace ts {
5299
5330
return types[0];
5300
5331
}
5301
5332
const typeSet = [] as TypeSet;
5302
- addTypesToSet (typeSet, types, TypeFlags.Union );
5333
+ addTypesToUnion (typeSet, types);
5303
5334
if (typeSet.containsAny) {
5304
5335
return anyType;
5305
5336
}
5306
- typeSet.sort(compareTypeIds);
5307
- if (strictNullChecks) {
5308
- if (typeSet.containsNull) typeSet.push(nullType);
5309
- if (typeSet.containsUndefined) typeSet.push(undefinedType);
5310
- }
5311
5337
if (subtypeReduction) {
5312
5338
removeSubtypes(typeSet);
5313
5339
}
@@ -5339,6 +5365,26 @@ namespace ts {
5339
5365
return links.resolvedType;
5340
5366
}
5341
5367
5368
+ function addTypeToIntersection(typeSet: TypeSet, type: Type) {
5369
+ if (type.flags & TypeFlags.Intersection) {
5370
+ addTypesToIntersection(typeSet, (<IntersectionType>type).types);
5371
+ }
5372
+ else if (type.flags & TypeFlags.Any) {
5373
+ typeSet.containsAny = true;
5374
+ }
5375
+ else if (!(type.flags & TypeFlags.Never) && (strictNullChecks || !(type.flags & TypeFlags.Nullable)) && !contains(typeSet, type)) {
5376
+ typeSet.push(type);
5377
+ }
5378
+ }
5379
+
5380
+ // Add the given types to the given type set. Order is preserved, duplicates are removed,
5381
+ // and nested types of the given kind are flattened into the set.
5382
+ function addTypesToIntersection(typeSet: TypeSet, types: Type[]) {
5383
+ for (const type of types) {
5384
+ addTypeToIntersection(typeSet, type);
5385
+ }
5386
+ }
5387
+
5342
5388
// We do not perform structural deduplication on intersection types. Intersection types are created only by the &
5343
5389
// type operator and we can't reduce those because we want to support recursive intersection types. For example,
5344
5390
// a type alias of the form "type List<T> = T & { next: List<T> }" cannot be reduced during its declaration.
@@ -5349,14 +5395,10 @@ namespace ts {
5349
5395
return emptyObjectType;
5350
5396
}
5351
5397
const typeSet = [] as TypeSet;
5352
- addTypesToSet (typeSet, types, TypeFlags.Intersection );
5398
+ addTypesToIntersection (typeSet, types);
5353
5399
if (typeSet.containsAny) {
5354
5400
return anyType;
5355
5401
}
5356
- if (strictNullChecks) {
5357
- if (typeSet.containsNull) typeSet.push(nullType);
5358
- if (typeSet.containsUndefined) typeSet.push(undefinedType);
5359
- }
5360
5402
if (typeSet.length === 1) {
5361
5403
return typeSet[0];
5362
5404
}
@@ -6395,21 +6437,10 @@ namespace ts {
6395
6437
6396
6438
function typeRelatedToSomeType(source: Type, target: UnionOrIntersectionType, reportErrors: boolean): Ternary {
6397
6439
const targetTypes = target.types;
6398
- if (contains (targetTypes, source)) {
6440
+ if (target.flags & TypeFlags.Union && containsType (targetTypes, source)) {
6399
6441
return Ternary.True;
6400
6442
}
6401
- // The null and undefined types are guaranteed to be at the end of the constituent type list. In order
6402
- // to produce the best possible errors we first check the nullable types, such that the last type we
6403
- // check and report errors from is a non-nullable type if one is present.
6404
- let len = targetTypes.length;
6405
- while (len >= 2 && targetTypes[len - 1].flags & TypeFlags.Nullable) {
6406
- const related = isRelatedTo(source, targetTypes[len - 1], /*reportErrors*/ false);
6407
- if (related) {
6408
- return related;
6409
- }
6410
- len--;
6411
- }
6412
- // Now check the non-nullable types and report errors on the last one.
6443
+ const len = targetTypes.length;
6413
6444
for (let i = 0; i < len; i++) {
6414
6445
const related = isRelatedTo(source, targetTypes[i], reportErrors && i === len - 1);
6415
6446
if (related) {
@@ -6434,21 +6465,10 @@ namespace ts {
6434
6465
6435
6466
function someTypeRelatedToType(source: UnionOrIntersectionType, target: Type, reportErrors: boolean): Ternary {
6436
6467
const sourceTypes = source.types;
6437
- if (contains (sourceTypes, target)) {
6468
+ if (source.flags & TypeFlags.Union && containsType (sourceTypes, target)) {
6438
6469
return Ternary.True;
6439
6470
}
6440
- // The null and undefined types are guaranteed to be at the end of the constituent type list. In order
6441
- // to produce the best possible errors we first check the nullable types, such that the last type we
6442
- // check and report errors from is a non-nullable type if one is present.
6443
- let len = sourceTypes.length;
6444
- while (len >= 2 && sourceTypes[len - 1].flags & TypeFlags.Nullable) {
6445
- const related = isRelatedTo(sourceTypes[len - 1], target, /*reportErrors*/ false);
6446
- if (related) {
6447
- return related;
6448
- }
6449
- len--;
6450
- }
6451
- // Now check the non-nullable types and report errors on the last one.
6471
+ const len = sourceTypes.length;
6452
6472
for (let i = 0; i < len; i++) {
6453
6473
const related = isRelatedTo(sourceTypes[i], target, reportErrors && i === len - 1);
6454
6474
if (related) {
0 commit comments