Skip to content

Commit af9552d

Browse files
committed
Create many-to-many mapping with certain limits, also use in CFA
1 parent 9e8f24b commit af9552d

File tree

2 files changed

+61
-63
lines changed

2 files changed

+61
-63
lines changed

src/compiler/checker.ts

Lines changed: 59 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -21376,80 +21376,65 @@ namespace ts {
2137621376
return result;
2137721377
}
2137821378

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;
2139121386
}
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+
});
2139221395
}
2139321396
}
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;
2141121398
}
2141221399

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;
2142721404
}
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;
2143921416
}
2144021417
}
2144121418
}
21442-
return unionType.constituentMap;
21419+
return !!(keyPropertyName as string).length;
2144321420
}
2144421421

2144521422
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)));
2144821428
}
2144921429

2145021430
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)));
2145321438
}
2145421439

2145521440
function isOrContainsMatchingReference(source: Node, target: Node) {
@@ -22743,6 +22728,19 @@ namespace ts {
2274322728
});
2274422729
}
2274522730

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+
2274622744
function narrowTypeByTruthiness(type: Type, expr: Expression, assumeTrue: boolean): Type {
2274722745
if (isMatchingReference(reference, expr)) {
2274822746
return getTypeWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy);
@@ -22812,10 +22810,10 @@ namespace ts {
2281222810
}
2281322811
}
2281422812
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);
2281622814
}
2281722815
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);
2281922817
}
2282022818
if (isMatchingConstructorReference(left)) {
2282122819
return narrowTypeByConstructor(type, operator, right, assumeTrue);

src/compiler/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5306,9 +5306,9 @@ namespace ts {
53065306
/* @internal */
53075307
origin?: Type; // Denormalized union, intersection, or index type in which union originates
53085308
/* @internal */
5309-
keyPropertyNames?: __String[] | undefined; // Unit type properties that exist in every object/intersection in union type
5309+
keyPropertyName?: __String; // Property with unique unit type that exists in every object/intersection in union type
53105310
/* @internal */
5311-
constituentMap?: ESMap<string, Type | undefined>; // Constituents keyed by unit type discriminants
5311+
constituentMap?: ESMap<TypeId, Type>; // Constituents keyed by unit type discriminants
53125312
}
53135313

53145314
export interface IntersectionType extends UnionOrIntersectionType {

0 commit comments

Comments
 (0)