在 TypeScript 中要求模块实现接口似乎非常有用。他们选择不实施这种能力有什么特别的原因吗?
我想不出任何理由为什么不允许模块实现接口是不可取的,但如果是这样,那么有没有其他方法要求模块提供一组特定的属性/方法?
在 TypeScript 中要求模块实现接口似乎非常有用。他们选择不实施这种能力有什么特别的原因吗?
我想不出任何理由为什么不允许模块实现接口是不可取的,但如果是这样,那么有没有其他方法要求模块提供一组特定的属性/方法?
如果模块不遵循这样的接口(技术上非零运行时开销,但实际上不太重要),您可以强制编译错误:
interface foo {
bar(): number;
}
module mod {
export function bar(): number {
return 0;
}
}
var mod_is_foo: foo = mod; // errors if you change 'number' to 'string' above
至于为什么不能说module mod implements foo
?所有功能都从负 100 开始。
var
编辑添加——如果你想避免创建一个新的顶级变量,你可以写一些其他(有点古怪)的东西来代替上面的语句:
<foo>mod; // Shortest, probably fastest?
<foo>undefined === mod; // Clearest non-var version?
Ryan 实际上是 Typescript 团队的成员,所以他的回答几乎是最终的。但我个人认为它不提供您所寻求的语法很好。
原因是模块更像是一个实例,而不是可以被实例化的东西(例如一个类)。通过这种方式,它更接近于一个 var 而不是一个类。
所以原因就变成了它类似于你写的原因:
var x:foo;
代替
var x implements foo;
模块的行为与您可以做的相同
var y:foo = mod;
正如瑞安已经指出的那样。
还要检查一下:
interface foo {
bar(): number;
}
module mod {
export function bar(): number {
return 0;
}
}
var x: foo;
x = mod;
// Still a bit whacky but allowed as module is a type as well as an instance
var y:mod;
y = x;
您可以将您的模块放在具有明确定义接口的 TypeScript 类中。一个小缺点是您需要在使用之前实例化该类。
./脚.ts
export default interface Foo {
bar(): number;
}
./FooImpl.ts
import Foo from "./Foo";
export default class FooImpl implements Foo {
bar(): number {
return 13;
}
}
./Main.ts
import Foo from "./Foo";
import FooImpl from "./FooImpl";
const foo: Foo = new FooImpl();
export default function main(): number {
return foo.bar();
}
./FooMock.ts
import Foo from "./Foo";
export default class FooMock implements Foo {
bar(): number {
return 42;
}
}
./Main.test.ts
import main from "./Main";
jest.mock('./FooImpl', () => {
return jest.requireActual('./FooMock').default;
});
describe('Main', () => {
it('should return 42 for the mock Foo implementation', () => {
expect(main()).toBe(42);
});
});