我试图从官方 TS 文档中理解混音。
https://www.typescriptlang.org/docs/handbook/mixins.html
我在这里设立了一个游乐场。
我的问题是:我如何定义jump和duck来操作Sprite中定义的x和y属性?
即在这个例子中,我希望使用jump()和duck()来操作Sprite类中的x和y。那可能吗?将 x 和 y 属性添加到可跳和可鸭子中似乎很麻烦且重复。
一般来说,如果没有混合类之间属性的交叉操作,我很难看到 TS mixin 的真实用例。
代码是:
(() => {
// This can live anywhere in your codebase:
function applyMixins(derivedCtor: any, constructors: any[]) {
constructors.forEach((baseCtor) => {
Object.getOwnPropertyNames(baseCtor.prototype).forEach((name) => {
Object.defineProperty(
derivedCtor.prototype,
name,
Object.getOwnPropertyDescriptor(baseCtor.prototype, name) ||
Object.create(null)
);
});
});
}
// Each mixin is a traditional ES class
class Jumpable {
jump() {
console.log("jump");
}
}
class Duckable {
duck() {
console.log("duck");
}
}
// Including the base
class Sprite {
x = 0;
y = 0;
}
// Then you create an interface which merges
// the expected mixins with the same name as your base
interface Sprite extends Jumpable, Duckable {}
// Apply the mixins into the base class via
// the JS at runtime
applyMixins(Sprite, [Jumpable, Duckable]);
let player = new Sprite();
player.jump();
player.duck();
console.log(player.x, player.y);
})();
Run Code Online (Sandbox Code Playgroud)
通过使用声明合并,您可以手动告诉编译器您的混入正在做什么,因为它本身无法弄清楚这一点。该文档将您正在做的事情称为“替代模式”,并且“此模式对编译器的依赖较少,而对代码库的依赖更多,以确保运行时和类型系统正确保持同步。”
因此,您应该期待一些额外的单调乏味的工作,让编译器相信您看似独立的Jumpable
类Duckable
可以访问x
其中y
未声明的属性。也许最简单的方法是为具有正确形状的东西定义一个接口:
interface Positioned {
x: number,
y: number
}
Run Code Online (Sandbox Code Playgroud)
然后告诉编译器jump()
和方法旨在通过使用参数duck()
来操作这种形状的东西:this
class Jumpable {
jump(this: Positioned) {
console.log("jump");
this.y += 2;
}
}
class Duckable {
duck(this: Positioned) {
console.log("duck");
this.y -= 1;
}
}
Run Code Online (Sandbox Code Playgroud)
这既可以让您在方法内部进行修改x
,y
也可以在您尝试像常规方法一样使用 mixin 方法时发出警告:
const j = new Jumpable();
j.jump(); // compiler error
// The 'this' context of type 'Jumpable' is not
// assignable to method's 'this' of type 'Positioned'.
Run Code Online (Sandbox Code Playgroud)
一旦你这样做了,你就可以证明一切都“有效”:
class Sprite {
name = "";
x = 0;
y = 0;
constructor(name: string) {
this.name = name;
}
}
interface Sprite extends Jumpable, Duckable { }
applyMixins(Sprite, [Jumpable, Duckable]);
const player = new Sprite("Player");
console.log(player.name, player.x, player.y); // Player 0 0
player.jump(); // jump
console.log(player.name, player.x, player.y); // Player 0 2
player.duck(); // duck
console.log(player.name, player.x, player.y); // Player 0 1
Run Code Online (Sandbox Code Playgroud)
如果您要使用替代模式,那就太好了。
推荐的非替代 mixin 模式是使用类工厂函数,该函数使用标准类继承来通过 mixin 扩展基类。通过将基类限制为Positioned
对象的构造函数,您可以授予 mixin 访问基类x
和y
属性的权限:
function Jumpable<TBase extends new (...args: any[]) => Positioned>(Base: TBase) {
return class Jumpable extends Base {
jump() {
console.log("jump");
this.y += 2;
}
};
}
function Duckable<TBase extends new (...args: any[]) => Positioned>(Base: TBase) {
return class Duckable extends Base {
duck() {
console.log("duck");
this.y -= 1;
}
};
}
Run Code Online (Sandbox Code Playgroud)
Jumpable
和工厂函数内的类表达式Duckable
允许jump()
和duck()
访问this.y
,因为Base
构造函数属于 类型TBase
,已知该类型可以构造 的某些子类型Positioned
。
现在,您无需手动将 mixin 方法应用于原型,只需在构造函数上调用 mixin 工厂函数即可:
class BaseSprite {
name = "";
x = 0;
y = 0;
constructor(name: string) {
this.name = name;
}
}
const Sprite = Jumpable(Duckable(BaseSprite));
Run Code Online (Sandbox Code Playgroud)
并注意不需要声明合并;编译器会自动理解Sprite
实例Positioned
还有jump()
和duck()
方法:
const player = new Sprite("Player");
console.log(player.name, player.x, player.y); // Player 0 0
player.jump(); // jump
console.log(player.name, player.x, player.y); // Player 0 2
player.duck(); // duck
console.log(player.name, player.x, player.y); // Player 0 1
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1256 次 |
最近记录: |