对于我正在从事的一些教育性副项目,我在工厂类中使用了Phaser CE框架,这样,根据生成的Phaser.Text
元素,可以根据我使用预定义样式编写的JSON文件。
但是,我还有一个INamed
属性,可以粘贴到带有名称的内容上。这在Array<>
上也有扩展名,使我可以轻松地从名称中获取数组中的项。
因此,从理论上讲,我可以这样采集数据:
styles.json:
{
"text": [
{
"name": "default",
"fill": "#FFF",
"stroke": "#333",
"strokeWidth": 1
},
{
"name": "snazzy",
"fill": "#F33",
"stroke": "#633",
"strokeWidth": 2
},
]
}
...并使用我们的文本工厂...
TextFactory.ts:
namespace Main.UI {
export class TextFactory {
public styles: (Phaser.PhaserTextStyle & INamed)[] = [];
public constructor() {}
public initialize() {
const data = game.cache.getJSON('ui-styles');
const textStyles = data['text'];
for (let current of textStyles) {
this.styles.push(current);
}
}
public create(
x: number,
y: number,
text: string,
style?: string
): Phaser.Text {
let styleData: (Phaser.PhaserTextStyle & INamed);
if (!style)
styleData = this.styles[0];
else
styleData = this.styles.getByName(style);
// !!! Problem is here !!!
const newText = game.add.text(x, y, text, <Phaser.PhaserTextStyle>styleData);
return newText;
}
}
}
...并在游戏状态下使用它:
GameState.ts:
namespace Main.States {
export class GameState extends Phaser.State {
private textFactory: UI.TextFactory = null;
public preload(): void {
game.load.json('ui-styles', 'assets/data/styles.json');
}
public create(): void {
this.textFactory = new UI.TextFactory();
this.textFactory.initialize();
const testText = this.textFactory.create(2, 2, "This should be white with a dark gray outline...");
const otherText = this.textFactory.create(2, 52, "SNAZZY!", "snazzy");
}
}
}
如果使用Phaser中的相关样板运行该常规设置,则文本将呈现,但将变为黑色且未设置样式。我已经执行了一些健全性检查,发现联合类型(Phaser.PhaserTextStyle & INamed)
似乎是给我造成麻烦的原因-调用game.add.text
只希望得到一个Phaser.PhaserTextStyle
。
我尝试将textStyle
中的TextFactory.create
变量强制转换为Phaser.PhaserTextStyle
,但这似乎没有效果-我的文本仍然呈现未样式。我进行了进一步的“ Silly Developer”检查,例如确保已保存并构建了更改,并添加了调试消息,但是这些并没有产生任何见解,可以帮助您了解我所做的新的愚蠢的事情。因此,我的问题...
问题: :我可以采用哪种方式在TypeScript中使用(A & B)
之类的对象并将其用作A
或B
类型?
答案 0 :(得分:1)
简短的回答:您不能。
长答案:将A & B
用作A
或B
的唯一类型安全方法是使用类型防护。但是,即使这种方法也有局限性。
请考虑以下两种情况,其中两种形状都有相同的名称(id
):
interface A {
name: string;
id: string;
}
interface B {
id: number;
}
type Intersection = A & B;
const test: Intersection = {
id: 0, // Error — cannot be implemented! Nothing is both a string and a number
name: ''
}
相交的A
和B
告诉TypeScript id
属性必须同时为string & number
,这是无法实现的。即使这种概念存在于类型级别,也不能具有运行时表示形式。
即使-假设-我们可以有一个Intersection
(有人可以使用类型断言),TypeScript仍将允许我们编写将在运行时爆炸的代码。
declare function alpha(argument: A): void;
declare function beta(argument: B): void;
declare const intersection: Intersection;
alpha(intersection); // TypeScript allows that, but it can fail in runtime if `alpha` expects `id` to be of type `string`.
可以通过创建用户定义的类型防护来减轻风险。
/**
* Type-safe approach.
*/
declare function isA(argument: unknown): argument is A;
declare function isB(argument: unknown): argument is B;
if (isA(intersection)) {
alpha(intersection);
}
if (isB(intersection)) {
beta(intersection);
}
不幸的是,这些需要在运行时中存在,并且没有什么可以阻止开发人员错误地实现它们。
答案 1 :(得分:1)
我认为对象的类型与问题无关。
game.add.text
函数可能在对象上具有额外的name
属性。
类型断言在运行时不做任何事情,因此您可以使用它们来删除属性,可以使用它们来更改编译器知道的类型(在这种情况下无济于事)。
您可以使用delete删除额外的属性:
delete styleData['name']
或者您可以创建一个不带属性的新对象,并使用spread来使用它:
let { name, ...styleDataNoName } = styleData;