admin管理员组

文章数量:1391783

I have a JavaScript object which dynamically allows members to be bound as accessor properties to instances of the object:

Source

function DynamicObject(obj) {
    for (var prop in obj) {
        Object.defineProperty(this, prop, {
            get: function () { return obj[prop]; },
            set: function (value) { obj[prop] = value; },
            enumerable: true,
            configurable: false
        });
    }
}

Usage

var obj = new DynamicObject({
    name: "John Smith",
    email: "[email protected]",
    id: 1
});

When obj is created, the members of the constructor parameter are bound to obj as accessor properties. These show up in intellisense

I would like to know if it is possible to model this sort of behavior (including having intellisense) in TypeScript?

Notes

When you run this code in TypeScript, there is no intellisense becuase everything is any, so TypeScript doesn't really know what's going on.

I have a JavaScript object which dynamically allows members to be bound as accessor properties to instances of the object:

Source

function DynamicObject(obj) {
    for (var prop in obj) {
        Object.defineProperty(this, prop, {
            get: function () { return obj[prop]; },
            set: function (value) { obj[prop] = value; },
            enumerable: true,
            configurable: false
        });
    }
}

Usage

var obj = new DynamicObject({
    name: "John Smith",
    email: "[email protected]",
    id: 1
});

When obj is created, the members of the constructor parameter are bound to obj as accessor properties. These show up in intellisense

I would like to know if it is possible to model this sort of behavior (including having intellisense) in TypeScript?

Notes

When you run this code in TypeScript, there is no intellisense becuase everything is any, so TypeScript doesn't really know what's going on.

Share Improve this question asked Oct 6, 2016 at 15:27 Matthew LaytonMatthew Layton 42.5k58 gold badges209 silver badges338 bronze badges 3
  • 1 I'm curious as to why you would want do this over just using a basic object literal. Just for the configurable: false? You could just use Object.freeze or Object.seal to perform similar tasks... – Heretic Monkey Commented Oct 6, 2016 at 16:35
  • @MikeMcCaughan No, the advantages here are actually in the get/set functions, not configurability. – Matthew Layton Commented Oct 6, 2016 at 16:48
  • So, your "real" code does something more interesting with the get/set? That's cool. – Heretic Monkey Commented Oct 6, 2016 at 16:52
Add a ment  | 

2 Answers 2

Reset to default 5

You can't. These are pletely dynamic properties, added at runtime, so you can't possibly know what they are at pile-time. I would also argue that you don't want to know what they are that early; if you have constraints to enforce, they should just be stated on their own (first example below).

If your code depends on a set of accessors, you should put those in the interface or contract directly, because you know ahead of time that you expect them and should advertise that. You can use optional properties (with the accessor defined lower) to make that simpler:

interface HasSomeProps {
  foo: string;
  bar?: string;
}

class DoesTheProps implements HasSomeProps {
  set foo(value) { 
    // ...
  }
}

If you have a bunch of consistent (or semi-consistent) accessors, you can define an indexer on your type like:

interface AccessStrings {
  [key: string]: string;
}

That does not allow you to restrict the keys. If you want that, you should explicitly list the properties.

I would like to know if it is possible to model this sort of behavior (including having intellisense) in TypeScript?

Yes.

You can assign a generic call signature to DynamicObject. You'll need to declare it as a variable:

var DynamicObject: new <T>(obj: T) => T = function (obj)
{
    for (var prop in obj)
    {
        Object.defineProperty(this, prop, {
            get: function () { return obj[prop]; },
            set: function (value) { obj[prop] = value; },
            enumerable: true,
            configurable: false
        });
    }
} as any;

This way, IntelliSense will treat the value returned from new DynamicObject as having the same type as the value passed in. Same property names, same property types. You'll get full autoplete and type-safety.

Live demo on TypeScript Playground


If you have trouble wrapping your head around that part in the first line, it's the same as writing the following:

// Declare type (only exists during pile-time)
var DynamicObject: new <T>(obj: T) => T;

// Assign value (during runtime)
DynamicObject = function (obj)
{
    ...
} as any;

本文标签: JavaScript to TypeScript Intellisense and dynamic membersStack Overflow