Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Support naming tuple members#38234

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
weswigham merged 14 commits intomicrosoft:masterfromweswigham:named-tuple-members
May 19, 2020

Conversation

weswigham
Copy link
Member

@weswighamweswigham commentedApr 28, 2020
edited
Loading

Fixes#28259

This PR allows tuple members to have names like so:

typeSegment=[length:number,count:number]

The optional marker and variadic markers (? and...) are expected in the same places as the parameter lists. I already have code in place to gracefully parse a? or... in the type and issue an error. Parameter lists automatically make named tuples (in fact, there is no new checker machinery for trafficking the names for named tuples, since it was already in place for parameter lists - mostly just new parser/emitter code). Names do not affect assignability in any way, and just exist for documentation and quickinfo.

j-oliveras, brainkim, peterblazejewicz, strelga, Bnaya, dragomirtitian, acid-chicken, millsp, cherryblossom000, fuafa, and 19 more reacted with thumbs up emojibrainkim, j-oliveras, Bnaya, fuafa, streamich, fvilante, and Juninhoww2 reacted with laugh emojij-oliveras, brainkim, peterblazejewicz, Bnaya, millsp, fuafa, streamich, doumr, Bartunek, mminer, and 7 more reacted with hooray emojiikokostya, Bnaya, and streamich reacted with confused emojij-oliveras, alexrock, brainkim, peterblazejewicz, markusjohnsson, Bnaya, fuafa, JackCA, elektronik2k5, donaldpipowitch, and 8 more reacted with heart emojibrainkim, j-oliveras, AviVahl, strelga, Bnaya, streamich, AwesomeObserver, fvilante, ExE-Boss, jamesrwaugh, and bakkdoor reacted with rocket emojibrainkim, j-oliveras, Bnaya, streamich, and fvilante reacted with eyes emoji
@weswighamweswigham added the ExperimentA fork with an experimental idea which might not make it into master labelApr 28, 2020
==== tests/cases/conformance/types/tuple/named/namedTupleMembersErrors.ts (5 errors) ====
export type Segment1 = [length: number, number]; // partially named, disallowed
~~~~~~
!!! error TS5084: Tuple members must all have names or not have names.
Copy link
MemberAuthor

Choose a reason for hiding this comment

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

@sandersn@DanielRosenwasser 🚲 🏚️ - I rewrote this message like 3 times and they all sound awkward. I want to say something likeTuple member namedness must be homogeneous but that's maybe also bad.

Copy link
Member

@DanielRosenwasserDanielRosenwasserApr 28, 2020
edited
Loading

Choose a reason for hiding this comment

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

Tuple members must all be named or anonymous.

or

All members of a tuple must either be named or anonymous.

or

Named tuple members cannot be mixed with anonymous tuple members.

Choose a reason for hiding this comment

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

But you might have to play where you issue the error depending on which one you pick. Related errors can help too as a learning device for what an "anonymous" member is.

Choose a reason for hiding this comment

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

"Either all or no tuple member can be named"

or

"Naming of tuples is only supported if all members are named."

or

"Either all or no tuple members need to be named"

@@ -1273,7 +1275,15 @@ namespace ts {

export interface TupleTypeNode extends TypeNode {
kind: SyntaxKind.TupleType;
elementTypes: NodeArray<TypeNode>;
elements: NodeArray<TypeNode | NamedTupleMember>;
Copy link
MemberAuthor

Choose a reason for hiding this comment

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

In the end, I madeNamedTupleMember aTypeNode, which is inline withOptionalType andRestType, so I don'thave to rename this field (or even change the type). If anyone feels strongly about it, I can change it back. However renaming it was very useful for figuring out where I needed to adjust/handle the new node; so "breaking" the API because of the new child kind might be worthwhile for other consumers, too. Depends on how strongly we want to maintain AST compatibility I suppose.

Copy link
Member

Choose a reason for hiding this comment

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

I like the rename.

Copy link
Contributor

Choose a reason for hiding this comment

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

This rename is missing in the API breaking changes for 4.0 -https://github.com/microsoft/TypeScript/wiki/API-Breaking-Changes

@weswigham
Copy link
MemberAuthor

@typescript-bot pack this

@orta@DanielRosenwasser can either of you think of any extra language service features that these names might need hooking up to? I haven't implemented it yet, but I figure trafficking doc comments along with the names might also be useful for quick info. So, eg, when you have
non-tuple-quickinfo
if you tupleize it, you can retain the same quickinfo experience, rather than it being missing, since today it is:
tuple-quickinfo

Though admittedly, that's not directly related to this and could be a separate feature request!

hediet and McGiogen reacted with heart emoji

@typescript-bot
Copy link
Collaborator

typescript-bot commentedApr 28, 2020
edited
Loading

Heya@weswigham, I've started to run the tarball bundle task on this PR atc80c84a. You can monitor the buildhere.

@DanielRosenwasser
Copy link
Member

It'd be interesting to see how JSDoc plays into a lot of this. Not sure what tests you want, but quick info and signature help on these examples might be useful.

/** *@param args {[a: string, b: number]} the params */functionfoo(...args){let[a,b]=args;}foo()/** *@param args {[a: string, b: number]} the params */functionbar(...[a,b]){}bar()

@typescript-bot
Copy link
Collaborator

typescript-bot commentedApr 28, 2020
edited
Loading

Hey@weswigham, I've packed this intoan installable tgz. You can install it for testing by referencing it in yourpackage.json like so:

{    "devDependencies": {        "typescript": "https://typescript.visualstudio.com/cf7ac146-d525-443c-b23c-0d58337efebc/_apis/build/builds/72444/artifacts?artifactName=tgz&fileId=2D62822DA042EC2DA13BEE472478F4D5575F9C36062F7718D8375C60BB11C2B002&fileName=/typescript-4.0.0-insiders.20200428.tgz"    }}

and then runningnpm install.


There is also a playgroundfor this build.

@TimvdLippe
Copy link
Contributor

Incidentally, this also fixes a small bug do do with jsdoc variadic types and declaration emit (which is as far as I can tell, was unreported, but was reflected in our type baselines, which I noticed while I was adjusting tuple whitespace emit to support preserving comments/multiline-ness).

I tried 3.9.1-rc on DevTools today and ran into that issue:#38242 Glad to see a fix in progress!

@weswigham
Copy link
MemberAuthor

weswigham commentedMay 1, 2020
edited
Loading

From design meeting: Better tuple language service experiences across the board would be nice:

  • Element completions (that mention tuple name) eg:0 (property) first (also relevant to non-labeled tuple members)
  • Teasing apart unions of rest tuples into separate signature help overloads
  • Quickfix for grammar errors on... and? in name
  • Refactoring for extracting a parameter list as a tuple type/set of overload parameter lists as a tuple type
  • Try to preserve doc comments with the tuple on the best effort basis

@weswigham
Copy link
MemberAuthor

weswigham commentedMay 12, 2020
edited
Loading

@typescript-bot pack this

The syntax has been swapped to the parameter-like one, and I've got most of what we want in-place now (so should be usable to test). Still todo:

  • Teasing apart unions of rest tuples into separate signature help overloads
  • More tests for the refactoring
    • Better parameter comment preservation for the refactoring
  • More tests in general, just for increased coverage (codifying the behavior of recursive aliases, generics, and such, while I know it works, should probably be in a test, and it's probably worth sanity checking the assignability relationships involving tuples with labels, even though I didn't really touch the existing implementation)

@typescript-bot
Copy link
Collaborator

typescript-bot commentedMay 12, 2020
edited
Loading

Heya@weswigham, I've started to run the tarball bundle task on this PR atf0cf8d4. You can monitor the buildhere.

@typescript-bot
Copy link
Collaborator

typescript-bot commentedMay 12, 2020
edited
Loading

Hey@weswigham, I've packed this intoan installable tgz. You can install it for testing by referencing it in yourpackage.json like so:

{    "devDependencies": {        "typescript": "https://typescript.visualstudio.com/cf7ac146-d525-443c-b23c-0d58337efebc/_apis/build/builds/73632/artifacts?artifactName=tgz&fileId=6E6817DF35333947E853CF063D39FB4BE556988D7B92F809B8CD718CE706059602&fileName=/typescript-4.0.0-insiders.20200512.tgz"    }}

and then runningnpm install.


There is also a playgroundfor this build.

@weswigham
Copy link
MemberAuthor

@sandersn@ahejlsberg@rbuckton@andrewbranch reviews and feedback whenever you're ready would be appreciated now ❤️

@weswigham
Copy link
MemberAuthor

It shouldn't affect much, mostly just being a new syntax feature, but

@typescript-bot test this
@typescript-bot user test this
@typescript-bot run dt
@typescript-bot perf test this

@typescript-bot
Copy link
Collaborator

typescript-bot commentedMay 14, 2020
edited
Loading

Heya@weswigham, I've started to run the perf test suite on this PR ata7aa566. You can monitor the buildhere.

@typescript-bot
Copy link
Collaborator

typescript-bot commentedMay 14, 2020
edited
Loading

Heya@weswigham, I've started to run the parallelized Definitely Typed test suite on this PR ata7aa566. You can monitor the buildhere.

@typescript-bot
Copy link
Collaborator

typescript-bot commentedMay 14, 2020
edited
Loading

Heya@weswigham, I've started to run the extended test suite on this PR ata7aa566. You can monitor the buildhere.

@typescript-bot
Copy link
Collaborator

typescript-bot commentedMay 14, 2020
edited
Loading

Heya@weswigham, I've started to run the parallelized community code test suite on this PR ata7aa566. You can monitor the buildhere.

@typescript-bot
Copy link
Collaborator

typescript-bot commentedMay 14, 2020
edited
Loading

Hey@weswigham, I've packed this intoan installable tgz. You can install it for testing by referencing it in yourpackage.json like so:

{    "devDependencies": {        "typescript": "https://typescript.visualstudio.com/cf7ac146-d525-443c-b23c-0d58337efebc/_apis/build/builds/73966/artifacts?artifactName=tgz&fileId=31DDF907A9FA5808E09BD99C8A1AC55236B525EFE19354549F6E4ECF150B754F02&fileName=/typescript-4.0.0-insiders.20200514.tgz"    }}

and then runningnpm install.


There is also a playgroundfor this build.

@typescript-bot
Copy link
Collaborator

The user suite test run you requested has finished andfailed. I've opened aPR with the baseline diff from master.

kind: SyntaxKind.NamedTupleMember;
dotDotDotToken?: Token<SyntaxKind.DotDotDotToken>;
name: Identifier;
questionToken?: Token<SyntaxKind.QuestionToken>;
Copy link
Member

Choose a reason for hiding this comment

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

General design question: how do you decide between saving a reference to a token like this vs. saving a boolean property that indicates optionality, likeImportTypeNode['isTypeOf']? Is this token actually used anywhere?

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

In this case, mostly so the members of the same name are the same type asParameterDeclaration. In the case ofParameterDeclaration, so comments can more easily be collected on the intervening tokens (iirc). (Types don't normally care about comment preservation on intervening tokens)

andrewbranch reacted with thumbs up emoji
else if (unwrappedType.kind === SyntaxKind.RestType) {
sawRest = true;
}
unwrappedType = (unwrappedType as OptionalTypeNode | RestTypeNode | ParenthesizedTypeNode).type;
Copy link
Member

Choose a reason for hiding this comment

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

Nit / nagging question: any reason not to useisOptionalType and friends in thewhile so as to avoid this type assertion?

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Uhh, mostly just because iirc one of them doesn't exist. Yeah, I think there isn't aisRestType and mixing and matching looked odd.

andrewbranch reacted with thumbs up emoji
}
const tupleTypeNode = createTupleTypeNode(tupleConstituentNodes);
const tupleTypeNode =setEmitFlags(createTupleTypeNode(tupleConstituentNodes), EmitFlags.SingleLine);
Copy link
Member

Choose a reason for hiding this comment

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

How do you know single-line is a reasonable choice from here? It almost seems like single-line ought to remain the default for tuples, but I guess that would have meant introducing a new EmitFlag (which we’re running out of space for).

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Exactly that. Unlike object types, tuples have hisorically only been pretty printed single line, however we only have a flag for single line. So in many cases, I now detect if the input is single line and add the single line flag, so I can use it's absence as the multiline case. In this location specifically, I have no reason to change it to multiline (which is now the "default", lacking any emit flags), so it stays single line.

@@ -9520,27 +9541,36 @@ namespace ts {
return result;
}

function getExpandedParameters(sig: Signature): readonly Symbol[] {
function getExpandedParameters(sig: Signature, skipUnionExpanding?: boolean): readonly(readonlySymbol[])[] {
Copy link
Member

Choose a reason for hiding this comment

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

I understand this change on its surface, but I don’t immediately understand why it was necessary. Can you point me to the test case(s) where this matters?

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

It returns of array of arrays so it can return, to the language service, a list of "psuedooverloads" - one for each rest union tuple member. The flag exists to disable that behavior for node serialization, where we'd like to keep it as the single overload.

Copy link
Member

Choose a reason for hiding this comment

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

Got it, so this only comes into play in signature help (and possibly quick info or whatever)?

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Yep.

@weswigham
Copy link
MemberAuthor

weswigham commentedMay 14, 2020
edited
Loading

Fixed a crash the user suite found (a parameter without name is apparently a thing which can happen, even though our types disallow it?), the rest of the user suite looks pretty good - just named tuples in error messages. RWC diff is just a tuple in declaration emit being multiline (since now we can preserve that). DT diff is currently filled with failures due to dropping 2.9 support, but the few failures not related to that are places where tuples acquired named and thus no longer matchExpectType calls, so are also good (though should probably be preemptively updated to accept the new labeled printback, once we can get a full list).

Perf test died early with a crash innpm itself... looks like it's running an old version ofnpm.@rbuckton is it safe to update the npm version in use on the perf testing machine (and if so, would you be able to)? It's on 6.7.0, while hosted workers are working fine on 6.14.4.

@weswigham
Copy link
MemberAuthor

@typescript-bot run dt again since the 2.9 thing should be fixed now

@typescript-bot
Copy link
Collaborator

typescript-bot commentedMay 15, 2020
edited
Loading

Heya@weswigham, I've started to run the parallelized Definitely Typed test suite on this PR ate3a1cb1. You can monitor the buildhere.

Copy link
Member

@ahejlsbergahejlsberg left a comment

Choose a reason for hiding this comment

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

Looks good. The one thing I wonder about is whether it is better to omit tuple element names in certain error cases (see my review comment). I worry we'll confuse people between theactual names ('0','1', etc.) and the element names.

function getTupleTypeOfArity(arity: number, minLength: number, hasRestElement: boolean, readonly: boolean,associatedNames?:__String[]): GenericType {
const key = arity + (hasRestElement ? "+" : ",") + minLength + (readonly ? "R" : "") + (associatedNames &&associatedNames.length ? "," +associatedNames.join(",") : "");
function getTupleTypeOfArity(arity: number, minLength: number, hasRestElement: boolean, readonly: boolean,namedMemberDeclarations?:readonly (NamedTupleMember | ParameterDeclaration)[]): GenericType {
const key = arity + (hasRestElement ? "+" : ",") + minLength + (readonly ? "R" : "") + (namedMemberDeclarations &&namedMemberDeclarations.length ? "," +map(namedMemberDeclarations, getNodeId).join(",") : "");
Copy link
Member

Choose a reason for hiding this comment

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

I like using node IDs here, though it will increase the number of unique tuple target types we generate.

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Yeah. Using node id here brings tuples in line with normal anonymous object types. It gets us good name and documentation preservation, though!

@@ -13531,7 +13573,7 @@ namespace ts {

function getAliasSymbolForTypeNode(node: Node) {
let host = node.parent;
while (isParenthesizedTypeNode(host) || isTypeOperatorNode(host) && host.operator === SyntaxKind.ReadonlyKeyword) {
while (isParenthesizedTypeNode(host) ||host.kind === SyntaxKind.NamedTupleMember ||isTypeOperatorNode(host) && host.operator === SyntaxKind.ReadonlyKeyword) {
Copy link
Member

Choose a reason for hiding this comment

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

Can't think of when a named tuple member would occur in the parent chain?

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

This is mostly a leftover from an initial stage where I considered labels a kind of parenthesized declaration. I must have missed removing this in the cleanup.

@@ -25117,19 +25176,23 @@ namespace ts {
}
}
const types = [];
const names: (ParameterDeclaration | NamedTupleMember)[] = [];
Copy link
Member

Choose a reason for hiding this comment

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

What's the scenario for capturing names here?

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

When you concatenate one tuple to another via spread, this allows us to concatenate the name list, too.

for (let i = pos; i < nonRestCount; i++) {
types.push(getTypeAtPosition(source, i));
names.push(getParameterNameAtPosition(source, i));
const name = getNameableDeclarationAtPosition(source, i);
Copy link
Member

Choose a reason for hiding this comment

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

So previously we'd always collect names (even made uparg_xxx names), but now we only collect names with an actual declaration associated?

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Correct! That does mean that generated names can still be shoved into parameter lists (likearg_0) but will disappear when copied into a tuple. I actually think this is a good thing, since the generated names were just positional anyway.

seenNamedElement = true;
}
else if (seenNamedElement) {
grammarErrorOnNode(e, Diagnostics.Tuple_members_must_all_have_names_or_not_have_names);
Copy link
Member

Choose a reason for hiding this comment

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

How about "Tuple members must all have names or all not have names". As it is now, you can read it as all members must have a name or not, which obviously is always true.

weswigham reacted with thumbs up emojiandrewbranch reacted with laugh emoji
@@ -1273,7 +1275,15 @@ namespace ts {

export interface TupleTypeNode extends TypeNode {
kind: SyntaxKind.TupleType;
elementTypes: NodeArray<TypeNode>;
elements: NodeArray<TypeNode | NamedTupleMember>;
Copy link
Member

Choose a reason for hiding this comment

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

I like the rename.

Type '[string] | [number, boolean]' is not assignable to type '[number, boolean]'.
Property '1' is missing in type '[string]' but required in type '[number, boolean]'.
Type '[string] | [number, boolean]' is not assignable to type '[y:number, z: boolean]'.
Property '1' is missing in type '[string]' but required in type '[y:number, z: boolean]'.
Copy link
Member

Choose a reason for hiding this comment

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

Hmm, theactual property name is'1', but it sure looks like it's name isz. Wonder if it is better to omit the names in some cases?

Copy link
MemberAuthor

Choose a reason for hiding this comment

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

Eh... I think it'd be kinda odd to sometimes display the same type with labels and sometimes without. Plus, this error is somewhat rare; I believe we usually try to report tuple length errors as an arity mismatch (ie, on length), but fail to do so here because of the union.

Choose a reason for hiding this comment

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

How about

Type '[string] | [number, boolean]' is not assignable to type '[y: number, z: boolean]'.      Property '1' (labeled as 'z') is missing in type '[string]' but required in type '[y: number, z: boolean]'.

Sorry for the late question, I was super excited for this feature as I want to use it now (and get the benefit in future as Angular updates it's TS dependency)

tills13 reacted with thumbs up emoji
@weswigham
Copy link
MemberAuthor

@typescript-bot run dt

@typescript-bot
Copy link
Collaborator

typescript-bot commentedMay 19, 2020
edited
Loading

Heya@weswigham, I've started to run the parallelized Definitely Typed test suite on this PR at05d398e. You can monitor the buildhere.

@weswighamweswigham merged commit5f597e6 intomicrosoft:masterMay 19, 2020
cangSDARM added a commit to cangSDARM/TypeScript that referenced this pull requestMay 20, 2020
* upstream/master:  Support naming tuple members (microsoft#38234)  LEGO: check in for master to temporary branch.  fix: extract const in jsx (microsoft#37912)  No contextual types from circular mapped type properties (microsoft#38653)  Ensure formatter can always get a newline character (microsoft#38579)  Fix debug command for Node debugging  Remove mentions of runtests-browser in CONTRIBUTING.md  fix(33233): add outlining for comments before property access expression  regression(38485): allow using rawText property in processing a tagged template
sheetalkamat added a commit to microsoft/TypeScript-TmLanguage that referenced this pull requestMay 21, 2020
@ftzi
Copy link

ftzi commentedSep 24, 2020
edited
Loading

This new feature looks like is just what I needed right now. But, I do not know how to connect the dots:

In summary, I have a readonly array of const strings, likereadonly ['aaa', 'bbb', 'ccc']. I can't find a way (if there is one) to transform this into a generic variadic function parameter type, like[aaa: string | number, bbb: string | number, ccc: string | number].

JulianXander reacted with thumbs up emoji

@Gerrit0
Copy link
Contributor

There is no such functionality in this PR (or, as far as I know, planned). Tuple member names are purely documentation... they don't interact with the rest of the type system in any way.

andrewbranch and cowwoc reacted with thumbs up emojiftzi reacted with confused emoji

@ftzi
Copy link

Hmm, that's bad. Would be a really cool feature to allow "dynamic functions"

andreterron, JulianXander, MathisBullinger, melody-universe, majg0, and Vanilagy reacted with thumbs up emoji

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers

@martinheideggermartinheideggermartinheidegger left review comments

@AkxeAkxeAkxe left review comments

@andrewbranchandrewbranchandrewbranch approved these changes

@Gerrit0Gerrit0Gerrit0 left review comments

@ahejlsbergahejlsbergahejlsberg approved these changes

@sandersnsandersnAwaiting requested review from sandersn

@rbucktonrbucktonAwaiting requested review from rbuckton

@DanielRosenwasserDanielRosenwasserAwaiting requested review from DanielRosenwasser

Assignees

@weswighamweswigham

Projects
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

Feature Request: Add labels to tuple elements
10 participants
@weswigham@typescript-bot@DanielRosenwasser@TimvdLippe@ftzi@Gerrit0@martinheidegger@Akxe@andrewbranch@ahejlsberg

[8]ページ先頭

©2009-2025 Movatter.jp