(Switch the version selector to 5.9.3 to see the expected errors.)
⯠Playground Link
No response
ð» Code
type Inner = { a?: boolean };
type Outer = { inner?: Inner };
type Identity<T> = { [K in keyof T]: T[K] };
declare function f<T extends Outer>(x: Identity<T>): T;
// 6.0+ : NO ERROR (regression). 5.9.3 : correctly errors on `oops`.
f({ inner: { a: true, oops: true } });
// errors in every version â the nested literal has no valid property
f({ inner: { oops: true } });
ð Actual behavior
From 6.0.0-beta onwards, the first call compiles with no error. oops is not a property of Inner, and the object literal is fresh, but no TS2353 / TS2561 is reported.
The second call still errors, which points at the trigger: the excess property check on the nested literal is skipped as soon as that literal contains at least one property that does exist in the target type. A literal made up exclusively of unknown properties is still checked.
Confirmed with a wider literal on 7.0.2 â one valid key is enough to suppress the check for all of the invalid ones:
type Inner = { a?: boolean; b?: boolean };
f({ inner: { a: true, bad1: true, bad2: true } }); // 5.9.3: errors on bad1 â 7.0.2: no error
f({ inner: { bad3: true, bad4: true } }); // errors on bad3 in both
Narrowing which construct is required (all checked on 5.9.3 vs 7.0.2):
parameter type
5.9.3
7.0.2
f<T extends Outer>(x: { [K in keyof T]: K extends keyof Outer ? T[K] : never })
errors
no error
f<T extends Outer>(x: { [K in keyof T]: T[K] })
errors
no error
f(x: { [K in keyof Outer]: Outer[K] }) â same mapped type, not generic
errors
errors
f<T extends Outer>(x: T) â generic, no mapped type
no error
no error
So the regression is specific to a homomorphic mapped type over the type parameter â i.e. a reverse-mapped-type inference site. The conditional in the mapped type's template is not required, and dropping the generic makes the check work again.
The top level of the argument is unaffected â f({ nope: true }) still errors in 6.0+. Only nested literals lose the check.
ð Expected behavior
The first call should report the same error 5.9.3 does:
error TS2353: Object literal may only specify known properties, and 'oops' does not exist in type 'Inner'.
Freshness of a nested object literal shouldn't depend on whether it happens to also contain a valid property.
Additional information about the issue
The affected shape is not exotic â it is the core of the types Prisma Client generates, so the regression converts a compile-time error into a runtime failure for any Prisma codebase that has moved to 6.0+. Prisma's generated delegate methods are declared as:
findMany<T extends PostFindManyArgs>(
args: SelectSubset<T, PostFindManyArgs<ExtArgs>>
): ...
// SelectSubset<T, U> = { [K in keyof T]: K extends keyof U ? T[K] : never }
// PostFindManyArgs = { select?: PostSelect | null; where?: PostWhereInput; ... }
which is exactly row 1 of the table above. Selecting a field that does not exist on the model:
is rejected by 5.9.3 with TS2353 against the generated PostSelect type (or TS2561 with a "did you mean" suggestion when a similarly named field exists), and accepted silently from 6.0.0-beta onwards. The query then fails at runtime: Prisma validates it against its own model metadata before it reaches the database and throws PrismaClientValidationError â Unknown field 'not_a_real_field' for select statement on model 'Post'.
The failure is quiet in every layer above the compiler too: @typescript-eslint's type-checked rules do not model the generated field set, and nothing in the expression is any, so the no-unsafe-* rules never fire.
Compiler options make no difference â skipLibCheck, strict, noUncheckedIndexedAccess, exactOptionalPropertyTypes and moduleResolution were each toggled independently against the failing case with no change in output. Only the compiler version matters.