Skip to content

Commit c7e115a

Browse files
committed
Swap allowed syntax to parameter-like
1 parent 4c50574 commit c7e115a

25 files changed

+190
-85
lines changed

src/compiler/checker.ts

Lines changed: 52 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4563,14 +4563,24 @@ namespace ts {
45634563
const tupleConstituentNodes = mapToTypeNodes(typeArguments.slice(0, arity), context);
45644564
const hasRestElement = (<TupleType>type.target).hasRestElement;
45654565
if (tupleConstituentNodes) {
4566-
for (let i = (<TupleType>type.target).minLength; i < Math.min(arity, tupleConstituentNodes.length); i++) {
4567-
tupleConstituentNodes[i] = hasRestElement && i === arity - 1 ?
4568-
createRestTypeNode(createArrayTypeNode(tupleConstituentNodes[i])) :
4569-
createOptionalTypeNode(tupleConstituentNodes[i]);
4570-
}
45714566
if ((type.target as TupleType).labeledElementDeclarations) {
45724567
for (let i = 0; i < tupleConstituentNodes.length; i++) {
4573-
tupleConstituentNodes[i] = createNamedTupleMember(createIdentifier(unescapeLeadingUnderscores(getTupleElementLabel((type.target as TupleType).labeledElementDeclarations![i]))), tupleConstituentNodes[i]);
4568+
const isOptionalOrRest = i >= (<TupleType>type.target).minLength;
4569+
const isRest = isOptionalOrRest && hasRestElement && i === arity - 1;
4570+
const isOptional = isOptionalOrRest && !isRest;
4571+
tupleConstituentNodes[i] = createNamedTupleMember(
4572+
isRest ? createToken(SyntaxKind.DotDotDotToken) : undefined,
4573+
createIdentifier(unescapeLeadingUnderscores(getTupleElementLabel((type.target as TupleType).labeledElementDeclarations![i]))),
4574+
isOptional ? createToken(SyntaxKind.QuestionToken) : undefined,
4575+
isRest ? createArrayTypeNode(tupleConstituentNodes[i]) : tupleConstituentNodes[i]
4576+
);
4577+
}
4578+
}
4579+
else {
4580+
for (let i = (<TupleType>type.target).minLength; i < Math.min(arity, tupleConstituentNodes.length); i++) {
4581+
tupleConstituentNodes[i] = hasRestElement && i === arity - 1 ?
4582+
createRestTypeNode(createArrayTypeNode(tupleConstituentNodes[i])) :
4583+
createOptionalTypeNode(tupleConstituentNodes[i]);
45744584
}
45754585
}
45764586
const tupleTypeNode = setEmitFlags(createTupleTypeNode(tupleConstituentNodes), EmitFlags.SingleLine);
@@ -12092,18 +12102,22 @@ namespace ts {
1209212102
return createTypeFromGenericGlobalType(readonly ? globalReadonlyArrayType : globalArrayType, [elementType]);
1209312103
}
1209412104

12095-
function unwrapNamedTupleMember(node: TypeNode): TypeNode {
12096-
return node.kind === SyntaxKind.NamedTupleMember ? (node as NamedTupleMember).type : node;
12105+
function isTupleRestElement(node: TypeNode) {
12106+
return node.kind === SyntaxKind.RestType || (node.kind === SyntaxKind.NamedTupleMember && !!(node as NamedTupleMember).dotDotDotToken);
12107+
}
12108+
12109+
function isTupleOptionalElement(node: TypeNode) {
12110+
return node.kind === SyntaxKind.OptionalType || (node.kind === SyntaxKind.NamedTupleMember && !!(node as NamedTupleMember).questionToken);
1209712111
}
1209812112

1209912113
function getArrayOrTupleTargetType(node: ArrayTypeNode | TupleTypeNode): GenericType {
1210012114
const readonly = isReadonlyTypeOperator(node.parent);
12101-
if (node.kind === SyntaxKind.ArrayType || node.elements.length === 1 && node.elements[0].kind === SyntaxKind.RestType) {
12115+
if (node.kind === SyntaxKind.ArrayType || node.elements.length === 1 && isTupleRestElement(node.elements[0])) {
1210212116
return readonly ? globalReadonlyArrayType : globalArrayType;
1210312117
}
1210412118
const lastElement = lastOrUndefined(node.elements);
12105-
const restElement = lastElement && unwrapNamedTupleMember(lastElement).kind === SyntaxKind.RestType ? lastElement : undefined;
12106-
const minLength = findLastIndex(node.elements, n => unwrapNamedTupleMember(n).kind !== SyntaxKind.OptionalType && n !== restElement) + 1;
12119+
const restElement = lastElement && isTupleRestElement(lastElement) ? lastElement : undefined;
12120+
const minLength = findLastIndex(node.elements, n => !isTupleOptionalElement(n) && n !== restElement) + 1;
1210712121
const missingName = some(node.elements, e => e.kind !== SyntaxKind.NamedTupleMember);
1210812122
return getTupleTypeOfArity(node.elements.length, minLength, !!restElement, readonly, /*associatedNames*/ missingName ? undefined : node.elements as readonly NamedTupleMember[]);
1210912123
}
@@ -13855,6 +13869,21 @@ namespace ts {
1385513869
return links.resolvedType;
1385613870
}
1385713871

13872+
function getTypeFromNamedTupleTypeNode(node: NamedTupleMember): Type {
13873+
const links = getNodeLinks(node);
13874+
if (!links.resolvedType) {
13875+
let type = getTypeFromTypeNode(node.type);
13876+
if (node.dotDotDotToken) {
13877+
type = getElementTypeOfArrayType(type) || errorType;
13878+
}
13879+
if (node.questionToken && strictNullChecks) {
13880+
type = getOptionalType(type);
13881+
}
13882+
links.resolvedType = type;
13883+
}
13884+
return links.resolvedType;
13885+
}
13886+
1385813887
function getTypeFromTypeNode(node: TypeNode): Type {
1385913888
return getConditionalFlowTypeOfType(getTypeFromTypeNodeWorker(node), node);
1386013889
}
@@ -13913,8 +13942,9 @@ namespace ts {
1391313942
return getTypeFromJSDocNullableTypeNode(<JSDocNullableType>node);
1391413943
case SyntaxKind.JSDocOptionalType:
1391513944
return addOptionality(getTypeFromTypeNode((node as JSDocOptionalType).type));
13916-
case SyntaxKind.ParenthesizedType:
1391713945
case SyntaxKind.NamedTupleMember:
13946+
return getTypeFromNamedTupleTypeNode(node as NamedTupleMember);
13947+
case SyntaxKind.ParenthesizedType:
1391813948
case SyntaxKind.JSDocNonNullableType:
1391913949
case SyntaxKind.JSDocTypeExpression:
1392013950
return getTypeFromTypeNode((<ParenthesizedTypeNode | JSDocTypeReferencingNode | JSDocTypeExpression | NamedTupleMember>node).type);
@@ -30142,16 +30172,15 @@ namespace ts {
3014230172
let seenOptionalElement = false;
3014330173
let seenNamedElement = false;
3014430174
for (let i = 0; i < elementTypes.length; i++) {
30145-
let e = elementTypes[i];
30175+
const e = elementTypes[i];
3014630176
if (e.kind === SyntaxKind.NamedTupleMember) {
3014730177
seenNamedElement = true;
30148-
e = (e as NamedTupleMember).type;
3014930178
}
3015030179
else if (seenNamedElement) {
3015130180
grammarErrorOnNode(e, Diagnostics.Tuple_members_must_all_have_names_or_not_have_names);
3015230181
break;
3015330182
}
30154-
if (e.kind === SyntaxKind.RestType) {
30183+
if (isTupleRestElement(e)) {
3015530184
if (i !== elementTypes.length - 1) {
3015630185
grammarErrorOnNode(e, Diagnostics.A_rest_element_must_be_last_in_a_tuple_type);
3015730186
break;
@@ -30160,7 +30189,7 @@ namespace ts {
3016030189
error(e, Diagnostics.A_rest_element_type_must_be_an_array_type);
3016130190
}
3016230191
}
30163-
else if (e.kind === SyntaxKind.OptionalType) {
30192+
else if (isTupleOptionalElement(e)) {
3016430193
seenOptionalElement = true;
3016530194
}
3016630195
else if (seenOptionalElement) {
@@ -30255,11 +30284,14 @@ namespace ts {
3025530284
}
3025630285

3025730286
function checkNamedTupleMember(node: NamedTupleMember) {
30258-
if (node.dotDotDotToken) {
30259-
grammarErrorOnNode(node, Diagnostics.Rest_tuple_members_are_declared_with_a_on_the_type_A_on_the_name_is_invalid);
30287+
if (node.dotDotDotToken && node.questionToken) {
30288+
grammarErrorOnNode(node, Diagnostics.A_tuple_member_cannot_be_both_optional_and_rest);
30289+
}
30290+
if (node.type.kind === SyntaxKind.OptionalType) {
30291+
grammarErrorOnNode(node.type, Diagnostics.A_labeled_tuple_element_is_declared_as_optional_with_a_question_mark_after_the_name_and_before_the_colon_rather_than_after_the_type);
3026030292
}
30261-
if (node.questionToken) {
30262-
grammarErrorOnNode(node, Diagnostics.Tuple_members_express_optionality_with_a_trailing_question_mark_on_the_type_a_question_mark_on_the_member_name_is_invalid);
30293+
if (node.type.kind === SyntaxKind.RestType) {
30294+
grammarErrorOnNode(node.type, Diagnostics.A_labeled_tuple_element_is_declared_as_rest_with_a_before_the_name_rather_than_before_the_type);
3026330295
}
3026430296
checkSourceElement(node.type);
3026530297
getTypeFromTypeNode(node);

src/compiler/diagnosticMessages.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3521,14 +3521,18 @@
35213521
"category": "Error",
35223522
"code": 5084
35233523
},
3524-
"Tuple members express optionality with a trailing question mark on the type, a question mark on the member name is invalid.": {
3524+
"A tuple member cannot be both optional and rest.": {
35253525
"category": "Error",
35263526
"code": 5085
35273527
},
3528-
"Rest tuple members are declared with a `...` on the type. A `...` on the name is invalid.": {
3528+
"A labeled tuple element is declared as optional with a question mark after the name and before the colon, rather than after the type.": {
35293529
"category": "Error",
35303530
"code": 5086
35313531
},
3532+
"A labeled tuple element is declared as rest with a `...` before the name, rather than before the type.": {
3533+
"category": "Error",
3534+
"code": 5087
3535+
},
35323536

35333537
"Generates a sourcemap for each corresponding '.d.ts' file.": {
35343538
"category": "Message",

src/compiler/emitter.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2108,7 +2108,9 @@ namespace ts {
21082108
}
21092109

21102110
function emitNamedTupleMember(node: NamedTupleMember) {
2111+
emit(node.dotDotDotToken);
21112112
emit(node.name);
2113+
emit(node.questionToken);
21122114
emitTokenWithComment(SyntaxKind.ColonToken, node.name.end, writePunctuation, node);
21132115
writeSpace();
21142116
emit(node.type);

src/compiler/factoryPublic.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -934,17 +934,21 @@ namespace ts {
934934
: node;
935935
}
936936

937-
export function createNamedTupleMember(name: Identifier, type: TypeNode) {
937+
export function createNamedTupleMember(dotDotDotToken: Token<SyntaxKind.DotDotDotToken> | undefined, name: Identifier, questionToken: Token<SyntaxKind.QuestionToken> | undefined, type: TypeNode) {
938938
const node = <NamedTupleMember>createSynthesizedNode(SyntaxKind.NamedTupleMember);
939+
node.dotDotDotToken = dotDotDotToken;
939940
node.name = name;
941+
node.questionToken = questionToken;
940942
node.type = type;
941943
return node;
942944
}
943945

944-
export function updateNamedTupleMember(node: NamedTupleMember, name: Identifier, type: TypeNode) {
945-
return node.name !== name
946+
export function updateNamedTupleMember(node: NamedTupleMember, dotDotDotToken: Token<SyntaxKind.DotDotDotToken> | undefined, name: Identifier, questionToken: Token<SyntaxKind.QuestionToken> | undefined, type: TypeNode) {
947+
return node.dotDotDotToken !== dotDotDotToken
948+
|| node.name !== name
949+
|| node.questionToken !== questionToken
946950
|| node.type !== type
947-
? updateNode(createNamedTupleMember(name, type), node)
951+
? updateNode(createNamedTupleMember(dotDotDotToken, name, questionToken, type), node)
948952
: node;
949953
}
950954

src/compiler/utilities.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2702,10 +2702,7 @@ namespace ts {
27022702
}
27032703

27042704
export function walkUpParenthesizedTypes(node: Node) {
2705-
while (node && (node.kind === SyntaxKind.ParenthesizedType || node.kind === SyntaxKind.NamedTupleMember)) {
2706-
node = node.parent;
2707-
}
2708-
return node;
2705+
return walkUp(node, SyntaxKind.ParenthesizedType);
27092706
}
27102707

27112708
export function walkUpParenthesizedExpressions(node: Node) {

src/compiler/visitorPublic.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,9 @@ namespace ts {
519519

520520
case SyntaxKind.NamedTupleMember:
521521
return updateNamedTupleMember(<NamedTupleMember>node,
522+
visitNode((<NamedTupleMember>node).dotDotDotToken, visitor, isToken),
522523
visitNode((<NamedTupleMember>node).name, visitor, isIdentifierName),
524+
visitNode((<NamedTupleMember>node).questionToken, visitor, isToken),
523525
visitNode((<NamedTupleMember>node).type, visitor, isTypeNode),
524526
);
525527

tests/baselines/reference/api/tsserverlibrary.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4080,8 +4080,8 @@ declare namespace ts {
40804080
function updateImportTypeNode(node: ImportTypeNode, argument: TypeNode, qualifier?: EntityName, typeArguments?: readonly TypeNode[], isTypeOf?: boolean): ImportTypeNode;
40814081
function createParenthesizedType(type: TypeNode): ParenthesizedTypeNode;
40824082
function updateParenthesizedType(node: ParenthesizedTypeNode, type: TypeNode): ParenthesizedTypeNode;
4083-
function createNamedTupleMember(name: Identifier, type: TypeNode): NamedTupleMember;
4084-
function updateNamedTupleMember(node: NamedTupleMember, name: Identifier, type: TypeNode): NamedTupleMember;
4083+
function createNamedTupleMember(dotDotDotToken: Token<SyntaxKind.DotDotDotToken> | undefined, name: Identifier, questionToken: Token<SyntaxKind.QuestionToken> | undefined, type: TypeNode): NamedTupleMember;
4084+
function updateNamedTupleMember(node: NamedTupleMember, dotDotDotToken: Token<SyntaxKind.DotDotDotToken> | undefined, name: Identifier, questionToken: Token<SyntaxKind.QuestionToken> | undefined, type: TypeNode): NamedTupleMember;
40854085
function createThisTypeNode(): ThisTypeNode;
40864086
function createTypeOperatorNode(type: TypeNode): TypeOperatorNode;
40874087
function createTypeOperatorNode(operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword, type: TypeNode): TypeOperatorNode;

tests/baselines/reference/api/typescript.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4080,8 +4080,8 @@ declare namespace ts {
40804080
function updateImportTypeNode(node: ImportTypeNode, argument: TypeNode, qualifier?: EntityName, typeArguments?: readonly TypeNode[], isTypeOf?: boolean): ImportTypeNode;
40814081
function createParenthesizedType(type: TypeNode): ParenthesizedTypeNode;
40824082
function updateParenthesizedType(node: ParenthesizedTypeNode, type: TypeNode): ParenthesizedTypeNode;
4083-
function createNamedTupleMember(name: Identifier, type: TypeNode): NamedTupleMember;
4084-
function updateNamedTupleMember(node: NamedTupleMember, name: Identifier, type: TypeNode): NamedTupleMember;
4083+
function createNamedTupleMember(dotDotDotToken: Token<SyntaxKind.DotDotDotToken> | undefined, name: Identifier, questionToken: Token<SyntaxKind.QuestionToken> | undefined, type: TypeNode): NamedTupleMember;
4084+
function updateNamedTupleMember(node: NamedTupleMember, dotDotDotToken: Token<SyntaxKind.DotDotDotToken> | undefined, name: Identifier, questionToken: Token<SyntaxKind.QuestionToken> | undefined, type: TypeNode): NamedTupleMember;
40854085
function createThisTypeNode(): ThisTypeNode;
40864086
function createTypeOperatorNode(type: TypeNode): TypeOperatorNode;
40874087
function createTypeOperatorNode(operator: SyntaxKind.KeyOfKeyword | SyntaxKind.UniqueKeyword | SyntaxKind.ReadonlyKeyword, type: TypeNode): TypeOperatorNode;

tests/baselines/reference/genericRestParameters2.types

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ f20(42, "hello", ...t2, true);
417417
>true : true
418418

419419
type T01 = Parameters<(x: number, y: string, ...z: boolean[]) => void>;
420-
>T01 : [x: number, y: string, z: ...boolean[]]
420+
>T01 : [x: number, y: string, ...z: boolean[]]
421421
>x : number
422422
>y : string
423423
>z : boolean[]
@@ -427,7 +427,7 @@ type T02 = Parameters<(...args: [number, string, ...boolean[]]) => void>;
427427
>args : [number, string, ...boolean[]]
428428

429429
type T03 = ConstructorParameters<new (x: number, y: string, ...z: boolean[]) => void>;
430-
>T03 : [x: number, y: string, z: ...boolean[]]
430+
>T03 : [x: number, y: string, ...z: boolean[]]
431431
>x : number
432432
>y : string
433433
>z : boolean[]
@@ -452,7 +452,7 @@ type P1<T extends Function> = T extends (head: infer A, ...tail: infer B) => any
452452
>tail : B
453453

454454
type T10 = P1<(x: number, y: string, ...z: boolean[]) => void>;
455-
>T10 : { head: number; tail: [y: string, z: ...boolean[]]; }
455+
>T10 : { head: number; tail: [y: string, ...z: boolean[]]; }
456456
>x : number
457457
>y : string
458458
>z : boolean[]

tests/baselines/reference/genericRestTypes.errors.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
tests/cases/compiler/genericRestTypes.ts(21,11): error TS2322: Type '(cb: (x: string, ...rest: T) => void) => void' is not assignable to type '(cb: (...args: never) => void) => void'.
22
Types of parameters 'cb' and 'cb' are incompatible.
33
Types of parameters 'args' and 'x' are incompatible.
4-
Type '[x: string, rest: ...T[number][]]' is not assignable to type 'never'.
4+
Type '[x: string, ...rest: T[number][]]' is not assignable to type 'never'.
55

66

77
==== tests/cases/compiler/genericRestTypes.ts (1 errors) ====
@@ -30,7 +30,7 @@ tests/cases/compiler/genericRestTypes.ts(21,11): error TS2322: Type '(cb: (x: st
3030
!!! error TS2322: Type '(cb: (x: string, ...rest: T) => void) => void' is not assignable to type '(cb: (...args: never) => void) => void'.
3131
!!! error TS2322: Types of parameters 'cb' and 'cb' are incompatible.
3232
!!! error TS2322: Types of parameters 'args' and 'x' are incompatible.
33-
!!! error TS2322: Type '[x: string, rest: ...T[number][]]' is not assignable to type 'never'.
33+
!!! error TS2322: Type '[x: string, ...rest: T[number][]]' is not assignable to type 'never'.
3434
}
3535

3636
function assignmentWithComplexRest3<T extends any[]>() {

0 commit comments

Comments
 (0)