为什么 TypeScript 泛型会抑制类型错误?
请看以下 TypeScript 示例。
function expectInterface(args: { foo: string }): void {}
function passthrough<T>(fields: T): T {
return fields
}
expectInterface({
foo: 'bar',
badKey: 'baz', // error
})
expectInterface(passthrough({
foo: 'bar',
badKey: 'baz', // no error
}))
为什么 passthrough 会导致 badKey 上的类型错误被抑制?
答案#1
badKey 上的错误被抑制,因为 TypeScript 使用的是 结构化类型 系统。这意味着 如果类型 X 至少包含类型 Y 所有的字段,那么类型 X 与类型 Y 兼容。有关更具体的细节,请参见 TypeScript 文档中的类型兼容性部分。
为了清楚起见,我们将你的接口命名为HasFoo和HasFooAndBadKey。
interface HasFoo {
foo: string;
}
interface HasFooAndBadKey {
foo: string;
badKey: string;
}
你可以看到 HasFooAndBadKey 包含字段 foo,这意味着它与类型 HasFoo 兼容。因此这段代码是有效的,并且可以顺利编译:
const withBadKey: HasFooAndBadKey = {
foo: "bar",
badKey: "baz"
};
const item: HasFoo = withBadKey;
然而,使用字面量以相同方式分配会失败,并出现错误 Object literal may only specify known properties, and 'badKey' does not exist in type 'HasFoo'.
const other: HasFoo = {
foo: "bar",
badKey: "baz" // Error
}
因此,你的错误不是由泛型引起的。你的错误发生的原因是你只能在字面量中指定已知属性。你的泛型直通函数不会导致编译时错误,因为在这种情况下,它返回的是与HasFoo类型兼容的非字面量值。将直通函数显式地类型化为接受HasFoo并传递同一个包含badKey的字面量,将会产生相同的错误。
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
}))
