The error on badKey is suppressed because TypeScript uses a structural typing system. This means that one type X is compatible with another type Y if type X contains at least all the fields which type Y has. See the type compatibility section in the TypeScript docs for more specific details.
For clarity, let's name your interfaces HasFoo and HasFooAndBadKey.
interface HasFoo {
foo: string;
}
interface HasFooAndBadKey {
foo: string;
badKey: string;
}
You can see that HasFooAndBadKey contains the field foo, which means it is compatible with type HasFoo. Therefore this code is valid and compiles just fine:
const withBadKey: HasFooAndBadKey = {
foo: "bar",
badKey: "baz"
};
const item: HasFoo = withBadKey;
However, assigning the same way using a literal will fail with the error Object literal may only specify known properties, and 'badKey' does not exist in type 'HasFoo'.
const other: HasFoo = {
foo: "bar",
badKey: "baz" // Error
}
Therefore your error isn't caused by generics. Your error is happening because you are only allowed to specify known properties in literals. Your generic passthrough function does not result in a compile time error because in this case it is returning a non-literal value which is type compatible with HasFoo. Explicitly typing passthrough to take a HasFoo and passing it the same literal containing badKey will result in the same error.
interface HasFoo {
foo: string;
}
interface HasFooAndBadKey {
foo: string;
badKey: string;
}
function expectInterface(args: HasFoo): void {}
function passthrough<T>(fields: T): T {
return fields
}
// Expecting literal HasFoo, which can only have a foo field
expectInterface({
foo: 'bar',
badKey: 'baz', // error
})
const item: HasFooAndBadKey = {
foo: 'bar',
badKey: 'baz'
};
expectInterface(item); // No error
// Non-literal value returned by passthrough. The non-literal
// is type compatible with HasFoo, so no error occurs.
expectInterface(passthrough<HasFooAndBadKey>({
foo: 'bar',
badKey: 'baz', // no error
}))
// Now passthrough shows an error becuase we told it to expect a HasFoo,
// and this HasFoo literal cannot contain any other key than 'foo'.
expectInterface(passthrough<HasFoo>({
foo: 'bar',
badKey: 'baz', // error
}))