No property existence check for object type.
let foo: object;
foo.bar; // no error
The current object type is useful when used to type function arguments but not very useful on object members. See this IDL:
[SecureContext]
interface PaymentResponse {
[Default] object toJSON();
readonly attribute DOMString requestId;
readonly attribute DOMString methodName;
readonly attribute object details;
readonly attribute PaymentAddress? shippingAddress;
readonly attribute DOMString? shippingOption;
readonly attribute DOMString? payerName;
readonly attribute DOMString? payerEmail;
readonly attribute DOMString? payerPhone;
Promise<void> complete(optional PaymentComplete result = "unknown");
};
Here, the details member can be any "object" depending on payment method behavior. Current lib.d.ts defines this member as any so that a user can access any untyped members.
let response: PaymentResponse;
let { details } = response;
details.anImplementationDependentMemberName; // great
But any means it can be boolean, number, string or even null or undefined.
if (typeof details === "number") {
// `details` is resolved as `number` but really should be `never` here
}
details++; // no error, :(
A language service should be able to warn on these cases while still allowing arbitrary member accesses. Allowing such free accesses on object type will help here:
details.anImplementationDependentMemberName; // still great
if (typeof details === "number") {
// it cannot be a number, so `never` here
}
details++; // error now :D
function can also be an object in TS!Yes, but I don't think the fact will cause any problem as functions can also have arbitrary members. And I'm not proposing arbitrary function call.
Isn't this proposed type equivalent to {[k: string]: any} ?
https://www.typescriptlang.org/play/index.html#src=type%20Obj%20%3D%20%7B%20%5Bk%3A%20string%20%5D%3A%20any%20%7D%0A%0Avar%20a%3A%20Obj%20%3D%201%0Avar%20b%3A%20Obj%20%3D%20true%0Avar%20c%3A%20Obj%20%3D%20%27ss%27%0Avar%20d%3A%20Obj%20%3D%20function%20()%20%7B%20%7D%20%0Avar%20e%3A%20Obj%20%3D%20new%20Number(3)
See also #10715 and #12416
Looks like this should be covered by #10715
I think the main difference between this and the proposed unknown type is that unknown never ensures that the object is a non-primitive one, so I don't think this is a duplicate of #10715.
I do not think #10715 is about unknown. it is not clear what unknown really means. i think it is mainly about adding properties to a type by asserting them. if were to do this we would do it on object. I am assuming this is the underlying reason for this proposal, wanting to check for a JSON object properties, and assert their existence.
Asking for unchecked property access on object but not wanting to use any seems a bit unbalanced to me. details.foo++ is more or less the same as details++.
I'm not proposing adding properties by assertion, I'm rather proposing a type starting as object but doesn't have property existence check:
var o: object;
o.anything.is.possible.without.assertion;
var n: number;
n = o; // but not this, because `o` is a non-primitive object.
This basically makes object as "a union type of all possible object types", when any is a union type of all types.
so why not any? your argument about safety is compromised by making object property access unchecked.
I want to keep the type safety of the base object type, not the types of its properties.
function foo(bar: object) {
return bar;
}
// Here, you can put anything on the argument if it is a non-primitive object
const f = foo({ arbitrary1: 0, arbitrary2: 1 });
// Now f is an object with arbitrary properties, but suddenly TS warns
f.arbitrary1
//~~~~~~~~~~ Property `arbitrary1` does not exist on type `object`.
If I do any:
f.arbitrary1 // No problem
1 + f.arbitrary1; // Okay, do it
1 + f; // ??
not sure i follow..
var f: object;
f.arbitrary1 // Error: Property 'arbitrary1' does not exisit on type 'object'
1 + f.arbitrary1; // still an Error
1 + f; // Error: Operator '+' can not be applies to '1' and 'object'
this seems uniform to me.
this seems uniform to me.
Yes, but that's not a useful behavior. It just acts as an empty interface {} does.
var f: {};
f.arbitrary1 // Error: Property 'arbitrary1' does not exisit on type 'object'
1 + f.arbitrary1; // still an Error
1 + f; // Error: Operator '+' can not be applies to '1' and 'object'
My proposal here is to represent an object type that can have any properties, say a user-defined configuration object bag that is later exposed as a member.
BTW, @HerringtonDarkholme, what are the key differences between object and {[k: string]: any} when it is used on function arguments? The code in TS2.2 release note works also with the latter.
declare function create(o: {[k: string]: any} | null): void;
create({ prop: 0 }); // OK
create(null); // OK
create(42); // Error
create("string"); // Error
create(false); // Error
create(undefined); // Error
Yes, but that's not a useful behavior. It just acts as an empty interface {} does.
well, it is safe. if you want the open to all kinda object, use any.
My proposal here is to represent an object type that can have any properties, say a user-defined configuration object bag that is later exposed as a member.
how is that different from { [x:string]: any }?
how is that different from
{ [x:string]: any }?
That's my new question, what's the purpose of object type when it is for function arguments (at least #1809 and TS2.2 release note describe so) and { [x:string]: any } can be used there?
object is useful in an input position to signal something that is not a primitive. it is also useful when you have something you want to treat like a black box. { [x:string]: any} has more assumptions about the type. namelly that all its properties is of type any. it is similar to the difference between {} and any.
If one wants to use object to type functions written in TS then it makes sense, but the discussion on #1809 and the OP on #12501 show that it was to type existing JS functions including Object.create.
Why did TS team decide to introduce a new type when we can use static create(proto: { [x:string]: any }); and the resulting behavior is same?
In other words, do these functions have different behaviors?:
declare function foo(input: object): void;
declare function foo(input: { [key: string]: any }): void;
they are close, but they are not the same. object is more restrictive than { [key: string]: any } see https://github.com/Microsoft/TypeScript/issues/1809 for more details.
Still not sure, can you kindly give me an example where declare function foo(input: { [key: string]: any }): void; accepts when declare function foo(input: object): void; rejects?
for a function parameter they should behave the same way. however, a function taking { [x:string]: any } contract allows for accessing its members, where as object is more of a black box. it depends on what you want to do with them. if is it is a property bag that you expect use { [x:string]: any }, if it is an opaque object that you just want to hold on for identity use object. for constraints to ensure that you are not getting number or string, use T extends object just cause it is shorter and more descriptive.
Hmm, I see. Thank you for your explanation.
Seems like we should have a FAQ entry for the differences between {}, { [s: string]: any }, object, and any
Most helpful comment
Seems like we should have a FAQ entry for the differences between
{},{ [s: string]: any },object, andany