Skip to content

Document mapped object types in spec #20971

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

Closed

Conversation

DanielRosenwasser
Copy link
Member

@DanielRosenwasser DanielRosenwasser commented Jan 2, 2018

Fixes #20970.

Copy link
Member

@sandersn sandersn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like a good overview. Does trying to reflect the implementation help here? Or do you drown in a sea of special cases gathered from throughout the compiler?


* The first *Type* (immediately following the `in` keyword) is the operand *K* of a mapped type.
* The second *Type* forms the *property type template* of a mapped type, *T*.
* The *Identifier* forms a type variable *P* that is scoped only in *T*, and is bound and constrained to *K*.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A syntactic form doesn’t form a semantic object. It, uh, creates it? Not sure actually. Results in?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not following on the question here

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The verb used is wrong. Syntax doesn't form semantic things like type variable, so an identifier doesn't form a type variable. You might be able to say "The identifier specifies a type variable P" or "The identifier defines a type variable P" or "The identifier references a type variable P". The last two sound best to me, but I think defines is correct here.

  *MappedObjectType:*
&emsp;&emsp;&emsp;`{`&emsp;`readonly`*<sub>opt</sub>*&emsp;`[`*Identifier*&emsp;`in`&emsp;*Type*`]`&emsp;`?`*<sub>opt</sub>*&emsp;*TypeAnnotation<sub>opt</sub>* `}`

A ***mapped object type*** is a type operator that operates on types assignable to the string type, but primarily on unions of string literal types.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would change "but primarily" to "usually"

# Mapped Object Types { #mapped-object-types }

&emsp;&emsp;*MappedObjectType:*
&emsp;&emsp;&emsp;`{`&emsp;`readonly`*<sub>opt</sub>*&emsp;`[`*Identifier*&emsp;`in`&emsp;*Type*`]`&emsp;`?`*<sub>opt</sub>*&emsp;*TypeAnnotation<sub>opt</sub>* `}`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is TypeAnnotation optional?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because it's optional :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WOW. Why do we support this? It looks like if TypeAnnotation is missing, then it's treated as if it were : any. Is that documented in this PR?

In the above syntax,

* The first *Type* (immediately following the `in` keyword) is the operand *K* of a mapped type.
* The second *Type* forms the *property type template* of a mapped type, *T*.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is marked as a type annotation in the grammar. Instead of referencing the grammar, I would give an example of the syntax first: { [P in K]: T } and then reference P, T and K for this explanation.

* The second *Type* forms the *property type template* of a mapped type, *T*.
* The *Identifier* forms a type variable *P* that is scoped only in *T*, and is bound and constrained to *K*.

Mapped object types are primarily meant to iterate over a union of string literal types, and to generate a new object type containing properties whose names are based on each string literal within that union.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You already mentioned that mapped types usually operate on string literals. I would reword to "When the operand K is a union of string literal types, mapped object types iterate over ..."

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"are based on" is vague. Maybe "properties which take their names from the value of the members of the string literal union", or a more efficient variant.


## Homomorphic Mapped Object Types { #homomorphic-mapped-object types }

A ***homomorphic mapped object type*** is a mapped type of a particular form, where the operand *K* is a type query `keyof` *O*.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

d of a particular form,


type A = {
foo?: number;
bar: number;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bar should be optional

A ***homomorphic mapped object type*** is a mapped type of a particular form, where the operand *K* is a type query `keyof` *O*.

In such instances, TypeScript will consult the type `O` when generating each property and index signature for respective modifiers.
If a modifier is not specified in the mapped type itself, but is specified for a given property in *O*, then the resulting property inherits that same modifier.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In such instances, TypeScript uses the properties and index signatures in O as the source of modifiers for the properties and index signature of the mapped type.
That is, a property p' in a mapped type has all the modifiers from the originating property p in O unless the mapped type's template specifies modifiers.

};
```

This can be powerfully combined with key query types, and indexed access types:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

combined powerfully reads better to me, but the sentence is a little weird either way.

```

As mentioned, mapped object types introduce a type variable *P*.
When *K* is not a generic type, as seen thus far, then when generating each property of a mapped object type, the type of that property is *T* with instances of *P* substituted with a string literal type whose contents are equivalent to the property name itself.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's a lot better to phrase this something like "in the scope of the template type, P is each string literal type in the union K for properties and P is K for index signatures.

@mhegazy mhegazy added the Spec Issues related to the TypeScript language specification label Jan 5, 2018
@mhegazy
Copy link
Contributor

mhegazy commented Mar 29, 2018

@DanielRosenwasser can we get these in? the spec is now stale, and the changes to update it are stale as well :)

@MartinJohns
Copy link
Contributor

@DanielRosenwasser I'd join @mhegazy question: Any progress in updating the spec? The spec is heavily outdated and all related MR seem stale.

@MartinJohns
Copy link
Contributor

@DanielRosenwasser Is there any progress on this six weeks later?

@MartinJohns
Copy link
Contributor

@DanielRosenwasser Is there any progress on this ten weeks later? :-)

@MartinJohns
Copy link
Contributor

@DanielRosenwasser Is there any progress on this PR? :-) It's been opened 44 weeks ago.

@RyanCavanaugh RyanCavanaugh deleted the spec-update-mapped-object-types branch June 16, 2022 22:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Spec Issues related to the TypeScript language specification
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants