Is there any way to target the plain JavaScript ob

2019-06-20 13:58发布

问题:

I'm trying to write a function where I'd like to indicate that it returns some kind of plain JavaScript object. The object's signature is unknown, and not interesting for now, only the fact that it's a plain object. I mean a plain object which satisfies for example jQuery's isPlainObject function. For example

{ a: 1, b: "b" }

is a plain object, but

var obj = new MyClass();

is not a "plain" object, as its constructor is not Object. jQuery does some more precise job in $.isPlainObject, but that's out of the question's scope.

If I try to use Object type, then it will be compatible to any custom object's too, as they're inherited from Object.

Is there a way to target the "plain object" type in TypeScript?

I would like a type, which would satisfy this for example.

var obj: PlainObject = { a: 1 }; // perfect
var obj2: PlainObject = new MyClass(); // compile-error: not a plain object

Use case

I have kind of a strongly-typed stub for server-side methods, like this. These stubs are generated by one of my code generators, based on ASP.NET MVC controllers.

export class MyController {
  ...
  static GetResult(id: number): JQueryPromise<PlainObject> {
    return $.post("mycontroller/getresult", ...);
  }
  ...
}

Now when I call it in a consumer class, I can do something like this.

export class MyViewModelClass {
  ...
  LoadResult(id: number): JQueryPromise<MyControllerResult> { // note the MyControllerResult strong typing here
    return MyController.GetResult(id).then(plainResult => new MyControllerResult(plainResult));
  }
  ...
}

And now imagine that the controller method returns JQueryPromise<any> or JQueryPromise<Object>. And now also imagine that by accident I write done instead of then. Now I have a hidden error, because the viewmodel method will not return the correct promise, but I won't get a compile-error.

If I had this imaginary PlainObject type, I'd expect to get a compile error stating that PlainObject cannot be converted to MyControllerResult, or something like that.

回答1:

In my code I have something similiar to what you're asking:

export type PlainObject = { [name: string]: any }
export type PlainObjectOf<T> = { [name: string]: T }

And I also have a type guard for that:

export function isPlainObject(obj: any): obj is PlainObject {
    return obj && obj.constructor === Object || false;
}

Edit

Ok, I understand what you're looking for, but unfortunately that is not possible.
If i understand you correctly then this is what you're after:

type PlainObject = {
    constructor: ObjectConstructor;
    [name: string]: any
}

The problem is that in 'lib.d.ts' Object is defined like so:

interface Object {
    /** The initial value of Object.prototype.constructor is the standard built-in Object constructor. */
    constructor: Function;

    ...
}

And then this:

let o: PlainObject = { key: "value" };

Results with an error:

Type '{ key: string; }' is not assignable to type 'PlainObject'.
  Types of property 'constructor' are incompatible.
    Type 'Function' is not assignable to type 'ObjectConstructor'.
      Property 'getPrototypeOf' is missing in type 'Function'.