typescript - 如何让接口(interface)包含子类型,同时本身也是有效类型

标签 typescript types interface namespaces typescript-typings

我正在尝试创建一个 namespace /类/接口(interface)/类型(无论我需要什么,我都研究过它们,但似乎没有一个能够满足我的需要),它具有一些属性方法类型。但其中也有子类型,如 Character.StatsCharacter.Currency虽然仍然有只是 Character作为有效类型

我需要如何格式化/布局我的打字文件才能允许此 classes.ts文件是有效的 typescript 吗?

// statement to import Character type

class Player implements Character {
    constructor (
        public name: string, // a property
        public stats: Character.Stats, // a property with a subtype typing
        public currency: Character.Currency
    ) {
        this.name = name
        this.stats = stats
        this.currency = currency
    }

    attack(target: Player) { // parameter is typing of its own class
        console.log(`Attacked ${Character.name}`)
    }
}

目前使用 typescript 可以实现这一点吗?

下面是我当前的测试代码库:

types/items.ts

export interface Type {
    name: string,
    sprite: string,
}

types/characters.ts

import * as Classes from "../classes";

export interface Stats {
    health: number,
    attack: number,
    defence: number,
}

export interface Currency {
    gold: number,
    ridium: number
}

export interface Type {
    name: string,
    stats: Stats
    currency: Currency

    announce(sentence: string): void,
    attack(target: Type): void,
    heal(item: Classes.HealingItem): void

}

classes.ts

import * as Characters from "./types/characters";
import * as Items from "./types/items"

export class Character implements Characters.Type {

    constructor(
        public name: string,
        public stats: Characters.Stats,
        public currency: Characters.Currency
    ) {
        this.name = name;
        this.stats = stats
        this.currency = currency
    }

    announce(sentence: string) {
        console.log(`I, ${this.name}, ${sentence}`)
    }

    attack(target: Character): void {
        this.announce(`am going to attack ${target.name}`)
    }

    heal(item: HealingItem) {
        this.stats.health += item.healthPoints;
        this.announce(`used ${item.name} to heal my hp from ${this.stats.health - item.healthPoints} to ${this.stats.health}`)
    }

}

export class HealingItem implements Items.Type {

    constructor(
        public name: string,
        public sprite: string,
        public healthPoints: number
    ) {
        this.name = name
        this.sprite = sprite
        this.healthPoints = healthPoints
    }

}

index.ts

import * as Classes from "./classes";

const hero = new Classes.Character("Bob", // name
    { // stats
        health: 100,
        attack: 10,
        defence: 25
    },
    { // currency
        gold: 50,
        ridium: 0
    }
)

const apple = new Classes.HealingItem("Apple", "./sprites/apple.sprite", 25);

hero.heal(apple);
hero.attack(hero);

当前的代码一切正常,但似乎当前的布局将来会引起问题。由于Items.Type/Characters.Type

如果这是我能得到的最接近我想要的结果,那么......

简而言之...

我想要这样的东西

interface B {
    x: string
}

namespace A {
    export type v1 = number
    export type v2 = boolean
    export type v3 = B
}

let p: A = {
    v1: 9,
    v2: true,
    v3: { x: "some string" }
};
let q: A.v1 = 2;
let r: A.v2 = false;
let s: A.v3 = { x: "strung" };

这不是有效的代码 let p: A不允许将命名空间作为类型,但希望这能够描述我想要实现的目标。

最佳答案

如果我理解正确的话,你希望能够去掉.Type后缀,这样你就可以直接写:

class Character implements Characters {
  constructor(
    public name: string,
    public stats: Characters.Stats, // Type as a "member" of Characters
    public currency: Characters.Currency
  ) {}
  // Etc.
}

您感到困惑,因为您没有看到如何将 StatsCurrency 类型“嵌入”为 Characters 的“成员”。

一方面,如果它是一个接口(interface),任何“嵌入”类型也将被视为该接口(interface)的成员。

另一方面,如果它是一个命名空间,它就不能直接用作类型本身。

您实际上可以同时实现这两个目标,因为命名空间启用 merging转换为其他类型。

这样,我们就声明了接口(interface),并将其与命名空间合并使用相同的名称 .

export interface Characters {
    name: string
    stats: Characters.Stats
    currency: Characters.Currency

    announce(sentence: string): void
    attack(target: Characters): void
    heal(item: HealingItem): void
}

// Merge the namespace with the type of the same name
namespace Characters {
    // Make sure the "embedded" types are explicitly exported
    export interface Stats {
        health: number
        attack: number
        defence: number
    }
    export interface Currency {
        gold: number
        ridium: number
    }
}

Playground Link


顺便说一句,在构造函数中,如果类成员已用作具有相同名称和可见性修饰符的构造函数参数,则无需显式分配它们,请参阅 Class Parameter Properties :

TypeScript offers special syntax for turning a constructor parameter into a class property with the same name and value. These are called parameter properties and are created by prefixing a constructor argument with one of the visibility modifiers public, private, protected, or readonly.

关于typescript - 如何让接口(interface)包含子类型,同时本身也是有效类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74104654/

相关文章:

JavaScript 函数参数

Delphi:从接口(interface)引用调用子类的方法

angular - NPM 和在 Angular2 TypeScript 中加载外部库

javascript - 用于组件目的的 Angular 扩展 API 模型

javascript - 如何在javascript中比较array1的obj和array的obj

function - fmap fmap如何应用于函数(作为参数)?

typescript - 使用 Angular2 : Unexpected anonymous System. 注册调用进行 karma 测试

java - 可以在 Java 中创建新的原始数据类型

c# - 为什么接口(interface)成员没有访问修饰符?

vb.net - 为什么 VB.NET 编译器会认为接口(interface)没有实现?