Repository navigation
Suggestion: The object primitive type #1809
Description
Activity
- changed the title
[-]Siggestion: The `object` primitive type[/-][+]Suggestion: The `object` primitive type[/+]on Jan 26, 2015 - addedIn DiscussionNot yet reached consensusNot yet reached consensusSuggestionAn idea for TypeScriptAn idea for TypeScript
on Jan 26, 2015 RyanCavanaugh commented
on Jan 26, 2015 MemberMore actionsIt'd be useful to list some APIs other than the
Object.functions which would benefit from this.fdecampredon commented
on Jan 27, 2015 AuthorMore actionsIn the javascript core api except from
Objectonly perhaps some es6 constructs will benefit from this (WeakMap, Proxy etc).
But for example everyunderscore collectionsfunction would also gain better typing, framework like React use a 'setState' method that accept only object ornull, ImmutableJS, Mori etc etc.Edit: underscore collection work with any types
- addedNeeds More InfoThe issue still hasn't been fully clarifiedThe issue still hasn't been fully clarifiedand removedIn DiscussionNot yet reached consensusNot yet reached consensus
on Apr 28, 2015 RyanCavanaugh commented
on Apr 28, 2015 MemberMore actionsDiscussed at suggestion review meeting. This all makes sense, but we need to justify the complexity of a new primitive type with the number of errors / richness of description it is able to provide. Basically, more examples of APIs that can't operate on primitives, especially ones that aren't "expert" APIs (e.g. Proxies)
RyanCavanaugh commented
on May 27, 2015 MemberMore actionsPosting more as reference than recommendation 😉
interface String { _primitiveBrand?: string; } interface Boolean { _primitiveBrand?: boolean; } interface Number { _primitiveBrand?: number; } interface ObjectOnly { _primitiveBrand?: void; } function fn(x: ObjectOnly) { } // Error fn(43); // Error fn('foo'); // OK fn({}); // OK fn(window);
+1 on this proposal. I wouldn't consider
WeakMapan "expert" API, but I do consider it reason enough alone to implement this. Since the runtime makes a distinction between object types and primitive types, it only makes sense to have a facility in TypeScript to make the distinction as well.This proposal will also solve the problem of typing "object bags" where each property is optional, but other primitive types should be disallowed.
Reacted by Ivaylo Bratoev, Claudia Meadows and Benoit BénézechObject.observerequires a non-primitive target as well: http://arv.github.io/ecmascript-object-observe/#Object.observeI think the title is a bit confusing since it mentions the word "primitive". This could alternatively be called the "ECMAScript object type" or the "Runtime object type". Which would be assignable from anything other than primitive types (including
undefinedtype and symbols), function or constructor types. The criteria for what is a legalobjecttype should be based on the following guard:let isObject (x) => typeof x === "object";
According to MDN, this is the output of
typeof:Undefined: "undefined"
Null: "object" (see below)
Boolean: "boolean"
Number: "number"
String: "string"
Symbol (new in ECMAScript 2015): "symbol"
Host object (provided by the JS environment): Implementation-dependent
Function object (implements [[Call]] in ECMA-262 terms): "function"
Any other object: "object"Without this type, there is no way to correctly match a precise type for the above mentioned guard, not even as a user-defined guard. This is also essential for use with any function that strictly requires an object that has properties and allows
for inloops and calls to theObjectprototype, and for implementing generic constraints likeT extends objectand then safely referencing properties or using the prototype functions.Having functions and constructors excluded, though, would be a bit limiting in some cases, so there could be another type that would include them as well and called the "non-primitive object type". A partial workaround could be the union
object | Functionthough casts or additional guards would be needed to handle it correctly.- addedHelp WantedYou can do thisYou can do thisand removedNeeds More InfoThe issue still hasn't been fully clarifiedThe issue still hasn't been fully clarified
on Nov 2, 2015 RyanCavanaugh commented
on Nov 3, 2015 MemberMore actionsAccepting PRs. This seems to be a common problem; this will be a breaking change for anyone brave enough to have written
interface object { ... }. Implementation should be straightforward and follows the rules outlined above.Side note: We're all wondering where François de Campredon (@fdecampredon) has been!
Reacted by Herrington Darkholme and Sean Vieira2 remaining items
- added this to the This milestone has been deleted milestone
on Jan 15, 2016 I would also like to see something like an
objecttype. However, I question whether it should conform totypeof value === 'object', as this would exclude functions. Instead, it seems to me that the prospectiveobjecttype should reflect the Object language type, which includes functions. There are a few reasons for this:- APIs that accept plain objects always, as far as I know, also accept functions.
- Functions inherit from the
Objectconstructor.
I'll take these in order.
APIs
APIs such as
WeakMapthat only take objects accept the Object language type, not just plain objects. The following is ok:function theAnswer() {} let map = new WeakMap(); map.set(theAnswer, 42); console.log(map.get(theAnswer)); // 42
This is as true for custom APIs as it is for built-in APIs. If I expect an object, I usually just want a bundle of properties. Functions are just callable bundles of properties.
Inheritance
Since
Functionextends theObjectconstructor, we can use the static and instance methods available onObjecton functions as well. For example, the following is possible:class DeepThought { static getAnswer() { return 42; } } let computer = Object.create(DeepThought); console.log(computer.getAnswer()); // 42 console.log(Object.getPrototypeOf(computer)); // [Function: DeepThought]
While the example above is a bit silly, it just illustrates that APIs that use the
Objectmethods internally could just as well accept functions.So I would suggest that the
objecttype conform to the following, which corresponds to the Object language type (except that it includesnull, but there's no guarding againstnullin TypeScript in any case).function isObject(value) { return typeof value === 'object' || typeof value === 'function'; }
Use Case
I have a project called Deep Map that recurses through the key-value pairs of nested objects and transforms primitive values along the way. As such, it distinguishes between primitive and non-primitive types. I had to define a custom
NonPrimitiveinterface, as follows:interface NonPrimitive { [key: string]: any; [index: number]: any; } export class DeepMap { // ... private mapObject(obj: NonPrimitive): NonPrimitive { /* ... */ } }
This is not the first time I've had to define the
NonPrimitiveinterface. It would be nice if I could just do this:export class DeepMap { // ... private mapObject(obj: object): object { /* ... */ } }
As I suggested above, I don't really care whether the
objparameter is callable or not. All that matters is that it is of theObjectlanguage type, i.e., that it is a bundle of properties whose key-value pairs I can iterate through.RyanCavanaugh commented
on Jun 27, 2016 MemberMore actionsI would agree that functions are
objects. The intent is to exclude primitives.Ok, I thought that must have been the intent. I just thought maybe I was missing something with all this talk of
typeof value === 'object'.would this
objecttype allow ad-hoc property assignment (likeany)? For example:const foo: object = {}; foo.bar = 'baz';
RyanCavanaugh commented
on Aug 1, 2016 MemberMore actionsNo
- added 2 commits that reference this issue
on Nov 25, 2016 - added a commit that references this issue
on Dec 22, 2016 - added a commit that references this issue
on Jan 6, 2017 What's the practical difference between
export class DeepMap { // ... private mapObject(obj: object): object { /* ... */ } }
and
export class DeepMap { // ... private mapObject(obj: Object): Object { /* ... */ } }
? (Using
objectvsObject). It seems to me that aFunctionextends fromObject, so I don't see why we can't already do that. Can you explain?@trusktr All values other than
nullandundefinedare assignable to theObjecttype; a function that accepts anObjectwill take a string, number, boolean, or symbol. Only non-primitive values are assignable toobject; passing a string, etc, will produce a compile-time error. Theobjecttype is useful where we expect a non-primitive value, but where we don't care what its properties are.Reacted by Sirius Dely and Bo Lingen- removed this from the This milestone has been deleted milestone
on Apr 26, 2018 - locked and limited conversation to collaborators
on Jul 31, 2018
The
objectprimitive typeThis proposal describe a new primitive type for typescript
object.Use case
JavaScript core api contains some functions that takes
objectas parameter :Object.getPrototypeOfObject.getOwnPropertyDescriptorObject.createCurrently there is no way in typescript to prevent passing other primitive type (
string,number, etc ...) to those functions.For example
Object.create('string')is a perfectly valid typescript statement, even if it will ends up with an error.Creating a new
objectprimitive type would allows to model more correctly the signature of those function, and every function that share similar constraint.Type Checking
Assignability
The
objecttype is the equivalent of{}minus the assignability of other basic type, that means that :objectobject{}andanyThis behavior is coherant to how javascript works, the type represent every value that respect the constraint
typeof value === 'object'plusundefined.Type guard
A new type guard has to be introduced for
object:typeof value === 'object'.Optionally the compiler could also accept comparaison with the
Objectfunction cast :Object(value) === value.