-
Notifications
You must be signed in to change notification settings - Fork 12.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RFC: Support __proto__ literal in object initializers #38385
Comments
__proto__
declaration in literal object initializers
Just ran across this and was surprised
I acknowledge the confusion that |
Also just ran accross this issue. FWIW: According to @rauschma [0]:
|
Also, the |
supported in PR #42359, behavior:
|
@sandersn asked us to post thoughts in the issue (from #48816), so I'm going to throw in my 2c. My idea is:
I see several levels at which TypeScript as a language can support
I simply don't think "we should not think about it" is an adequate way to approach this, especially when there's a PR in place. I understand it's a difficult decision to make, and I would personally go with approach (2) (as it's currently implemented), but be noted about its potential footgun. Expressing class inheritance has the same footgun in TS today, after all. |
Very concretely: As mentioned above, |
I was hesitant to call it "good", because if you actually want to work seriously with maps, you probably should use an actual |
Nah, |
I currently do: let dict: Record<string, string> = {
// the non-null assertion causes this to become `never`,
// which is assignable to a `string`:
__proto__: null!,
}; The other option is to use |
Another place where this issue is a pain: Object.defineProperty({}, 'someKey', {
__proto__: null,
configurable: true,
}); This perfectly valid JS code is flagged as invalid by TypeScript (https://www.typescriptlang.org/play?#code/PIIwVgpgxgLgdAEwgMwJYDsIAUBOB7ABwhxgE8AKAbwF8AaAAgHIBnPAWwgGkJTGHKAUPXoB9EQXww8YgFz10AVwA2S2kPpQ86NAHMFOAIYglEOTBwKIa6gEoA3AKA). |
Since this is still marked as "Awaiting More Feedback", I'll chime in with the chorus to say that this is seriously hindering my development efforts. Can we please get an update from Microsoft on this 4-year-old issue? At the very least, @RyanCavanaugh can you explain what further feedback is needed, or remove the tag? |
If this puts more weight on decision making: as MDN's JS docs writer, in recent years I've added a lot more documentation about |
It’s also safer, in that Object.create can be tampered with, but literal proto syntax can’t be. |
@RyanCavanaugh and team I hate to be a nag, but is there anything in particular about this that is still "awaiting more feedback" as the label suggests? This is a pretty straightforward suggestion that simply brings TypeScript in line with correct ECMA semantics, and there have been no concrete objections in the 4 years it's been open. This continues to promote unsafe programming patterns (creating map objects without |
I'm using such wrappers to bypass the limitation: const withProto = <Proto extends object, Base extends object>(
proto: Proto,
base: Base,
) =>
({
__proto__: proto,
...base,
}) as unknown as Proto & Base; Clearly a pain point. |
This is a new issue to specifically propose and elaborate on a feature I raised in this comment on #30587.
Search Terms
Suggestion
While accessing or mutating an existing object via the
Object.protptype.__proto__
getter/setter is deprecated, to the best of my knowledge defining the prototype of a new object via the object initializer__proto__
literal is very much encouraged.The rules for specifying the prototype of a new object via these semantics are very well-specified and safe. See the relevant section of the spec here.
Basic support
Given the following:
Typescript currently thinks that
foo
is the following shape:when in reality, it is:
TypeScript should be able to correctly detect the type of this object initialization.
Strict validity checks
Additionally, TypeScript should prevent invalid
__proto__
assignments that are "ignored" by the spec, and require all values to benull
or an object. This should fail validation:Correct handling of computed properties
It's important to note that per the spec,
__proto__
literals are not the same as regular property assignments.This object initialization, for example:
creates an object of the following shape:
Given this, I would recommend that a
__proto__
literal be forbidden in type/interface definitions, such that this is considered a syntax error:while this is an allowable way to specify a property named
__proto__
on the typefoo
.Use-Cases & Examples
This feature allows TypeScript to correctly understand the shape of objects defined with standard JS semantics. While this pattern isn't especially prevalent, it is an important feature of the language, and should be much more common in one particular use-case where TypeScript currently has a rather severe blind spot:
TypeScript currently PREVENTS the creation of safe indexed objects derived from existing indexed objects. For example:
Given this object, and the goal of "spreading" it into a new map:
The following is UNSAFE, and probably the most common approach I see people using. TypeScript should catch this, and should issue a compile-time error. See bug #37963.
The following is also UNSAFE. While using
Object.assign
andObject.create
is a perfectly valid alternative, theany
returned byObject.create
propagates through the statement and breaks type safety. (Perhaps the result ofObject.create
should beunknown
instead ofany
?)This is the SAFE way to accomplish this while using object spreads, but TypeScript currently forbids it, since it lacks support for the
__proto__
literal, and incorrectly believes a property of typenull
is being defined:Checklist
My suggestion meets these guidelines:
References
The text was updated successfully, but these errors were encountered: