假设我有一个接口(interface) Foo
和一个函数 bar
,它接受一个返回 Foo
的回调。
interface Foo {
foo: string;
}
function bar(callback: () => Foo): Foo {
return callback();
}
当我调用它时,如果我向返回值添加 foo
以外的属性,编译器不会抛出错误。
// throws an error as expected
const x: Foo = { foo: 'abc', baz: 'def' };
// does not throw an error
const y = bar(() => ({ foo: 'abc', baz: 'def' }));
如何确保该对象没有除 Foo
中指定的属性外的其他属性?我试过使用 type
而不是 interface
得到相同的结果。这是 TypeScript playground .
最佳答案
Excess property checking仅在特定情况下才会发生,而您已经找到了不会发生的情况。 TypeScript 中的类型(包括 Foo
等接口(interface))是开放式和可扩展的;你可以定义一个接口(interface),比如
interface Baz extends Foo {
baz: string;
}
并在 bar()
回调中返回一个 Baz
,它是一个有效的 Foo
:
const baz: Baz = { foo: "abc", baz: "def" };
const foo: Foo = baz; // no error
const z = bar(() => baz); // no error...
TypeScript 没有对应于 exactly 的具体类型Foo
没有额外的属性。但是,可以使用 generic constraint表示“没有已知额外属性的Foo
”。请注意“已知”警告,那里...稍后会出现:
function bar<
F extends Foo & { [K in keyof F]: K extends keyof Foo ? Foo[K] : never }
>(callback: () => F): Foo {
return callback();
}
在这里,我将 bar()
变成了一个通用函数,它接受类型为 () => F
的回调,其中 F
被限制为 Foo
,以及一个映射的条件类型。此映射类型采用任何已知的额外键并将属性类型更改为 never
,这是不可能的。例如,如果 F
是 Baz
,则约束是 Foo & { foo: string, baz: never }
。由于 Baz
不扩展 { foo: string, baz: never }
,如果您尝试调用 bar()
,您将收到错误消息在已知返回 Baz
的函数上:
const y = bar(() => ({ foo: "abc", baz: "def" })); // error, generic constraint failed
// -----------------------------> ~~~
// "string" is not assignable to "never"
const z = bar(() => baz); // error! "string" is not assignable to "never"
// ---------------> ~~~
这至少会阻止人们返回带有额外属性的对象字面量。
然而,这并不是一个完美的解决方案。之前的变量 foo
被注释为 Foo
类型,但被初始化为 Baz
类型的值。编译器只知道或关心 foo
是 Foo
类型。它完全忘记了 foo
也是 Baz
类型。因此它不知道 foo
有一个 "baz"
属性,并且不会阻止这个:
const oops = bar(() => foo); // still no error...
// foo has a "baz" property but the compiler forgot
// no way around this in TypeScript, sorry
恐怕除了运行时检查之外,没有什么可做的。如果你真的想在 TypeScript 中看到确切的类型,你可能想转到 relevant GitHub issue并给它一个👍。但也许上述解决方案足以满足您的用例。
好吧;希望有所帮助。祝你好运!
关于typescript - 防止回调返回类型在 TypeScript 中包含其他属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57316443/