Skip to content

Commit

Permalink
Fixed excess and common property checks with NoInfer
Browse files Browse the repository at this point in the history
  • Loading branch information
Andarist committed Mar 8, 2024
1 parent 881f449 commit bcbd419
Show file tree
Hide file tree
Showing 9 changed files with 486 additions and 1 deletion.
9 changes: 8 additions & 1 deletion src/compiler/checker.ts
Expand Up @@ -23756,6 +23756,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 (isNoInferType(type)) {
return isWeakType((type as SubstitutionType).baseType);
}
if (type.flags & TypeFlags.Intersection) {
return every((type as IntersectionType).types, isWeakType);
}
Expand Down Expand Up @@ -32464,7 +32467,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return true;
}
}
else if (targetType.flags & TypeFlags.UnionOrIntersection && isExcessPropertyCheckTarget(targetType)) {
if (isNoInferType(targetType)) {
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 @@ -32477,6 +32483,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 ||
isNoInferType(type) && 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))

0 comments on commit bcbd419

Please sign in to comment.