Excess discriminated types match all discriminable properties (#32755)

* Target types in excess property checking must match all discriminable properties

This allows fewer types to be discriminated in excess properties, which
fixes some examples.

* Add excess property test

* Fix semicolon lint

* Remove extra semicolon!

* Improve EPC for unions with multiple discriminants
This commit is contained in:
Nathan Shively-Sanders
2019-08-08 15:34:52 -07:00
committed by GitHub
parent b70f894881
commit b24050aefd
11 changed files with 642 additions and 8 deletions

View File

@@ -14269,20 +14269,25 @@ namespace ts {
function discriminateTypeByDiscriminableItems(target: UnionType, discriminators: [() => Type, __String][], related: (source: Type, target: Type) => boolean | Ternary): Type | undefined;
function discriminateTypeByDiscriminableItems(target: UnionType, discriminators: [() => Type, __String][], related: (source: Type, target: Type) => boolean | Ternary, defaultValue: Type): Type;
function discriminateTypeByDiscriminableItems(target: UnionType, discriminators: [() => Type, __String][], related: (source: Type, target: Type) => boolean | Ternary, defaultValue?: Type) {
let match: Type | undefined;
// undefined=unknown, true=discriminated, false=not discriminated
// The state of each type progresses from left to right. Discriminated types stop at 'true'.
const discriminable = target.types.map(_ => undefined) as (boolean | undefined)[];
for (const [getDiscriminatingType, propertyName] of discriminators) {
let i = 0;
for (const type of target.types) {
const targetType = getTypeOfPropertyOfType(type, propertyName);
if (targetType && related(getDiscriminatingType(), targetType)) {
if (match) {
if (type === match) continue; // Finding multiple fields which discriminate to the same type is fine
return defaultValue;
}
match = type;
discriminable[i] = discriminable[i] === undefined ? true : discriminable[i];
}
else {
discriminable[i] = false;
}
i++;
}
}
return match || defaultValue;
const match = discriminable.indexOf(/*searchElement*/ true);
// make sure exactly 1 matches before returning it
return match === -1 || discriminable.indexOf(/*searchElement*/ true, match + 1) !== -1 ? defaultValue : target.types[match];
}
/**