Filter primitives from union when checking for mismatched excess props if nonprimitive type is present (#31708)

* Filter primitives from union when checking for mismatched excess props if nonprimitive type is present

* Use maybeTypeOfKind
This commit is contained in:
Wesley Wigham 2019-06-12 17:31:57 -07:00 committed by GitHub
parent a0d164f142
commit 74c6bc1f85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 130 additions and 2 deletions

View File

@ -12600,8 +12600,8 @@ namespace ts {
result = typeRelatedToSomeType(getRegularTypeOfObjectLiteral(source), <UnionType>target, reportErrors && !(source.flags & TypeFlags.Primitive) && !(target.flags & TypeFlags.Primitive));
if (result && isPerformingExcessPropertyChecks) {
// Validate against excess props using the original `source`
const discriminantType = target.flags & TypeFlags.Union ? findMatchingDiscriminantType(source, target as UnionType) : undefined;
if (!propertiesRelatedTo(source, discriminantType || target, reportErrors, /*excludedProperties*/ undefined)) {
const discriminantType = findMatchingDiscriminantType(source, target as UnionType) || filterPrimitivesIfContainsNonPrimitive(target as UnionType);
if (!propertiesRelatedTo(source, discriminantType, reportErrors, /*excludedProperties*/ undefined)) {
return Ternary.False;
}
}
@ -12872,6 +12872,16 @@ namespace ts {
return bestMatch;
}
function filterPrimitivesIfContainsNonPrimitive(type: UnionType) {
if (maybeTypeOfKind(type, TypeFlags.NonPrimitive)) {
const result = filterType(type, t => !(t.flags & TypeFlags.Primitive));
if (!(result.flags & TypeFlags.Never)) {
return result;
}
}
return type;
}
// Keep this up-to-date with the same logic within `getApparentTypeOfContextualType`, since they should behave similarly
function findMatchingDiscriminantType(source: Type, target: Type) {
if (target.flags & TypeFlags.Union && source.flags & (TypeFlags.Intersection | TypeFlags.Object)) {

View File

@ -0,0 +1,17 @@
tests/cases/compiler/discriminateObjectTypesOnly.ts(9,7): error TS2741: Property 'toFixed' is missing in type '{ toString: undefined; }' but required in type '{ toFixed: null; toString: undefined; }'.
==== tests/cases/compiler/discriminateObjectTypesOnly.ts (1 errors) ====
type Thing = number | object;
const k: Thing = { toFixed: null }; // OK, satisfies object
type Thing2 = number | { toFixed: null } | object;
const q: Thing2 = { toFixed: null };
const h: Thing2 = { toString: null }; // OK, satisfies object
type Thing3 = number | { toFixed: null, toString: undefined } | object;
const l: Thing3 = { toString: undefined }; // error, toFixed isn't null
~
!!! error TS2741: Property 'toFixed' is missing in type '{ toString: undefined; }' but required in type '{ toFixed: null; toString: undefined; }'.
!!! related TS2728 tests/cases/compiler/discriminateObjectTypesOnly.ts:8:26: 'toFixed' is declared here.

View File

@ -0,0 +1,18 @@
//// [discriminateObjectTypesOnly.ts]
type Thing = number | object;
const k: Thing = { toFixed: null }; // OK, satisfies object
type Thing2 = number | { toFixed: null } | object;
const q: Thing2 = { toFixed: null };
const h: Thing2 = { toString: null }; // OK, satisfies object
type Thing3 = number | { toFixed: null, toString: undefined } | object;
const l: Thing3 = { toString: undefined }; // error, toFixed isn't null
//// [discriminateObjectTypesOnly.js]
"use strict";
var k = { toFixed: null }; // OK, satisfies object
var q = { toFixed: null };
var h = { toString: null }; // OK, satisfies object
var l = { toString: undefined }; // error, toFixed isn't null

View File

@ -0,0 +1,34 @@
=== tests/cases/compiler/discriminateObjectTypesOnly.ts ===
type Thing = number | object;
>Thing : Symbol(Thing, Decl(discriminateObjectTypesOnly.ts, 0, 0))
const k: Thing = { toFixed: null }; // OK, satisfies object
>k : Symbol(k, Decl(discriminateObjectTypesOnly.ts, 1, 5))
>Thing : Symbol(Thing, Decl(discriminateObjectTypesOnly.ts, 0, 0))
>toFixed : Symbol(toFixed, Decl(discriminateObjectTypesOnly.ts, 1, 18))
type Thing2 = number | { toFixed: null } | object;
>Thing2 : Symbol(Thing2, Decl(discriminateObjectTypesOnly.ts, 1, 35))
>toFixed : Symbol(toFixed, Decl(discriminateObjectTypesOnly.ts, 3, 24))
const q: Thing2 = { toFixed: null };
>q : Symbol(q, Decl(discriminateObjectTypesOnly.ts, 4, 5))
>Thing2 : Symbol(Thing2, Decl(discriminateObjectTypesOnly.ts, 1, 35))
>toFixed : Symbol(toFixed, Decl(discriminateObjectTypesOnly.ts, 4, 19))
const h: Thing2 = { toString: null }; // OK, satisfies object
>h : Symbol(h, Decl(discriminateObjectTypesOnly.ts, 5, 5))
>Thing2 : Symbol(Thing2, Decl(discriminateObjectTypesOnly.ts, 1, 35))
>toString : Symbol(toString, Decl(discriminateObjectTypesOnly.ts, 5, 19))
type Thing3 = number | { toFixed: null, toString: undefined } | object;
>Thing3 : Symbol(Thing3, Decl(discriminateObjectTypesOnly.ts, 5, 37))
>toFixed : Symbol(toFixed, Decl(discriminateObjectTypesOnly.ts, 7, 24))
>toString : Symbol(toString, Decl(discriminateObjectTypesOnly.ts, 7, 39))
const l: Thing3 = { toString: undefined }; // error, toFixed isn't null
>l : Symbol(l, Decl(discriminateObjectTypesOnly.ts, 8, 5))
>Thing3 : Symbol(Thing3, Decl(discriminateObjectTypesOnly.ts, 5, 37))
>toString : Symbol(toString, Decl(discriminateObjectTypesOnly.ts, 8, 19))
>undefined : Symbol(undefined)

View File

@ -0,0 +1,39 @@
=== tests/cases/compiler/discriminateObjectTypesOnly.ts ===
type Thing = number | object;
>Thing : Thing
const k: Thing = { toFixed: null }; // OK, satisfies object
>k : Thing
>{ toFixed: null } : { toFixed: null; }
>toFixed : null
>null : null
type Thing2 = number | { toFixed: null } | object;
>Thing2 : Thing2
>toFixed : null
>null : null
const q: Thing2 = { toFixed: null };
>q : Thing2
>{ toFixed: null } : { toFixed: null; }
>toFixed : null
>null : null
const h: Thing2 = { toString: null }; // OK, satisfies object
>h : Thing2
>{ toString: null } : { toString: null; }
>toString : null
>null : null
type Thing3 = number | { toFixed: null, toString: undefined } | object;
>Thing3 : Thing3
>toFixed : null
>null : null
>toString : undefined
const l: Thing3 = { toString: undefined }; // error, toFixed isn't null
>l : Thing3
>{ toString: undefined } : { toString: undefined; }
>toString : undefined
>undefined : undefined

View File

@ -0,0 +1,10 @@
// @strict: true
type Thing = number | object;
const k: Thing = { toFixed: null }; // OK, satisfies object
type Thing2 = number | { toFixed: null } | object;
const q: Thing2 = { toFixed: null };
const h: Thing2 = { toString: null }; // OK, satisfies object
type Thing3 = number | { toFixed: null, toString: undefined } | object;
const l: Thing3 = { toString: undefined }; // error, toFixed isn't null