No excess properties error in nested intersection type
sktw opened this issue · 1 comments
TypeScript Version: 2.1.5
Code
interface A {
x: string
}
interface B {
a: A;
}
interface C {
c: number;
}
type D = B & C;
let a: B = {a: {x: 'hello'}}; // ok
let b: B = {a: {x: 2}}; // error - types of property x are incompatible
let c: B = {a: {x: 'hello', y: 2}}; // error - y does not exist in type A
let d: D = {a: {x: 'hello'}, c: 5}; // ok
let e: D = {a: {x: 2}, c: 5}; // error - types of property x are incompatible
let f: D = {a: {x: 'hello', y: 2}, c: 5}; // should be an error?: y does not exist in type A
Expected behavior:
Excess property checking should cause a compiler error for the last example.
Actual behavior:
Last example compiles without error.
I just ran into this bug and it is marked "Needs Investigation", so I thought I would provide some. As far as I can tell (with the caveat that TypeScript is really complicated and I'm a newbie), the problem is with the following code in isRelatedTo
in checker.ts
(the file is too big to link to a formatted version):
if (isObjectLiteralType(source) && source.flags & TypeFlags.FreshLiteral) {
const discriminantType = target.flags & TypeFlags.Union ? findMatchingDiscriminantType(source, target as UnionType) : undefined;
if (hasExcessProperties(<FreshObjectLiteralType>source, target, discriminantType, reportErrors)) {
if (reportErrors) {
reportRelationError(headMessage, source, target);
}
return Ternary.False;
}
// Above we check for excess properties with respect to the entire target type. When union
// and intersection types are further deconstructed on the target side, we don't want to
// make the check again (as it might fail for a partial target type). Therefore we obtain
// the regular source type and proceed with that.
if (isUnionOrIntersectionTypeWithoutNullableConstituents(target) && !discriminantType) {
source = getRegularTypeOfObjectLiteral(source);
}
}
After the code checks for excess properties only at the top level, getRegularTypeOfObjectLiteral
disables the check recursively. Indeed, if a source type S can be successfully compared to a target type X & Y or X | Y by comparing S to X and/or Y individually, at no point during the recursive comparison to X or Y do we have all the information to do a correct check for excess properties in object literal types nested in S. So the least disruptive way I can think of to fix the problem is to change the code quoted above to do a recursive check for excess properties. Another option would be to declare the problem a design limitation.