我正在try 创建一个命名空间/类/接口/类型(无论我需要哪一个,我已经研究了所有它们,似乎没有一个能够满足我的需要),它声明了一些propertiesmethods类型.但其中也有子类型,如Character.StatsCharacter.Currency.一百零五

我需要如何格式化/布局我的打字文件才能使这classes.ts个个文件成为有效的打字脚本?

// 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}`)
    }
}

目前,使用TypeScrip还能做到这一点吗?

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

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);

当前的代码运行得很好,但当前的布局似乎会在future 引发问题.由于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不允许将命名空间作为类型,但希望这就是我要实现的目标.

推荐答案

如果我理解正确的话,您希望能够go 掉.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,任何"嵌入的"类型也将被视为接口的成员.

另一方面,如果它是namespace,它不能直接用作类型本身.

你实际上可以达到both at the same time,因为namespace可以将merging转化为其他类型.

有了这个,我们宣布interface,我们用namespace using the same name宣布merge.

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:

TypeScrip提供了将构造函数参数转换为具有相同名称和值的类属性的特殊语法.它们被称为parameter properties,并且是通过在构造函数参数前面加上可见性修饰符publicprivateprotectedreadonly中的一个来创建的.

Typescript相关问答推荐

在这种情况下,如何获得类型安全函数的返回值?

如何在排版中正确键入中间件链和控制器链

Angular 行为:属性类型从SignalFunction更改为布尔

为什么tsx不判断名称中有"—"的属性?'

将对象属性转换为InstanceType或原样的强类型方法

对于合并对象中的可选属性(例如选项),类型推断可能是错误的

使用TypeScrip根据(可选)属性推断结果类型

如何解决类型T不能用于索引类型{ A:typeof A; B:typeof B;. }'

如何在ANGLE中注册自定义验证器

如何以Angular 导入其他组件S配置文件

如何获取受类型脚本泛型约束的有效输入参数

在Cypress中,是否可以在不标识错误的情况下对元素调用.Click()方法?

在TypeScript中扩展重载方法时出现问题

将类型脚本函数返回类型提取到VSCode中的接口

递归类型别名的Typescript 使用

如何使用IsEqual创建断言类型相等的实用程序

React Context TypeScript错误:属性';firstName';在类型';iCards|iSearch';

广义泛型类型不加载抽象类型上下文

使用react+ts通过props传递数据

为什么我们在条件语句中使用方括号[]?