动态 TypeScript 类型定义

7
有没有办法在TypeScript类中拥有动态对象属性,并为TypeScript添加动态类型?我看到类似的问题,但是没有像这样完整的示例。
interface IHasObjectName {
   objectName: string;
}

class example<A extends IHasObjectName, B  extends IHasObjectName> {

    constructor(a: A, b: B) {
        this[a.objectName] = function() { return a; };
        this[b.objectName] = function() { return b; }
    }
}

class Cat implements IHasObjectName {
    objectName: string = "";
}

class Dog implements IHasObjectName {
    objectName: string = "";
}

let cat = new Cat();
cat.objectName = "Cat";

let dog = new Dog();
dog.objectName = "Dog";

let test = new example<Cat,Dog>(cat, dog);

// ??? TYPESCRIPT DOESN'T KNOW ABOUT THESE DYNAMIC PROPERTIES
// HOW DO I MAKE THIS WORK?
let d = test.Dog();
let c = test.Cat();

// I know I could access like this 
// let d = test["Dog"](); 
// but I want to access like function and have it typed

我有一种感觉,我们在这里缺少了您真正的用例。这段代码是否真正代表了您想要做的事情?您是只是在这个示例中使用动态对象属性作为实现您真正想要的方式,还是动态属性真正是您的主要兴趣所在? - Alex
你可以只使用JavaScript。 - undefined
2个回答

11

您可以使用工厂函数和交叉类型

function factory<A extends IHasObjectName, B extends IHasObjectName, C>(a: A, b: B): example<A, B> & C {
    return new example<Cat, Dog>(a, b) as C;
}
var test = factory<Cat, Dog, { Dog(): Dog, Cat(): Cat }>(cat, dog);

var d = test.Dog(); // no error
var c = test.Cat(); // no error

(在 playground 中查看代码)


编辑

你不能在运行时“反射”类型,因为它们不存在于运行时,但是你可以使用传入实例的constructor.name,因此你可以简单地这样做:

class example<A, B> {
    constructor(a: A, b: B) {
        this[a.constructor.name] = function() { return a; };
        this[b.constructor.name] = function() { return b; }
    }
}

class Cat {}

class Dog {}

var cat = new Cat();
var dog = new Dog();


function factory<A, B, C>(a: A, b: B): example<A, B> & C {
    return new example<Cat, Dog>(a, b) as C;
}
var test = factory<Cat, Dog, { Dog(): Dog, Cat(): Cat }>(cat, dog);

var d = test.Dog();
var c = test.Cat();

(在 playground 中查看代码)


2
在工厂中,使用以下参数:return new example<Cat, Dog>(a, b) as C; ;) - Paleo
那真是太酷了,我在这里学到了东西。但我希望可以避免工厂用户在调用工厂时基本上需要键入方法(接口类型“C”)。我的目标只是让test.Dog像魔术一样出现。也许不可能实现。 - user210757
@user210757 不可能的。您正在运行时更改实例,编译器在编译时无法知道这一点。 - Nitzan Tomer
@user210757 有道理。 - Nitzan Tomer
我能以某种方式“反射”接口C并使用该信息创建示例中的方法,而不是使用属性“objectName”吗? - user210757
显示剩余9条评论

0

如果你希望在 TypeScript 中实现 "JavaScript 行为",你需要将其转换为 any 对象类型。

有两种等效的语法类型:

const d = (<any>test).Dog();
const c = (<any>test).Cat();

并且

const testAny = test as any;
const d = testAny.Dog();
const c = testAny.Cat();

最后一个是为tsx文件中的支持而创建的,其中wouldn't不起作用,现在是推荐的方法。

除了使用索引器外,几乎没有其他方法来做到这一点,因为属性是动态的而且没有类型。

顺便说一句,我鼓励使用constlet代替var


4
谢谢,这基本上消除了我键入的益处。还有智能感知。 - user210757

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接