mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-07-07 18:25:51 -05:00
Excess property understands conditional types (#25584)
Previously it did not, causing misleading excess property errors. Note that assignability errors with conditional types are still usually confusing. This PR doesn't address that. Also, make sure that exact matches in getSpellingSuggestion are skipped.
This commit is contained in:
committed by
GitHub
parent
52486ae362
commit
42a2d9e568
@@ -17406,7 +17406,7 @@ namespace ts {
|
||||
*/
|
||||
function isKnownProperty(targetType: Type, name: __String, isComparingJsxAttributes: boolean): boolean {
|
||||
if (targetType.flags & TypeFlags.Object) {
|
||||
const resolved = resolveStructuredTypeMembers(<ObjectType>targetType);
|
||||
const resolved = resolveStructuredTypeMembers(targetType as ObjectType);
|
||||
if (resolved.stringIndexInfo ||
|
||||
resolved.numberIndexInfo && isNumericLiteralName(name) ||
|
||||
getPropertyOfObjectType(targetType, name) ||
|
||||
@@ -17416,12 +17416,16 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
else if (targetType.flags & TypeFlags.UnionOrIntersection) {
|
||||
for (const t of (<UnionOrIntersectionType>targetType).types) {
|
||||
for (const t of (targetType as UnionOrIntersectionType).types) {
|
||||
if (isKnownProperty(t, name, isComparingJsxAttributes)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (targetType.flags & TypeFlags.Conditional) {
|
||||
return isKnownProperty((targetType as ConditionalType).root.trueType, name, isComparingJsxAttributes) ||
|
||||
isKnownProperty((targetType as ConditionalType).root.falseType, name, isComparingJsxAttributes);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1866,6 +1866,9 @@ namespace ts {
|
||||
if (candidateName !== undefined && Math.abs(candidateName.length - nameLowerCase.length) <= maximumLengthDifference) {
|
||||
const candidateNameLowerCase = candidateName.toLowerCase();
|
||||
if (candidateNameLowerCase === nameLowerCase) {
|
||||
if (candidateName === name) {
|
||||
continue;
|
||||
}
|
||||
return candidate;
|
||||
}
|
||||
if (justCheckExactMatches) {
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
tests/cases/conformance/types/conditional/conditionalTypesExcessProperties.ts(8,5): error TS2322: Type '{ test: string; arg: A; }' is not assignable to type 'Something<A>'.
|
||||
Type '{ test: string; arg: A; }' is not assignable to type 'A extends object ? { arg: A; } : { arg?: undefined; }'.
|
||||
tests/cases/conformance/types/conditional/conditionalTypesExcessProperties.ts(9,33): error TS2322: Type 'A' is not assignable to type 'Something<A>["arr"]'.
|
||||
Type 'object' is not assignable to type 'Something<A>["arr"]'.
|
||||
|
||||
|
||||
==== tests/cases/conformance/types/conditional/conditionalTypesExcessProperties.ts (2 errors) ====
|
||||
type Something<T> = { test: string } & (T extends object ? {
|
||||
arg: T
|
||||
} : {
|
||||
arg?: undefined
|
||||
});
|
||||
|
||||
function testFunc2<A extends object>(a: A, sa: Something<A>) {
|
||||
sa = { test: 'hi', arg: a }; // not excess (but currently still not assignable)
|
||||
~~
|
||||
!!! error TS2322: Type '{ test: string; arg: A; }' is not assignable to type 'Something<A>'.
|
||||
!!! error TS2322: Type '{ test: string; arg: A; }' is not assignable to type 'A extends object ? { arg: A; } : { arg?: undefined; }'.
|
||||
sa = { test: 'bye', arg: a, arr: a } // excess
|
||||
~~~
|
||||
!!! error TS2322: Type 'A' is not assignable to type 'Something<A>["arr"]'.
|
||||
!!! error TS2322: Type 'object' is not assignable to type 'Something<A>["arr"]'.
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
//// [conditionalTypesExcessProperties.ts]
|
||||
type Something<T> = { test: string } & (T extends object ? {
|
||||
arg: T
|
||||
} : {
|
||||
arg?: undefined
|
||||
});
|
||||
|
||||
function testFunc2<A extends object>(a: A, sa: Something<A>) {
|
||||
sa = { test: 'hi', arg: a }; // not excess (but currently still not assignable)
|
||||
sa = { test: 'bye', arg: a, arr: a } // excess
|
||||
}
|
||||
|
||||
|
||||
//// [conditionalTypesExcessProperties.js]
|
||||
function testFunc2(a, sa) {
|
||||
sa = { test: 'hi', arg: a }; // not excess (but currently still not assignable)
|
||||
sa = { test: 'bye', arg: a, arr: a }; // excess
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
=== tests/cases/conformance/types/conditional/conditionalTypesExcessProperties.ts ===
|
||||
type Something<T> = { test: string } & (T extends object ? {
|
||||
>Something : Symbol(Something, Decl(conditionalTypesExcessProperties.ts, 0, 0))
|
||||
>T : Symbol(T, Decl(conditionalTypesExcessProperties.ts, 0, 15))
|
||||
>test : Symbol(test, Decl(conditionalTypesExcessProperties.ts, 0, 21))
|
||||
>T : Symbol(T, Decl(conditionalTypesExcessProperties.ts, 0, 15))
|
||||
|
||||
arg: T
|
||||
>arg : Symbol(arg, Decl(conditionalTypesExcessProperties.ts, 0, 61))
|
||||
>T : Symbol(T, Decl(conditionalTypesExcessProperties.ts, 0, 15))
|
||||
|
||||
} : {
|
||||
arg?: undefined
|
||||
>arg : Symbol(arg, Decl(conditionalTypesExcessProperties.ts, 2, 5))
|
||||
|
||||
});
|
||||
|
||||
function testFunc2<A extends object>(a: A, sa: Something<A>) {
|
||||
>testFunc2 : Symbol(testFunc2, Decl(conditionalTypesExcessProperties.ts, 4, 7))
|
||||
>A : Symbol(A, Decl(conditionalTypesExcessProperties.ts, 6, 19))
|
||||
>a : Symbol(a, Decl(conditionalTypesExcessProperties.ts, 6, 37))
|
||||
>A : Symbol(A, Decl(conditionalTypesExcessProperties.ts, 6, 19))
|
||||
>sa : Symbol(sa, Decl(conditionalTypesExcessProperties.ts, 6, 42))
|
||||
>Something : Symbol(Something, Decl(conditionalTypesExcessProperties.ts, 0, 0))
|
||||
>A : Symbol(A, Decl(conditionalTypesExcessProperties.ts, 6, 19))
|
||||
|
||||
sa = { test: 'hi', arg: a }; // not excess (but currently still not assignable)
|
||||
>sa : Symbol(sa, Decl(conditionalTypesExcessProperties.ts, 6, 42))
|
||||
>test : Symbol(test, Decl(conditionalTypesExcessProperties.ts, 7, 10))
|
||||
>arg : Symbol(arg, Decl(conditionalTypesExcessProperties.ts, 7, 22))
|
||||
>a : Symbol(a, Decl(conditionalTypesExcessProperties.ts, 6, 37))
|
||||
|
||||
sa = { test: 'bye', arg: a, arr: a } // excess
|
||||
>sa : Symbol(sa, Decl(conditionalTypesExcessProperties.ts, 6, 42))
|
||||
>test : Symbol(test, Decl(conditionalTypesExcessProperties.ts, 8, 10))
|
||||
>arg : Symbol(arg, Decl(conditionalTypesExcessProperties.ts, 8, 23))
|
||||
>a : Symbol(a, Decl(conditionalTypesExcessProperties.ts, 6, 37))
|
||||
>arr : Symbol(arr, Decl(conditionalTypesExcessProperties.ts, 8, 31))
|
||||
>a : Symbol(a, Decl(conditionalTypesExcessProperties.ts, 6, 37))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
=== tests/cases/conformance/types/conditional/conditionalTypesExcessProperties.ts ===
|
||||
type Something<T> = { test: string } & (T extends object ? {
|
||||
>Something : Something<T>
|
||||
>T : T
|
||||
>test : string
|
||||
>T : T
|
||||
|
||||
arg: T
|
||||
>arg : T
|
||||
>T : T
|
||||
|
||||
} : {
|
||||
arg?: undefined
|
||||
>arg : undefined
|
||||
|
||||
});
|
||||
|
||||
function testFunc2<A extends object>(a: A, sa: Something<A>) {
|
||||
>testFunc2 : <A extends object>(a: A, sa: Something<A>) => void
|
||||
>A : A
|
||||
>a : A
|
||||
>A : A
|
||||
>sa : Something<A>
|
||||
>Something : Something<T>
|
||||
>A : A
|
||||
|
||||
sa = { test: 'hi', arg: a }; // not excess (but currently still not assignable)
|
||||
>sa = { test: 'hi', arg: a } : { test: string; arg: A; }
|
||||
>sa : Something<A>
|
||||
>{ test: 'hi', arg: a } : { test: string; arg: A; }
|
||||
>test : string
|
||||
>'hi' : "hi"
|
||||
>arg : A
|
||||
>a : A
|
||||
|
||||
sa = { test: 'bye', arg: a, arr: a } // excess
|
||||
>sa = { test: 'bye', arg: a, arr: a } : { test: string; arg: A; arr: A; }
|
||||
>sa : Something<A>
|
||||
>{ test: 'bye', arg: a, arr: a } : { test: string; arg: A; arr: A; }
|
||||
>test : string
|
||||
>'bye' : "bye"
|
||||
>arg : A
|
||||
>a : A
|
||||
>arr : A
|
||||
>a : A
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
type Something<T> = { test: string } & (T extends object ? {
|
||||
arg: T
|
||||
} : {
|
||||
arg?: undefined
|
||||
});
|
||||
|
||||
function testFunc2<A extends object>(a: A, sa: Something<A>) {
|
||||
sa = { test: 'hi', arg: a }; // not excess (but currently still not assignable)
|
||||
sa = { test: 'bye', arg: a, arr: a } // excess
|
||||
}
|
||||
Reference in New Issue
Block a user