@@ -21376,80 +21376,65 @@ namespace ts {
21376
21376
return result;
21377
21377
}
21378
21378
21379
- function getKeyPropertyNames(unionType: UnionType): __String[] | undefined {
21380
- const types = unionType.types;
21381
- if (types.length < 10 || getObjectFlags(unionType) & ObjectFlags.PrimitiveUnion) {
21382
- return undefined;
21383
- }
21384
- let keyPropertyNames = unionType.keyPropertyNames;
21385
- if (!keyPropertyNames) {
21386
- keyPropertyNames = unionType.keyPropertyNames = [];
21387
- const propType = find(types, t => !!(t.flags & (TypeFlags.Object | TypeFlags.Intersection)) && getPropertiesOfType(t).length !== 0) || unknownType;
21388
- for (const name of map(getPropertiesOfType(propType), prop => prop.escapedName)) {
21389
- if (every(types, t => !(t.flags & (TypeFlags.Object | TypeFlags.Intersection)) || isUnitType(getTypeOfPropertyOfType(t, name) || unknownType))) {
21390
- keyPropertyNames.push(name);
21379
+ function getMapByKeyProperty(types: Type[], name: __String) {
21380
+ let map: ESMap<TypeId, Type> | undefined;
21381
+ for (const type of types) {
21382
+ if (type.flags & (TypeFlags.Object | TypeFlags.Intersection)) {
21383
+ const discriminant = getTypeOfPropertyOfType(type, name);
21384
+ if (!discriminant || !isLiteralType(discriminant)) {
21385
+ return undefined;
21391
21386
}
21387
+ forEachType(discriminant, t => {
21388
+ const id = getTypeId(getRegularTypeOfLiteralType(t));
21389
+ const existing = (map || (map = new Map<TypeId, Type>())).get(id);
21390
+ if (existing && existing.flags & TypeFlags.Union && (<UnionType>existing).types.length >= 10) {
21391
+ return undefined;
21392
+ }
21393
+ map.set(id, existing ? getUnionType([existing, type]) : type);
21394
+ });
21392
21395
}
21393
21396
}
21394
- return keyPropertyNames.length ? keyPropertyNames : undefined;
21395
- }
21396
-
21397
- function getUnionConstituentKeyForType(unionType: UnionType, type: Type) {
21398
- const keyPropertyNames = getKeyPropertyNames(unionType);
21399
- if (!keyPropertyNames) {
21400
- return undefined;
21401
- }
21402
- const propTypes = [];
21403
- for (const name of keyPropertyNames) {
21404
- const propType = getTypeOfPropertyOfType(type, name);
21405
- if (!(propType && isUnitType(propType))) {
21406
- return undefined;
21407
- }
21408
- propTypes.push(getRegularTypeOfLiteralType(propType));
21409
- }
21410
- return getTypeListId(propTypes);
21397
+ return map && map.size >= 10 ? map : undefined;
21411
21398
}
21412
21399
21413
- function getUnionConstituentKeyForObjectLiteral(unionType: UnionType, node: ObjectLiteralExpression) {
21414
- const keyPropertyNames = getKeyPropertyNames(unionType);
21415
- if (!keyPropertyNames) {
21416
- return undefined;
21417
- }
21418
- const propTypes = [];
21419
- for (const name of keyPropertyNames) {
21420
- const propNode = find(node.properties, p => p.symbol && p.kind === SyntaxKind.PropertyAssignment &&
21421
- p.symbol.escapedName === name && isPossiblyDiscriminantValue(p.initializer));
21422
- const propType = propNode && getTypeOfExpression((<PropertyAssignment>propNode).initializer);
21423
- if (!(propType && isUnitType(propType))) {
21424
- return undefined;
21425
- }
21426
- propTypes.push(getRegularTypeOfLiteralType(propType));
21400
+ function hasConstituentMap(unionType: UnionType): boolean {
21401
+ const types = unionType.types;
21402
+ if (types.length < 10 || getObjectFlags(unionType) & ObjectFlags.PrimitiveUnion) {
21403
+ return false;
21427
21404
}
21428
- return getTypeListId(propTypes) ;
21429
- }
21430
-
21431
- function getUnionConstituentMap(unionType: UnionType) {
21432
- if (!unionType.constituentMap) {
21433
- const map = unionType.constituentMap = new Map<string, Type | undefined>();
21434
- for ( const t of unionType. types) {
21435
- const key = getUnionConstituentKeyForType(unionType, t);
21436
- if (key) {
21437
- const duplicate = map.has(key) ;
21438
- map.set(key, duplicate ? undefined : t) ;
21405
+ let keyPropertyName = unionType.keyPropertyName ;
21406
+ if (keyPropertyName === undefined) {
21407
+ unionType.keyPropertyName = keyPropertyName = "" as __String;
21408
+ const propType = find(types, t => !!(t.flags & (TypeFlags.Object | TypeFlags.Intersection)) && getPropertiesOfType(t).length !== 0) || unknownType;
21409
+ const propNames = map(getPropertiesOfType(propType), p => p.escapedName);
21410
+ for ( const name of propNames) {
21411
+ const mapByKeyProperty = getMapByKeyProperty( types, name);
21412
+ if (mapByKeyProperty) {
21413
+ unionType.keyPropertyName = keyPropertyName = name;
21414
+ unionType.constituentMap = mapByKeyProperty ;
21415
+ break ;
21439
21416
}
21440
21417
}
21441
21418
}
21442
- return unionType.constituentMap ;
21419
+ return !!(keyPropertyName as string).length ;
21443
21420
}
21444
21421
21445
21422
function getMatchingUnionConstituentForType(unionType: UnionType, type: Type) {
21446
- const key = getUnionConstituentKeyForType(unionType, type);
21447
- return key && getUnionConstituentMap(unionType).get(key);
21423
+ if (!hasConstituentMap(unionType)) {
21424
+ return undefined;
21425
+ }
21426
+ const propType = getTypeOfPropertyOfType(type, unionType.keyPropertyName!);
21427
+ return propType && unionType.constituentMap!.get(getTypeId(getRegularTypeOfLiteralType(propType)));
21448
21428
}
21449
21429
21450
21430
function getMatchingUnionConstituentForObjectLiteral(unionType: UnionType, node: ObjectLiteralExpression) {
21451
- const key = getUnionConstituentKeyForObjectLiteral(unionType, node);
21452
- return key && getUnionConstituentMap(unionType).get(key);
21431
+ if (!hasConstituentMap(unionType)) {
21432
+ return undefined;
21433
+ }
21434
+ const propNode = find(node.properties, p => p.symbol && p.kind === SyntaxKind.PropertyAssignment &&
21435
+ p.symbol.escapedName === unionType.keyPropertyName! && isPossiblyDiscriminantValue(p.initializer));
21436
+ const propType = propNode && getTypeOfExpression((<PropertyAssignment>propNode).initializer);
21437
+ return propType && unionType.constituentMap!.get(getTypeId(getRegularTypeOfLiteralType(propType)));
21453
21438
}
21454
21439
21455
21440
function isOrContainsMatchingReference(source: Node, target: Node) {
@@ -22743,6 +22728,19 @@ namespace ts {
22743
22728
});
22744
22729
}
22745
22730
22731
+ function narrowTypeByDiscriminantProperty(type: Type, access: AccessExpression, operator: SyntaxKind, value: Expression, assumeTrue: boolean) {
22732
+ if ((operator === SyntaxKind.EqualsEqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) && type.flags & TypeFlags.Union &&
22733
+ hasConstituentMap(<UnionType>type) && (<UnionType>type).keyPropertyName === getAccessedPropertyName(access)) {
22734
+ const candidate = (<UnionType>type).constituentMap!.get(getTypeId(getRegularTypeOfLiteralType(getTypeOfExpression(value))));
22735
+ if (candidate) {
22736
+ return operator === (assumeTrue ? SyntaxKind.EqualsEqualsEqualsToken : SyntaxKind.ExclamationEqualsEqualsToken) ?
22737
+ candidate :
22738
+ filterType(type, t => candidate.flags & TypeFlags.Union ? !contains((<UnionType>candidate).types, t) : t !== candidate);
22739
+ }
22740
+ }
22741
+ return narrowTypeByDiscriminant(type, access, t => narrowTypeByEquality(t, operator, value, assumeTrue));
22742
+ }
22743
+
22746
22744
function narrowTypeByTruthiness(type: Type, expr: Expression, assumeTrue: boolean): Type {
22747
22745
if (isMatchingReference(reference, expr)) {
22748
22746
return getTypeWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy);
@@ -22812,10 +22810,10 @@ namespace ts {
22812
22810
}
22813
22811
}
22814
22812
if (isMatchingReferenceDiscriminant(left, type)) {
22815
- return narrowTypeByDiscriminant (type, <AccessExpression>left, t => narrowTypeByEquality(t, operator, right, assumeTrue) );
22813
+ return narrowTypeByDiscriminantProperty (type, <AccessExpression>left, operator, right, assumeTrue);
22816
22814
}
22817
22815
if (isMatchingReferenceDiscriminant(right, type)) {
22818
- return narrowTypeByDiscriminant (type, <AccessExpression>right, t => narrowTypeByEquality(t, operator, left, assumeTrue) );
22816
+ return narrowTypeByDiscriminantProperty (type, <AccessExpression>right, operator, left, assumeTrue);
22819
22817
}
22820
22818
if (isMatchingConstructorReference(left)) {
22821
22819
return narrowTypeByConstructor(type, operator, right, assumeTrue);
0 commit comments