Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed excess and common property checks with NoInfer #57673

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/compiler/checker.ts
Expand Up @@ -23904,6 +23904,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return resolved.callSignatures.length === 0 && resolved.constructSignatures.length === 0 && resolved.indexInfos.length === 0 &&
resolved.properties.length > 0 && every(resolved.properties, p => !!(p.flags & SymbolFlags.Optional));
}
if (type.flags & TypeFlags.Substitution) {
return isWeakType((type as SubstitutionType).baseType);
}
if (type.flags & TypeFlags.Intersection) {
return every((type as IntersectionType).types, isWeakType);
}
Expand Down Expand Up @@ -32626,7 +32629,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return true;
}
}
else if (targetType.flags & TypeFlags.UnionOrIntersection && isExcessPropertyCheckTarget(targetType)) {
if (targetType.flags & TypeFlags.Substitution) {
return isKnownProperty((targetType as SubstitutionType).baseType, name, isComparingJsxAttributes);
}
if (targetType.flags & TypeFlags.UnionOrIntersection && isExcessPropertyCheckTarget(targetType)) {
for (const t of (targetType as UnionOrIntersectionType).types) {
if (isKnownProperty(t, name, isComparingJsxAttributes)) {
return true;
Expand All @@ -32639,6 +32645,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
function isExcessPropertyCheckTarget(type: Type): boolean {
return !!(type.flags & TypeFlags.Object && !(getObjectFlags(type) & ObjectFlags.ObjectLiteralPatternWithComputedProperties) ||
type.flags & TypeFlags.NonPrimitive ||
type.flags & TypeFlags.Substitution && isExcessPropertyCheckTarget((type as SubstitutionType).baseType) ||
type.flags & TypeFlags.Union && some((type as UnionType).types, isExcessPropertyCheckTarget) ||
type.flags & TypeFlags.Intersection && every((type as IntersectionType).types, isExcessPropertyCheckTarget));
}
Expand Down
36 changes: 36 additions & 0 deletions tests/baselines/reference/noInferCommonPropertyCheck1.errors.txt
@@ -0,0 +1,36 @@
noInferCommonPropertyCheck1.ts(7,20): error TS2559: Type '{ x: string; }' has no properties in common with type 'NoInfer<Partial<{ a: unknown; b: unknown; }>> & { prop?: unknown; }'.
noInferCommonPropertyCheck1.ts(15,33): error TS2559: Type '{ x: string; }' has no properties in common with type 'NoInfer<Partial<{ a: unknown; b: unknown; }>> & NoInfer<Partial<{ c: unknown; d: unknown; }>>'.
noInferCommonPropertyCheck1.ts(23,33): error TS2559: Type '{ x: string; }' has no properties in common with type 'Partial<{ a: unknown; b: unknown; }> & Partial<{ c: unknown; d: unknown; }>'.


==== noInferCommonPropertyCheck1.ts (3 errors) ====
declare const partialObj1: Partial<{ a: unknown; b: unknown }>;
declare const partialObj2: Partial<{ c: unknown; d: unknown }>;
declare const someObj1: { x: string };

declare function test1<T>(a: T, b: NoInfer<T> & { prop?: unknown }): void;

test1(partialObj1, someObj1);
~~~~~~~~
!!! error TS2559: Type '{ x: string; }' has no properties in common with type 'NoInfer<Partial<{ a: unknown; b: unknown; }>> & { prop?: unknown; }'.

declare function test2<T1, T2>(
a: T1,
b: T2,
c: NoInfer<T1> & NoInfer<T2>,
): void;

test2(partialObj1, partialObj2, someObj1);
~~~~~~~~
!!! error TS2559: Type '{ x: string; }' has no properties in common with type 'NoInfer<Partial<{ a: unknown; b: unknown; }>> & NoInfer<Partial<{ c: unknown; d: unknown; }>>'.

declare function test3<T1, T2>(
a: T1,
b: T2,
c: NoInfer<T1 & T2>,
): void;

test3(partialObj1, partialObj2, someObj1);
~~~~~~~~
!!! error TS2559: Type '{ x: string; }' has no properties in common with type 'Partial<{ a: unknown; b: unknown; }> & Partial<{ c: unknown; d: unknown; }>'.

89 changes: 89 additions & 0 deletions tests/baselines/reference/noInferCommonPropertyCheck1.symbols
@@ -0,0 +1,89 @@
//// [tests/cases/compiler/noInferCommonPropertyCheck1.ts] ////

=== noInferCommonPropertyCheck1.ts ===
declare const partialObj1: Partial<{ a: unknown; b: unknown }>;
>partialObj1 : Symbol(partialObj1, Decl(noInferCommonPropertyCheck1.ts, 0, 13))
>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --))
>a : Symbol(a, Decl(noInferCommonPropertyCheck1.ts, 0, 36))
>b : Symbol(b, Decl(noInferCommonPropertyCheck1.ts, 0, 48))

declare const partialObj2: Partial<{ c: unknown; d: unknown }>;
>partialObj2 : Symbol(partialObj2, Decl(noInferCommonPropertyCheck1.ts, 1, 13))
>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --))
>c : Symbol(c, Decl(noInferCommonPropertyCheck1.ts, 1, 36))
>d : Symbol(d, Decl(noInferCommonPropertyCheck1.ts, 1, 48))

declare const someObj1: { x: string };
>someObj1 : Symbol(someObj1, Decl(noInferCommonPropertyCheck1.ts, 2, 13))
>x : Symbol(x, Decl(noInferCommonPropertyCheck1.ts, 2, 25))

declare function test1<T>(a: T, b: NoInfer<T> & { prop?: unknown }): void;
>test1 : Symbol(test1, Decl(noInferCommonPropertyCheck1.ts, 2, 38))
>T : Symbol(T, Decl(noInferCommonPropertyCheck1.ts, 4, 23))
>a : Symbol(a, Decl(noInferCommonPropertyCheck1.ts, 4, 26))
>T : Symbol(T, Decl(noInferCommonPropertyCheck1.ts, 4, 23))
>b : Symbol(b, Decl(noInferCommonPropertyCheck1.ts, 4, 31))
>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(noInferCommonPropertyCheck1.ts, 4, 23))
>prop : Symbol(prop, Decl(noInferCommonPropertyCheck1.ts, 4, 49))

test1(partialObj1, someObj1);
>test1 : Symbol(test1, Decl(noInferCommonPropertyCheck1.ts, 2, 38))
>partialObj1 : Symbol(partialObj1, Decl(noInferCommonPropertyCheck1.ts, 0, 13))
>someObj1 : Symbol(someObj1, Decl(noInferCommonPropertyCheck1.ts, 2, 13))

declare function test2<T1, T2>(
>test2 : Symbol(test2, Decl(noInferCommonPropertyCheck1.ts, 6, 29))
>T1 : Symbol(T1, Decl(noInferCommonPropertyCheck1.ts, 8, 23))
>T2 : Symbol(T2, Decl(noInferCommonPropertyCheck1.ts, 8, 26))

a: T1,
>a : Symbol(a, Decl(noInferCommonPropertyCheck1.ts, 8, 31))
>T1 : Symbol(T1, Decl(noInferCommonPropertyCheck1.ts, 8, 23))

b: T2,
>b : Symbol(b, Decl(noInferCommonPropertyCheck1.ts, 9, 8))
>T2 : Symbol(T2, Decl(noInferCommonPropertyCheck1.ts, 8, 26))

c: NoInfer<T1> & NoInfer<T2>,
>c : Symbol(c, Decl(noInferCommonPropertyCheck1.ts, 10, 8))
>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --))
>T1 : Symbol(T1, Decl(noInferCommonPropertyCheck1.ts, 8, 23))
>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --))
>T2 : Symbol(T2, Decl(noInferCommonPropertyCheck1.ts, 8, 26))

): void;

test2(partialObj1, partialObj2, someObj1);
>test2 : Symbol(test2, Decl(noInferCommonPropertyCheck1.ts, 6, 29))
>partialObj1 : Symbol(partialObj1, Decl(noInferCommonPropertyCheck1.ts, 0, 13))
>partialObj2 : Symbol(partialObj2, Decl(noInferCommonPropertyCheck1.ts, 1, 13))
>someObj1 : Symbol(someObj1, Decl(noInferCommonPropertyCheck1.ts, 2, 13))

declare function test3<T1, T2>(
>test3 : Symbol(test3, Decl(noInferCommonPropertyCheck1.ts, 14, 42))
>T1 : Symbol(T1, Decl(noInferCommonPropertyCheck1.ts, 16, 23))
>T2 : Symbol(T2, Decl(noInferCommonPropertyCheck1.ts, 16, 26))

a: T1,
>a : Symbol(a, Decl(noInferCommonPropertyCheck1.ts, 16, 31))
>T1 : Symbol(T1, Decl(noInferCommonPropertyCheck1.ts, 16, 23))

b: T2,
>b : Symbol(b, Decl(noInferCommonPropertyCheck1.ts, 17, 8))
>T2 : Symbol(T2, Decl(noInferCommonPropertyCheck1.ts, 16, 26))

c: NoInfer<T1 & T2>,
>c : Symbol(c, Decl(noInferCommonPropertyCheck1.ts, 18, 8))
>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --))
>T1 : Symbol(T1, Decl(noInferCommonPropertyCheck1.ts, 16, 23))
>T2 : Symbol(T2, Decl(noInferCommonPropertyCheck1.ts, 16, 26))

): void;

test3(partialObj1, partialObj2, someObj1);
>test3 : Symbol(test3, Decl(noInferCommonPropertyCheck1.ts, 14, 42))
>partialObj1 : Symbol(partialObj1, Decl(noInferCommonPropertyCheck1.ts, 0, 13))
>partialObj2 : Symbol(partialObj2, Decl(noInferCommonPropertyCheck1.ts, 1, 13))
>someObj1 : Symbol(someObj1, Decl(noInferCommonPropertyCheck1.ts, 2, 13))

71 changes: 71 additions & 0 deletions tests/baselines/reference/noInferCommonPropertyCheck1.types
@@ -0,0 +1,71 @@
//// [tests/cases/compiler/noInferCommonPropertyCheck1.ts] ////

=== noInferCommonPropertyCheck1.ts ===
declare const partialObj1: Partial<{ a: unknown; b: unknown }>;
>partialObj1 : Partial<{ a: unknown; b: unknown; }>
>a : unknown
>b : unknown

declare const partialObj2: Partial<{ c: unknown; d: unknown }>;
>partialObj2 : Partial<{ c: unknown; d: unknown; }>
>c : unknown
>d : unknown

declare const someObj1: { x: string };
>someObj1 : { x: string; }
>x : string

declare function test1<T>(a: T, b: NoInfer<T> & { prop?: unknown }): void;
>test1 : <T>(a: T, b: NoInfer<T> & { prop?: unknown;}) => void
>a : T
>b : NoInfer<T> & { prop?: unknown; }
>prop : unknown

test1(partialObj1, someObj1);
>test1(partialObj1, someObj1) : void
>test1 : <T>(a: T, b: NoInfer<T> & { prop?: unknown; }) => void
>partialObj1 : Partial<{ a: unknown; b: unknown; }>
>someObj1 : { x: string; }

declare function test2<T1, T2>(
>test2 : <T1, T2>(a: T1, b: T2, c: NoInfer<T1> & NoInfer<T2>) => void

a: T1,
>a : T1

b: T2,
>b : T2

c: NoInfer<T1> & NoInfer<T2>,
>c : NoInfer<T1> & NoInfer<T2>

): void;

test2(partialObj1, partialObj2, someObj1);
>test2(partialObj1, partialObj2, someObj1) : void
>test2 : <T1, T2>(a: T1, b: T2, c: NoInfer<T1> & NoInfer<T2>) => void
>partialObj1 : Partial<{ a: unknown; b: unknown; }>
>partialObj2 : Partial<{ c: unknown; d: unknown; }>
>someObj1 : { x: string; }

declare function test3<T1, T2>(
>test3 : <T1, T2>(a: T1, b: T2, c: NoInfer<T1 & T2>) => void

a: T1,
>a : T1

b: T2,
>b : T2

c: NoInfer<T1 & T2>,
>c : NoInfer<T1 & T2>

): void;

test3(partialObj1, partialObj2, someObj1);
>test3(partialObj1, partialObj2, someObj1) : void
>test3 : <T1, T2>(a: T1, b: T2, c: NoInfer<T1 & T2>) => void
>partialObj1 : Partial<{ a: unknown; b: unknown; }>
>partialObj2 : Partial<{ c: unknown; d: unknown; }>
>someObj1 : { x: string; }

@@ -0,0 +1,36 @@
noInferUnionExcessPropertyCheck1.ts(7,33): error TS2353: Object literal may only specify known properties, and 'y' does not exist in type 'NoInfer<{ x: string; }> | (() => NoInfer<{ x: string; }>)'.
noInferUnionExcessPropertyCheck1.ts(15,33): error TS2353: Object literal may only specify known properties, and 'y' does not exist in type 'NoInfer<{ x: string; }> | NoInfer<() => { x: string; }>'.
noInferUnionExcessPropertyCheck1.ts(23,33): error TS2353: Object literal may only specify known properties, and 'y' does not exist in type '{ x: string; } | (() => { x: string; })'.


==== noInferUnionExcessPropertyCheck1.ts (3 errors) ====
declare function test1<T extends { x: string }>(
a: T,
b: NoInfer<T> | (() => NoInfer<T>),
): void;

test1({ x: "foo" }, { x: "bar" }); // no error
test1({ x: "foo" }, { x: "bar", y: 42 }); // epc error
~
!!! error TS2353: Object literal may only specify known properties, and 'y' does not exist in type 'NoInfer<{ x: string; }> | (() => NoInfer<{ x: string; }>)'.

declare function test2<T extends { x: string }>(
a: T,
b: NoInfer<T> | NoInfer<() => T>,
): void;

test2({ x: "foo" }, { x: "bar" }); // no error
test2({ x: "foo" }, { x: "bar", y: 42 }); // epc error
~
!!! error TS2353: Object literal may only specify known properties, and 'y' does not exist in type 'NoInfer<{ x: string; }> | NoInfer<() => { x: string; }>'.

declare function test3<T extends { x: string }>(
a: T,
b: NoInfer<T | (() => T)>,
): void;

test3({ x: "foo" }, { x: "bar" }); // no error
test3({ x: "foo" }, { x: "bar", y: 42 }); // epc error
~
!!! error TS2353: Object literal may only specify known properties, and 'y' does not exist in type '{ x: string; } | (() => { x: string; })'.

89 changes: 89 additions & 0 deletions tests/baselines/reference/noInferUnionExcessPropertyCheck1.symbols
@@ -0,0 +1,89 @@
//// [tests/cases/compiler/noInferUnionExcessPropertyCheck1.ts] ////

=== noInferUnionExcessPropertyCheck1.ts ===
declare function test1<T extends { x: string }>(
>test1 : Symbol(test1, Decl(noInferUnionExcessPropertyCheck1.ts, 0, 0))
>T : Symbol(T, Decl(noInferUnionExcessPropertyCheck1.ts, 0, 23))
>x : Symbol(x, Decl(noInferUnionExcessPropertyCheck1.ts, 0, 34))

a: T,
>a : Symbol(a, Decl(noInferUnionExcessPropertyCheck1.ts, 0, 48))
>T : Symbol(T, Decl(noInferUnionExcessPropertyCheck1.ts, 0, 23))

b: NoInfer<T> | (() => NoInfer<T>),
>b : Symbol(b, Decl(noInferUnionExcessPropertyCheck1.ts, 1, 7))
>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(noInferUnionExcessPropertyCheck1.ts, 0, 23))
>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(noInferUnionExcessPropertyCheck1.ts, 0, 23))

): void;

test1({ x: "foo" }, { x: "bar" }); // no error
>test1 : Symbol(test1, Decl(noInferUnionExcessPropertyCheck1.ts, 0, 0))
>x : Symbol(x, Decl(noInferUnionExcessPropertyCheck1.ts, 5, 7))
>x : Symbol(x, Decl(noInferUnionExcessPropertyCheck1.ts, 5, 21))

test1({ x: "foo" }, { x: "bar", y: 42 }); // epc error
>test1 : Symbol(test1, Decl(noInferUnionExcessPropertyCheck1.ts, 0, 0))
>x : Symbol(x, Decl(noInferUnionExcessPropertyCheck1.ts, 6, 7))
>x : Symbol(x, Decl(noInferUnionExcessPropertyCheck1.ts, 6, 21))
>y : Symbol(y, Decl(noInferUnionExcessPropertyCheck1.ts, 6, 31))

declare function test2<T extends { x: string }>(
>test2 : Symbol(test2, Decl(noInferUnionExcessPropertyCheck1.ts, 6, 41))
>T : Symbol(T, Decl(noInferUnionExcessPropertyCheck1.ts, 8, 23))
>x : Symbol(x, Decl(noInferUnionExcessPropertyCheck1.ts, 8, 34))

a: T,
>a : Symbol(a, Decl(noInferUnionExcessPropertyCheck1.ts, 8, 48))
>T : Symbol(T, Decl(noInferUnionExcessPropertyCheck1.ts, 8, 23))

b: NoInfer<T> | NoInfer<() => T>,
>b : Symbol(b, Decl(noInferUnionExcessPropertyCheck1.ts, 9, 7))
>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(noInferUnionExcessPropertyCheck1.ts, 8, 23))
>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(noInferUnionExcessPropertyCheck1.ts, 8, 23))

): void;

test2({ x: "foo" }, { x: "bar" }); // no error
>test2 : Symbol(test2, Decl(noInferUnionExcessPropertyCheck1.ts, 6, 41))
>x : Symbol(x, Decl(noInferUnionExcessPropertyCheck1.ts, 13, 7))
>x : Symbol(x, Decl(noInferUnionExcessPropertyCheck1.ts, 13, 21))

test2({ x: "foo" }, { x: "bar", y: 42 }); // epc error
>test2 : Symbol(test2, Decl(noInferUnionExcessPropertyCheck1.ts, 6, 41))
>x : Symbol(x, Decl(noInferUnionExcessPropertyCheck1.ts, 14, 7))
>x : Symbol(x, Decl(noInferUnionExcessPropertyCheck1.ts, 14, 21))
>y : Symbol(y, Decl(noInferUnionExcessPropertyCheck1.ts, 14, 31))

declare function test3<T extends { x: string }>(
>test3 : Symbol(test3, Decl(noInferUnionExcessPropertyCheck1.ts, 14, 41))
>T : Symbol(T, Decl(noInferUnionExcessPropertyCheck1.ts, 16, 23))
>x : Symbol(x, Decl(noInferUnionExcessPropertyCheck1.ts, 16, 34))

a: T,
>a : Symbol(a, Decl(noInferUnionExcessPropertyCheck1.ts, 16, 48))
>T : Symbol(T, Decl(noInferUnionExcessPropertyCheck1.ts, 16, 23))

b: NoInfer<T | (() => T)>,
>b : Symbol(b, Decl(noInferUnionExcessPropertyCheck1.ts, 17, 7))
>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --))
>T : Symbol(T, Decl(noInferUnionExcessPropertyCheck1.ts, 16, 23))
>T : Symbol(T, Decl(noInferUnionExcessPropertyCheck1.ts, 16, 23))

): void;

test3({ x: "foo" }, { x: "bar" }); // no error
>test3 : Symbol(test3, Decl(noInferUnionExcessPropertyCheck1.ts, 14, 41))
>x : Symbol(x, Decl(noInferUnionExcessPropertyCheck1.ts, 21, 7))
>x : Symbol(x, Decl(noInferUnionExcessPropertyCheck1.ts, 21, 21))

test3({ x: "foo" }, { x: "bar", y: 42 }); // epc error
>test3 : Symbol(test3, Decl(noInferUnionExcessPropertyCheck1.ts, 14, 41))
>x : Symbol(x, Decl(noInferUnionExcessPropertyCheck1.ts, 22, 7))
>x : Symbol(x, Decl(noInferUnionExcessPropertyCheck1.ts, 22, 21))
>y : Symbol(y, Decl(noInferUnionExcessPropertyCheck1.ts, 22, 31))