admin管理员组

文章数量:1221288

Is there any way to have dynamic object properties in a TypeScript class, and add dynamic Typings in for TypeScript?

I have seen similar questions but none with a complete example like this -

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

Is there any way to have dynamic object properties in a TypeScript class, and add dynamic Typings in for TypeScript?

I have seen similar questions but none with a complete example like this -

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
Share Improve this question edited Dec 1, 2016 at 16:35 user210757 asked Dec 1, 2016 at 15:38 user210757user210757 7,38618 gold badges74 silver badges121 bronze badges 2
  • I have a feeling that we are missing your real use-case here. Is this code really representative for what you want to do? Are you just using dynamic object properties in this example as a way to achieve what you really want, or is dynamic properties really your main interest here? – Alex Commented Dec 1, 2016 at 16:42
  • You could just use Javascript – Brian Ogden Commented Nov 28, 2023 at 17:09
Add a comment  | 

2 Answers 2

Reset to default 11

You can use a factory function and intersection:

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

(code in playground)


Edit

You can't "reflect" types because they don't exist in runtime, but you can use the constructor.name of the passed in instances, so you can simply do this:

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

(code in playground)

You need to cast it to any object type if you wan't "JavaScript behavior" in TypeScript.

There are two syntax types, which are equivalent:

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

and

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

the last one was created for support in tsx files, where wouldn't work and is now the recommended way.

There is hardly an other way to do that other than use the indexer, since the properties are dynamic and not typed.

BTW I encourage to use const and let instead of var.

本文标签: javascriptDynamic TypeScript TypingStack Overflow