TypeScript:"A extends B" 的输入问题

TypeScript: Typing problem with "A extends B"

[编辑 - 结束语]:事实证明,下面的问题主要是一些次优类型的结果,通常你可能不会像我在我的程序中那样遇到这样的问题=>现在有了这些知识,我的问题似乎不再有意义(正如其他人已经在下面提到的那样)。


请查看以下 TypeScript 代码片段。 作为 B extends A 这条 c: 8 行当然是允许的。 我如何更改 function f 的类型以禁止 ab 旁边的其他键?

type A = { a: number, b: number }

function f<B extends A>(b: B): void {
}

f({
  a: 3,
  b: 4,
  c: 8 // <- This shall cause a compile error
})

» Demo

PS:当然function f(b: A) {}不是我要找的。这里的这个例子只是简化了,现实世界的用例要复杂得多...... function f 保持通用性很重要。


[编辑]:由于我上面的简化引起了一些混乱: 请在这里找到一个更复杂的例子,上面我的问题的答案可能会有所帮助(一个虚构的 UI 组件 API):

请查看Demo-A中的类型错误。这将在 Demo-B 中修复。

Demo-A (with type error)

Demo-B (without type error)

但是正如您在 Demo-B 中看到的那样,您现在可以向组件配置添加无效参数 - 这不是很好......这就是我提出上述问题的原因。

[编辑 - 稍后] 不是我上面问题的答案,但至少 Demo-B 的以下修改应该可以按预期工作:Demo-C

该行为是设计使然。 TS 是结构类型语言,其中类型兼容性基于子类型化,换句话说,具有所有约束的每个类型都将通过规则。在您的示例中,值类型包含所有需要的字段,因此 'a' 和 'b',因此一切正常。

您没有理由不允许传递此类对象,因为内部的附加字段不会让您有所作为。说你扩展意味着说你可以在像对象这样的组合方面拥有更多的领域。

如果您想要一个子类型,则附加,但不幸的是,对于精确字段,唯一符合您需要的类型将是 A,因此函数签名应该是 f(a: A)

需要说明的是,即使使用 f(a: A),您也可以通过附加字段传递值,只有对象字面量会被严格验证。任何具有更多字段的赋值都可以使用而不会引起编译器的抱怨。原因我已经描述过了。