Allowed tuples to have both named and anonymous members (#53356)

Co-authored-by: Jake Bailey <jabaile@microsoft.com>
Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com>
Co-authored-by: Daniel Rosenwasser <drosenwasser@microsoft.com>
This commit is contained in:
Josh Goldberg ✨
2023-06-26 10:22:41 -07:00
committed by GitHub
co-authored by Jake Bailey Mateusz Burzyński Daniel Rosenwasser
parent a94eb319eb
commit 5b85e9469a
17 changed files with 518 additions and 106 deletions
+37 -37
View File
@@ -6888,21 +6888,21 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const arity = getTypeReferenceArity(type);
const tupleConstituentNodes = mapToTypeNodes(typeArguments.slice(0, arity), context);
if (tupleConstituentNodes) {
if ((type.target as TupleType).labeledElementDeclarations) {
for (let i = 0; i < tupleConstituentNodes.length; i++) {
const flags = (type.target as TupleType).elementFlags[i];
const { labeledElementDeclarations } = type.target as TupleType;
for (let i = 0; i < tupleConstituentNodes.length; i++) {
const flags = (type.target as TupleType).elementFlags[i];
const labeledElementDeclaration = labeledElementDeclarations?.[i];
if (labeledElementDeclaration) {
tupleConstituentNodes[i] = factory.createNamedTupleMember(
flags & ElementFlags.Variable ? factory.createToken(SyntaxKind.DotDotDotToken) : undefined,
factory.createIdentifier(unescapeLeadingUnderscores(getTupleElementLabel((type.target as TupleType).labeledElementDeclarations![i]))),
factory.createIdentifier(unescapeLeadingUnderscores(getTupleElementLabel(labeledElementDeclaration))),
flags & ElementFlags.Optional ? factory.createToken(SyntaxKind.QuestionToken) : undefined,
flags & ElementFlags.Rest ? factory.createArrayTypeNode(tupleConstituentNodes[i]) :
tupleConstituentNodes[i]
);
}
}
else {
for (let i = 0; i < Math.min(arity, tupleConstituentNodes.length); i++) {
const flags = (type.target as TupleType).elementFlags[i];
else {
tupleConstituentNodes[i] =
flags & ElementFlags.Variable ? factory.createRestTypeNode(flags & ElementFlags.Rest ? factory.createArrayTypeNode(tupleConstituentNodes[i]) : tupleConstituentNodes[i]) :
flags & ElementFlags.Optional ? factory.createOptionalTypeNode(tupleConstituentNodes[i]) :
@@ -12657,19 +12657,20 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
function getExpandedParameters(sig: Signature, skipUnionExpanding?: boolean): readonly (readonly Symbol[])[] {
if (signatureHasRestParameter(sig)) {
const restIndex = sig.parameters.length - 1;
const restName = sig.parameters[restIndex].escapedName;
const restType = getTypeOfSymbol(sig.parameters[restIndex]);
if (isTupleType(restType)) {
return [expandSignatureParametersWithTupleMembers(restType, restIndex)];
return [expandSignatureParametersWithTupleMembers(restType, restIndex, restName)];
}
else if (!skipUnionExpanding && restType.flags & TypeFlags.Union && every((restType as UnionType).types, isTupleType)) {
return map((restType as UnionType).types, t => expandSignatureParametersWithTupleMembers(t as TupleTypeReference, restIndex));
return map((restType as UnionType).types, t => expandSignatureParametersWithTupleMembers(t as TupleTypeReference, restIndex, restName));
}
}
return [sig.parameters];
function expandSignatureParametersWithTupleMembers(restType: TupleTypeReference, restIndex: number) {
const elementTypes = getElementTypes(restType);
const associatedNames = getUniqAssociatedNamesFromTupleType(restType);
function expandSignatureParametersWithTupleMembers(restType: TupleTypeReference, restIndex: number, restName: __String) {
const elementTypes = getTypeArguments(restType);
const associatedNames = getUniqAssociatedNamesFromTupleType(restType, restName);
const restParams = map(elementTypes, (t, i) => {
// Lookup the label from the individual tuple passed in before falling back to the signature `rest` parameter name
const name = associatedNames && associatedNames[i] ? associatedNames[i] :
@@ -12684,10 +12685,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return concatenate(sig.parameters.slice(0, restIndex), restParams);
}
function getUniqAssociatedNamesFromTupleType(type: TupleTypeReference) {
function getUniqAssociatedNamesFromTupleType(type: TupleTypeReference, restName: __String) {
const associatedNamesMap = new Map<__String, number>();
return map(type.target.labeledElementDeclarations, labeledElement => {
const name = getTupleElementLabel(labeledElement);
return map(type.target.labeledElementDeclarations, (labeledElement, i) => {
const name = getTupleElementLabel(labeledElement, i, restName);
const prevCounter = associatedNamesMap.get(name);
if (prevCounter === undefined) {
associatedNamesMap.set(name, 1);
@@ -15963,8 +15964,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return readonly ? globalReadonlyArrayType : globalArrayType;
}
const elementFlags = map((node as TupleTypeNode).elements, getTupleElementFlags);
const missingName = some((node as TupleTypeNode).elements, e => e.kind !== SyntaxKind.NamedTupleMember);
return getTupleTargetType(elementFlags, readonly, /*associatedNames*/ missingName ? undefined : (node as TupleTypeNode).elements as readonly NamedTupleMember[]);
return getTupleTargetType(elementFlags, readonly, map((node as TupleTypeNode).elements, memberIfLabeledElementDeclaration));
}
function memberIfLabeledElementDeclaration(member: Node): NamedTupleMember | ParameterDeclaration | undefined {
return isNamedTupleMember(member) || isParameter(member) ? member : undefined;
}
// Return true if the given type reference node is directly aliased or if it needs to be deferred
@@ -16054,21 +16058,22 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return isTypeOperatorNode(node) && node.operator === SyntaxKind.ReadonlyKeyword;
}
function createTupleType(elementTypes: readonly Type[], elementFlags?: readonly ElementFlags[], readonly = false, namedMemberDeclarations?: readonly (NamedTupleMember | ParameterDeclaration)[]) {
function createTupleType(elementTypes: readonly Type[], elementFlags?: readonly ElementFlags[], readonly = false, namedMemberDeclarations: readonly (NamedTupleMember | ParameterDeclaration | undefined)[] = []) {
const tupleTarget = getTupleTargetType(elementFlags || map(elementTypes, _ => ElementFlags.Required), readonly, namedMemberDeclarations);
return tupleTarget === emptyGenericType ? emptyObjectType :
elementTypes.length ? createNormalizedTypeReference(tupleTarget, elementTypes) :
tupleTarget;
}
function getTupleTargetType(elementFlags: readonly ElementFlags[], readonly: boolean, namedMemberDeclarations?: readonly (NamedTupleMember | ParameterDeclaration)[]): GenericType {
function getTupleTargetType(elementFlags: readonly ElementFlags[], readonly: boolean, namedMemberDeclarations: readonly (NamedTupleMember | ParameterDeclaration | undefined)[]): GenericType {
if (elementFlags.length === 1 && elementFlags[0] & ElementFlags.Rest) {
// [...X[]] is equivalent to just X[]
return readonly ? globalReadonlyArrayType : globalArrayType;
}
const memberIds = mapDefined(namedMemberDeclarations, node => node ? getNodeId(node) : undefined);
const key = map(elementFlags, f => f & ElementFlags.Required ? "#" : f & ElementFlags.Optional ? "?" : f & ElementFlags.Rest ? "." : "*").join() +
(readonly ? "R" : "") +
(namedMemberDeclarations && namedMemberDeclarations.length ? "," + map(namedMemberDeclarations, getNodeId).join(",") : "");
(memberIds.length ? "," + memberIds.join(",") : "");
let type = tupleTypes.get(key);
if (!type) {
tupleTypes.set(key, type = createTupleTargetType(elementFlags, readonly, namedMemberDeclarations));
@@ -16083,7 +16088,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
//
// Note that the generic type created by this function has no symbol associated with it. The same
// is true for each of the synthesized type parameters.
function createTupleTargetType(elementFlags: readonly ElementFlags[], readonly: boolean, namedMemberDeclarations: readonly (NamedTupleMember | ParameterDeclaration)[] | undefined): TupleType {
function createTupleTargetType(elementFlags: readonly ElementFlags[], readonly: boolean, namedMemberDeclarations: readonly (NamedTupleMember | ParameterDeclaration | undefined)[]): TupleType {
const arity = elementFlags.length;
const minLength = countWhere(elementFlags, f => !!(f & (ElementFlags.Required | ElementFlags.Variadic)));
let typeParameters: TypeParameter[] | undefined;
@@ -16165,7 +16170,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// In either layout, zero or more generic variadic elements may be present at any location.
const expandedTypes: Type[] = [];
const expandedFlags: ElementFlags[] = [];
let expandedDeclarations: (NamedTupleMember | ParameterDeclaration)[] | undefined = [];
const expandedDeclarations: (NamedTupleMember | ParameterDeclaration | undefined)[] = [];
let lastRequiredIndex = -1;
let firstRestIndex = -1;
let lastOptionalOrRestIndex = -1;
@@ -16208,7 +16213,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
(t, i) => expandedFlags[firstRestIndex + i] & ElementFlags.Variadic ? getIndexedAccessType(t, numberType) : t));
expandedTypes.splice(firstRestIndex + 1, lastOptionalOrRestIndex - firstRestIndex);
expandedFlags.splice(firstRestIndex + 1, lastOptionalOrRestIndex - firstRestIndex);
expandedDeclarations?.splice(firstRestIndex + 1, lastOptionalOrRestIndex - firstRestIndex);
expandedDeclarations.splice(firstRestIndex + 1, lastOptionalOrRestIndex - firstRestIndex);
}
const tupleTarget = getTupleTargetType(expandedFlags, target.readonly, expandedDeclarations);
return tupleTarget === emptyGenericType ? emptyObjectType :
@@ -16227,12 +16232,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
expandedTypes.push(flags & ElementFlags.Optional ? addOptionality(type, /*isProperty*/ true) : type);
expandedFlags.push(flags);
if (expandedDeclarations && declaration) {
expandedDeclarations.push(declaration);
}
else {
expandedDeclarations = undefined;
}
expandedDeclarations.push(declaration);
}
}
@@ -34828,7 +34828,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return type;
}
function getTupleElementLabel(d: ParameterDeclaration | NamedTupleMember) {
function getTupleElementLabel(d: ParameterDeclaration | NamedTupleMember): __String;
function getTupleElementLabel(d: ParameterDeclaration | NamedTupleMember | undefined, index: number, restParameterName?: __String): __String;
function getTupleElementLabel(d: ParameterDeclaration | NamedTupleMember | undefined, index?: number, restParameterName = "arg" as __String) {
if (!d) {
return `${restParameterName}_${index}` as __String;
}
Debug.assert(isIdentifier(d.name)); // Parameter declarations could be binding patterns, but we only allow identifier names
return d.name.escapedText;
}
@@ -34843,7 +34848,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (isTupleType(restType)) {
const associatedNames = ((restType as TypeReference).target as TupleType).labeledElementDeclarations;
const index = pos - paramCount;
return associatedNames && getTupleElementLabel(associatedNames[index]) || restParameter.escapedName + "_" + index as __String;
return getTupleElementLabel(associatedNames?.[index], index, restParameter.escapedName);
}
return restParameter.escapedName;
}
@@ -38870,12 +38875,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const elementTypes = node.elements;
let seenOptionalElement = false;
let seenRestElement = false;
const hasNamedElement = some(elementTypes, isNamedTupleMember);
for (const e of elementTypes) {
if (e.kind !== SyntaxKind.NamedTupleMember && hasNamedElement) {
grammarErrorOnNode(e, Diagnostics.Tuple_members_must_all_have_names_or_all_not_have_names);
break;
}
const flags = getTupleElementFlags(e);
if (flags & ElementFlags.Variadic) {
const type = getTypeFromTypeNode((e as RestTypeNode | NamedTupleMember).type);
-4
View File
@@ -4277,10 +4277,6 @@
"category": "Error",
"code": 5083
},
"Tuple members must all have names or all not have names.": {
"category": "Error",
"code": 5084
},
"A tuple member cannot be both optional and rest.": {
"category": "Error",
"code": 5085
+1 -1
View File
@@ -6434,7 +6434,7 @@ export interface TupleType extends GenericType {
hasRestElement: boolean;
combinedFlags: ElementFlags;
readonly: boolean;
labeledElementDeclarations?: readonly (NamedTupleMember | ParameterDeclaration)[];
labeledElementDeclarations?: readonly (NamedTupleMember | ParameterDeclaration | undefined)[];
}
export interface TupleTypeReference extends TypeReference {
+1 -1
View File
@@ -6951,7 +6951,7 @@ declare namespace ts {
hasRestElement: boolean;
combinedFlags: ElementFlags;
readonly: boolean;
labeledElementDeclarations?: readonly (NamedTupleMember | ParameterDeclaration)[];
labeledElementDeclarations?: readonly (NamedTupleMember | ParameterDeclaration | undefined)[];
}
interface TupleTypeReference extends TypeReference {
target: TupleType;
+1 -1
View File
@@ -2898,7 +2898,7 @@ declare namespace ts {
hasRestElement: boolean;
combinedFlags: ElementFlags;
readonly: boolean;
labeledElementDeclarations?: readonly (NamedTupleMember | ParameterDeclaration)[];
labeledElementDeclarations?: readonly (NamedTupleMember | ParameterDeclaration | undefined)[];
}
interface TupleTypeReference extends TypeReference {
target: TupleType;
@@ -444,7 +444,7 @@ type T05<T extends any[]> = Parameters<(x: string, ...args: T) => void>;
>args : T
type T06 = T05<[number, ...boolean[]]>;
>T06 : [string, number, ...boolean[]]
>T06 : [x: string, number, ...boolean[]]
type P1<T extends Function> = T extends (head: infer A, ...tail: infer B) => any ? { head: A, tail: B } : any[];
>P1 : P1<T>
@@ -264,19 +264,19 @@ ff2 = ff1;
function ff3<A extends unknown[]>(s1: (...args: [x: string, ...rest: A | [number]]) => void, s2: (x: string, ...rest: A | [number]) => void) {
>ff3 : <A extends unknown[]>(s1: (...args: [x: string, ...rest: A | [number]]) => void, s2: (x: string, ...rest: A | [number]) => void) => void
>s1 : (...args: [x: string, ...rest: A | [number]]) => void
>args : [string, number] | [x: string, ...rest: A]
>args : [x: string, number] | [x: string, ...rest: A]
>s2 : (x: string, ...rest: A | [number]) => void
>x : string
>rest : [number] | A
s1 = s2;
>s1 = s2 : (x: string, ...rest: [number] | A) => void
>s1 : (...args: [string, number] | [x: string, ...rest: A]) => void
>s1 : (...args: [x: string, number] | [x: string, ...rest: A]) => void
>s2 : (x: string, ...rest: [number] | A) => void
s2 = s1;
>s2 = s1 : (...args: [string, number] | [x: string, ...rest: A]) => void
>s2 = s1 : (...args: [x: string, number] | [x: string, ...rest: A]) => void
>s2 : (x: string, ...rest: [number] | A) => void
>s1 : (...args: [string, number] | [x: string, ...rest: A]) => void
>s1 : (...args: [x: string, number] | [x: string, ...rest: A]) => void
}
@@ -40,7 +40,7 @@ type SubTup2VariadicAndRest<T extends unknown[]> = T extends [
: never;
type SubTup2VariadicAndRestTest = SubTup2VariadicAndRest<[a: 0, b: 1, ...c: 2[]]>;
>SubTup2VariadicAndRestTest : [0, 1, 2]
>SubTup2VariadicAndRestTest : [a: 0, b: 1, 2]
type SubTup2TrailingVariadic<T extends unknown[]> = T extends [
>SubTup2TrailingVariadic : SubTup2TrailingVariadic<T>
@@ -67,7 +67,7 @@ type SubTup2RestAndTrailingVariadic2<T extends unknown[]> = T extends [
: never;
type SubTup2RestAndTrailingVariadic2Test = SubTup2RestAndTrailingVariadic2<[...a: 0[], b: 1, c: 2]>;
>SubTup2RestAndTrailingVariadic2Test : [0, 1, 2]
>SubTup2RestAndTrailingVariadic2Test : [0, b: 1, c: 2]
type SubTup2VariadicWithLeadingFixedElements<T extends unknown[]> = T extends [
>SubTup2VariadicWithLeadingFixedElements : SubTup2VariadicWithLeadingFixedElements<T>
@@ -1,9 +1,3 @@
namedTupleMembersErrors.ts(1,41): error TS5084: Tuple members must all have names or all not have names.
namedTupleMembersErrors.ts(2,25): error TS5084: Tuple members must all have names or all not have names.
namedTupleMembersErrors.ts(4,32): error TS5084: Tuple members must all have names or all not have names.
namedTupleMembersErrors.ts(5,22): error TS5084: Tuple members must all have names or all not have names.
namedTupleMembersErrors.ts(7,32): error TS5084: Tuple members must all have names or all not have names.
namedTupleMembersErrors.ts(8,22): error TS5084: Tuple members must all have names or all not have names.
namedTupleMembersErrors.ts(10,29): error TS5086: A labeled tuple element is declared as optional with a question mark after the name and before the colon, rather than after the type.
namedTupleMembersErrors.ts(12,46): error TS5087: A labeled tuple element is declared as rest with a '...' before the name, rather than before the type.
namedTupleMembersErrors.ts(14,49): error TS5087: A labeled tuple element is declared as rest with a '...' before the name, rather than before the type.
@@ -14,27 +8,15 @@ namedTupleMembersErrors.ts(20,13): error TS2456: Type alias 'RecusiveRestUnlabel
namedTupleMembersErrors.ts(21,13): error TS2456: Type alias 'RecusiveRest' circularly references itself.
==== namedTupleMembersErrors.ts (14 errors) ====
export type Segment1 = [length: number, number]; // partially named, disallowed
~~~~~~
!!! error TS5084: Tuple members must all have names or all not have names.
export type Segment2 = [number, size: number]; // partially named, disallowed
~~~~~~
!!! error TS5084: Tuple members must all have names or all not have names.
==== namedTupleMembersErrors.ts (8 errors) ====
export type Segment1 = [length: number, number];
export type Segment2 = [number, size: number];
export type List = [item: any, ...any]; // partially named, disallowed
~~~~~~
!!! error TS5084: Tuple members must all have names or all not have names.
export type List2 = [any, ...remainder: any]; // partially named, disallowed
~~~
!!! error TS5084: Tuple members must all have names or all not have names.
export type List = [item: any, ...any];
export type List2 = [any, ...remainder: any];
export type Pair = [item: any, any?]; // partially named, disallowed
~~~~
!!! error TS5084: Tuple members must all have names or all not have names.
export type Pair2 = [any, last?: any]; // partially named, disallowed
~~~
!!! error TS5084: Tuple members must all have names or all not have names.
export type Pair = [item: any, any?];
export type Pair2 = [any, last?: any];
export type Opt = [element: string?]; // question mark on element disallowed
~~~~~~~
@@ -1,14 +1,14 @@
//// [tests/cases/conformance/types/tuple/named/namedTupleMembersErrors.ts] ////
//// [namedTupleMembersErrors.ts]
export type Segment1 = [length: number, number]; // partially named, disallowed
export type Segment2 = [number, size: number]; // partially named, disallowed
export type Segment1 = [length: number, number];
export type Segment2 = [number, size: number];
export type List = [item: any, ...any]; // partially named, disallowed
export type List2 = [any, ...remainder: any]; // partially named, disallowed
export type List = [item: any, ...any];
export type List2 = [any, ...remainder: any];
export type Pair = [item: any, any?]; // partially named, disallowed
export type Pair2 = [any, last?: any]; // partially named, disallowed
export type Pair = [item: any, any?];
export type Pair2 = [any, last?: any];
export type Opt = [element: string?]; // question mark on element disallowed
@@ -1,22 +1,22 @@
//// [tests/cases/conformance/types/tuple/named/namedTupleMembersErrors.ts] ////
=== namedTupleMembersErrors.ts ===
export type Segment1 = [length: number, number]; // partially named, disallowed
export type Segment1 = [length: number, number];
>Segment1 : Symbol(Segment1, Decl(namedTupleMembersErrors.ts, 0, 0))
export type Segment2 = [number, size: number]; // partially named, disallowed
export type Segment2 = [number, size: number];
>Segment2 : Symbol(Segment2, Decl(namedTupleMembersErrors.ts, 0, 48))
export type List = [item: any, ...any]; // partially named, disallowed
export type List = [item: any, ...any];
>List : Symbol(List, Decl(namedTupleMembersErrors.ts, 1, 46))
export type List2 = [any, ...remainder: any]; // partially named, disallowed
export type List2 = [any, ...remainder: any];
>List2 : Symbol(List2, Decl(namedTupleMembersErrors.ts, 3, 39))
export type Pair = [item: any, any?]; // partially named, disallowed
export type Pair = [item: any, any?];
>Pair : Symbol(Pair, Decl(namedTupleMembersErrors.ts, 4, 45))
export type Pair2 = [any, last?: any]; // partially named, disallowed
export type Pair2 = [any, last?: any];
>Pair2 : Symbol(Pair2, Decl(namedTupleMembersErrors.ts, 6, 37))
export type Opt = [element: string?]; // question mark on element disallowed
@@ -1,23 +1,23 @@
//// [tests/cases/conformance/types/tuple/named/namedTupleMembersErrors.ts] ////
=== namedTupleMembersErrors.ts ===
export type Segment1 = [length: number, number]; // partially named, disallowed
>Segment1 : [number, number]
export type Segment1 = [length: number, number];
>Segment1 : [length: number, number]
export type Segment2 = [number, size: number]; // partially named, disallowed
>Segment2 : [number, number]
export type Segment2 = [number, size: number];
>Segment2 : [number, size: number]
export type List = [item: any, ...any]; // partially named, disallowed
>List : [any, ...any[]]
export type List = [item: any, ...any];
>List : [item: any, ...any[]]
export type List2 = [any, ...remainder: any]; // partially named, disallowed
>List2 : [any, ...any[]]
export type List2 = [any, ...remainder: any];
>List2 : [any, ...remainder: any[]]
export type Pair = [item: any, any?]; // partially named, disallowed
>Pair : [any, any?]
export type Pair = [item: any, any?];
>Pair : [item: any, any?]
export type Pair2 = [any, last?: any]; // partially named, disallowed
>Pair2 : [any, any?]
export type Pair2 = [any, last?: any];
>Pair2 : [any, last?: any]
export type Opt = [element: string?]; // question mark on element disallowed
>Opt : [element: string]
@@ -0,0 +1,95 @@
//// [tests/cases/conformance/types/tuple/named/partiallyNamedTuples.ts] ////
//// [partiallyNamedTuples.ts]
type NamedAndAnonymous = [a: string, number];
function fa1(...args: NamedAndAnonymous) {}
function fa2(a: NamedAndAnonymous, ...args: NamedAndAnonymous) {}
type NamedAnonymousMixed = [a: string, number, c: number, NamedAndAnonymous];
function fb1(...args: NamedAnonymousMixed) {}
function fb2(a: NamedAnonymousMixed, ...args: NamedAnonymousMixed) {}
function fb3(a: NamedAnonymousMixed, ...args: NamedAnonymousMixed[3]) {}
type ToAnonymousTuple<T extends unknown[]> = {
[K in keyof T]: [K, T[K], keyof T, T];
};
type AnonymousToAnonymous = ToAnonymousTuple<[boolean, number]>;
type MixedToAnonymous = ToAnonymousTuple<[boolean, second: number]>;
type NamedToAnonymous = ToAnonymousTuple<[first: boolean, second: number]>;
type ToMixedTuple<T extends unknown[]> = {
[K in keyof T]: [K, second: T[K], keyof T, fourth: T];
};
type AnonymousToMixed = ToMixedTuple<[boolean, number]>;
type MixedToMixed = ToMixedTuple<[boolean, second: number]>;
type NamedToMixed = ToMixedTuple<[first: boolean, second: number]>;
type MixedSpread = [first: boolean, ...[second: string]];
type ConditionalTuple = [
first: boolean,
...(0 extends 0 ? [second: string] : [])
];
type AddMixedConditional<T> = [
first: boolean,
null,
third: T extends number ? "a" : "b",
...(T extends 0 ? [fourth: "c"] : [])
];
type AddMixedConditionalBoolean = AddMixedConditional<boolean>;
type AddMixedConditionalLiteral = AddMixedConditional<0>;
type AddMixedConditionalNumberPrimitive = AddMixedConditional<number>;
declare function test<T extends readonly unknown[]>(
arg: [
...{
[K in keyof T]: {
type: T[K];
};
}
]
): T;
declare const input: [first: { type: number }, { type: string }];
const output = test(input);
//// [partiallyNamedTuples.js]
function fa1() {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
}
function fa2(a) {
var args = [];
for (var _i = 1; _i < arguments.length; _i++) {
args[_i - 1] = arguments[_i];
}
}
function fb1() {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
}
function fb2(a) {
var args = [];
for (var _i = 1; _i < arguments.length; _i++) {
args[_i - 1] = arguments[_i];
}
}
function fb3(a) {
var args = [];
for (var _i = 1; _i < arguments.length; _i++) {
args[_i - 1] = arguments[_i];
}
}
var output = test(input);
@@ -0,0 +1,164 @@
//// [tests/cases/conformance/types/tuple/named/partiallyNamedTuples.ts] ////
=== partiallyNamedTuples.ts ===
type NamedAndAnonymous = [a: string, number];
>NamedAndAnonymous : Symbol(NamedAndAnonymous, Decl(partiallyNamedTuples.ts, 0, 0))
function fa1(...args: NamedAndAnonymous) {}
>fa1 : Symbol(fa1, Decl(partiallyNamedTuples.ts, 0, 45))
>args : Symbol(args, Decl(partiallyNamedTuples.ts, 2, 13))
>NamedAndAnonymous : Symbol(NamedAndAnonymous, Decl(partiallyNamedTuples.ts, 0, 0))
function fa2(a: NamedAndAnonymous, ...args: NamedAndAnonymous) {}
>fa2 : Symbol(fa2, Decl(partiallyNamedTuples.ts, 2, 43))
>a : Symbol(a, Decl(partiallyNamedTuples.ts, 3, 13))
>NamedAndAnonymous : Symbol(NamedAndAnonymous, Decl(partiallyNamedTuples.ts, 0, 0))
>args : Symbol(args, Decl(partiallyNamedTuples.ts, 3, 34))
>NamedAndAnonymous : Symbol(NamedAndAnonymous, Decl(partiallyNamedTuples.ts, 0, 0))
type NamedAnonymousMixed = [a: string, number, c: number, NamedAndAnonymous];
>NamedAnonymousMixed : Symbol(NamedAnonymousMixed, Decl(partiallyNamedTuples.ts, 3, 65))
>NamedAndAnonymous : Symbol(NamedAndAnonymous, Decl(partiallyNamedTuples.ts, 0, 0))
function fb1(...args: NamedAnonymousMixed) {}
>fb1 : Symbol(fb1, Decl(partiallyNamedTuples.ts, 5, 77))
>args : Symbol(args, Decl(partiallyNamedTuples.ts, 7, 13))
>NamedAnonymousMixed : Symbol(NamedAnonymousMixed, Decl(partiallyNamedTuples.ts, 3, 65))
function fb2(a: NamedAnonymousMixed, ...args: NamedAnonymousMixed) {}
>fb2 : Symbol(fb2, Decl(partiallyNamedTuples.ts, 7, 45))
>a : Symbol(a, Decl(partiallyNamedTuples.ts, 8, 13))
>NamedAnonymousMixed : Symbol(NamedAnonymousMixed, Decl(partiallyNamedTuples.ts, 3, 65))
>args : Symbol(args, Decl(partiallyNamedTuples.ts, 8, 36))
>NamedAnonymousMixed : Symbol(NamedAnonymousMixed, Decl(partiallyNamedTuples.ts, 3, 65))
function fb3(a: NamedAnonymousMixed, ...args: NamedAnonymousMixed[3]) {}
>fb3 : Symbol(fb3, Decl(partiallyNamedTuples.ts, 8, 69))
>a : Symbol(a, Decl(partiallyNamedTuples.ts, 9, 13))
>NamedAnonymousMixed : Symbol(NamedAnonymousMixed, Decl(partiallyNamedTuples.ts, 3, 65))
>args : Symbol(args, Decl(partiallyNamedTuples.ts, 9, 36))
>NamedAnonymousMixed : Symbol(NamedAnonymousMixed, Decl(partiallyNamedTuples.ts, 3, 65))
type ToAnonymousTuple<T extends unknown[]> = {
>ToAnonymousTuple : Symbol(ToAnonymousTuple, Decl(partiallyNamedTuples.ts, 9, 72))
>T : Symbol(T, Decl(partiallyNamedTuples.ts, 11, 22))
[K in keyof T]: [K, T[K], keyof T, T];
>K : Symbol(K, Decl(partiallyNamedTuples.ts, 12, 3))
>T : Symbol(T, Decl(partiallyNamedTuples.ts, 11, 22))
>K : Symbol(K, Decl(partiallyNamedTuples.ts, 12, 3))
>T : Symbol(T, Decl(partiallyNamedTuples.ts, 11, 22))
>K : Symbol(K, Decl(partiallyNamedTuples.ts, 12, 3))
>T : Symbol(T, Decl(partiallyNamedTuples.ts, 11, 22))
>T : Symbol(T, Decl(partiallyNamedTuples.ts, 11, 22))
};
type AnonymousToAnonymous = ToAnonymousTuple<[boolean, number]>;
>AnonymousToAnonymous : Symbol(AnonymousToAnonymous, Decl(partiallyNamedTuples.ts, 13, 2))
>ToAnonymousTuple : Symbol(ToAnonymousTuple, Decl(partiallyNamedTuples.ts, 9, 72))
type MixedToAnonymous = ToAnonymousTuple<[boolean, second: number]>;
>MixedToAnonymous : Symbol(MixedToAnonymous, Decl(partiallyNamedTuples.ts, 15, 64))
>ToAnonymousTuple : Symbol(ToAnonymousTuple, Decl(partiallyNamedTuples.ts, 9, 72))
type NamedToAnonymous = ToAnonymousTuple<[first: boolean, second: number]>;
>NamedToAnonymous : Symbol(NamedToAnonymous, Decl(partiallyNamedTuples.ts, 16, 68))
>ToAnonymousTuple : Symbol(ToAnonymousTuple, Decl(partiallyNamedTuples.ts, 9, 72))
type ToMixedTuple<T extends unknown[]> = {
>ToMixedTuple : Symbol(ToMixedTuple, Decl(partiallyNamedTuples.ts, 17, 75))
>T : Symbol(T, Decl(partiallyNamedTuples.ts, 19, 18))
[K in keyof T]: [K, second: T[K], keyof T, fourth: T];
>K : Symbol(K, Decl(partiallyNamedTuples.ts, 20, 3))
>T : Symbol(T, Decl(partiallyNamedTuples.ts, 19, 18))
>K : Symbol(K, Decl(partiallyNamedTuples.ts, 20, 3))
>T : Symbol(T, Decl(partiallyNamedTuples.ts, 19, 18))
>K : Symbol(K, Decl(partiallyNamedTuples.ts, 20, 3))
>T : Symbol(T, Decl(partiallyNamedTuples.ts, 19, 18))
>T : Symbol(T, Decl(partiallyNamedTuples.ts, 19, 18))
};
type AnonymousToMixed = ToMixedTuple<[boolean, number]>;
>AnonymousToMixed : Symbol(AnonymousToMixed, Decl(partiallyNamedTuples.ts, 21, 2))
>ToMixedTuple : Symbol(ToMixedTuple, Decl(partiallyNamedTuples.ts, 17, 75))
type MixedToMixed = ToMixedTuple<[boolean, second: number]>;
>MixedToMixed : Symbol(MixedToMixed, Decl(partiallyNamedTuples.ts, 23, 56))
>ToMixedTuple : Symbol(ToMixedTuple, Decl(partiallyNamedTuples.ts, 17, 75))
type NamedToMixed = ToMixedTuple<[first: boolean, second: number]>;
>NamedToMixed : Symbol(NamedToMixed, Decl(partiallyNamedTuples.ts, 24, 60))
>ToMixedTuple : Symbol(ToMixedTuple, Decl(partiallyNamedTuples.ts, 17, 75))
type MixedSpread = [first: boolean, ...[second: string]];
>MixedSpread : Symbol(MixedSpread, Decl(partiallyNamedTuples.ts, 25, 67))
type ConditionalTuple = [
>ConditionalTuple : Symbol(ConditionalTuple, Decl(partiallyNamedTuples.ts, 27, 57))
first: boolean,
...(0 extends 0 ? [second: string] : [])
];
type AddMixedConditional<T> = [
>AddMixedConditional : Symbol(AddMixedConditional, Decl(partiallyNamedTuples.ts, 32, 2))
>T : Symbol(T, Decl(partiallyNamedTuples.ts, 34, 25))
first: boolean,
null,
third: T extends number ? "a" : "b",
>T : Symbol(T, Decl(partiallyNamedTuples.ts, 34, 25))
...(T extends 0 ? [fourth: "c"] : [])
>T : Symbol(T, Decl(partiallyNamedTuples.ts, 34, 25))
];
type AddMixedConditionalBoolean = AddMixedConditional<boolean>;
>AddMixedConditionalBoolean : Symbol(AddMixedConditionalBoolean, Decl(partiallyNamedTuples.ts, 39, 2))
>AddMixedConditional : Symbol(AddMixedConditional, Decl(partiallyNamedTuples.ts, 32, 2))
type AddMixedConditionalLiteral = AddMixedConditional<0>;
>AddMixedConditionalLiteral : Symbol(AddMixedConditionalLiteral, Decl(partiallyNamedTuples.ts, 41, 63))
>AddMixedConditional : Symbol(AddMixedConditional, Decl(partiallyNamedTuples.ts, 32, 2))
type AddMixedConditionalNumberPrimitive = AddMixedConditional<number>;
>AddMixedConditionalNumberPrimitive : Symbol(AddMixedConditionalNumberPrimitive, Decl(partiallyNamedTuples.ts, 42, 57))
>AddMixedConditional : Symbol(AddMixedConditional, Decl(partiallyNamedTuples.ts, 32, 2))
declare function test<T extends readonly unknown[]>(
>test : Symbol(test, Decl(partiallyNamedTuples.ts, 43, 70))
>T : Symbol(T, Decl(partiallyNamedTuples.ts, 45, 22))
arg: [
>arg : Symbol(arg, Decl(partiallyNamedTuples.ts, 45, 52))
...{
[K in keyof T]: {
>K : Symbol(K, Decl(partiallyNamedTuples.ts, 48, 7))
>T : Symbol(T, Decl(partiallyNamedTuples.ts, 45, 22))
type: T[K];
>type : Symbol(type, Decl(partiallyNamedTuples.ts, 48, 23))
>T : Symbol(T, Decl(partiallyNamedTuples.ts, 45, 22))
>K : Symbol(K, Decl(partiallyNamedTuples.ts, 48, 7))
};
}
]
): T;
>T : Symbol(T, Decl(partiallyNamedTuples.ts, 45, 22))
declare const input: [first: { type: number }, { type: string }];
>input : Symbol(input, Decl(partiallyNamedTuples.ts, 55, 13))
>type : Symbol(type, Decl(partiallyNamedTuples.ts, 55, 30))
>type : Symbol(type, Decl(partiallyNamedTuples.ts, 55, 48))
const output = test(input);
>output : Symbol(output, Decl(partiallyNamedTuples.ts, 57, 5))
>test : Symbol(test, Decl(partiallyNamedTuples.ts, 43, 70))
>input : Symbol(input, Decl(partiallyNamedTuples.ts, 55, 13))
@@ -0,0 +1,117 @@
//// [tests/cases/conformance/types/tuple/named/partiallyNamedTuples.ts] ////
=== partiallyNamedTuples.ts ===
type NamedAndAnonymous = [a: string, number];
>NamedAndAnonymous : [a: string, number]
function fa1(...args: NamedAndAnonymous) {}
>fa1 : (a: string, args_1: number) => void
>args : NamedAndAnonymous
function fa2(a: NamedAndAnonymous, ...args: NamedAndAnonymous) {}
>fa2 : (a: NamedAndAnonymous, a: string, args_1: number) => void
>a : NamedAndAnonymous
>args : NamedAndAnonymous
type NamedAnonymousMixed = [a: string, number, c: number, NamedAndAnonymous];
>NamedAnonymousMixed : [a: string, number, c: number, NamedAndAnonymous]
function fb1(...args: NamedAnonymousMixed) {}
>fb1 : (a: string, args_1: number, c: number, args_3: NamedAndAnonymous) => void
>args : NamedAnonymousMixed
function fb2(a: NamedAnonymousMixed, ...args: NamedAnonymousMixed) {}
>fb2 : (a: NamedAnonymousMixed, a: string, args_1: number, c: number, args_3: NamedAndAnonymous) => void
>a : NamedAnonymousMixed
>args : NamedAnonymousMixed
function fb3(a: NamedAnonymousMixed, ...args: NamedAnonymousMixed[3]) {}
>fb3 : (a: NamedAnonymousMixed, a: string, args_1: number) => void
>a : NamedAnonymousMixed
>args : NamedAndAnonymous
type ToAnonymousTuple<T extends unknown[]> = {
>ToAnonymousTuple : ToAnonymousTuple<T>
[K in keyof T]: [K, T[K], keyof T, T];
};
type AnonymousToAnonymous = ToAnonymousTuple<[boolean, number]>;
>AnonymousToAnonymous : [["0", boolean, keyof [boolean, number], [boolean, number]], ["1", number, keyof [boolean, number], [boolean, number]]]
type MixedToAnonymous = ToAnonymousTuple<[boolean, second: number]>;
>MixedToAnonymous : [["0", boolean, keyof [boolean, second: number], [boolean, second: number]], second: ["1", number, keyof [boolean, second: number], [boolean, second: number]]]
type NamedToAnonymous = ToAnonymousTuple<[first: boolean, second: number]>;
>NamedToAnonymous : [first: ["0", boolean, keyof [first: boolean, second: number], [first: boolean, second: number]], second: ["1", number, keyof [first: boolean, second: number], [first: boolean, second: number]]]
type ToMixedTuple<T extends unknown[]> = {
>ToMixedTuple : ToMixedTuple<T>
[K in keyof T]: [K, second: T[K], keyof T, fourth: T];
};
type AnonymousToMixed = ToMixedTuple<[boolean, number]>;
>AnonymousToMixed : [["0", second: boolean, keyof [boolean, number], fourth: [boolean, number]], ["1", second: number, keyof [boolean, number], fourth: [boolean, number]]]
type MixedToMixed = ToMixedTuple<[boolean, second: number]>;
>MixedToMixed : [["0", second: boolean, keyof [boolean, second: number], fourth: [boolean, second: number]], second: ["1", second: number, keyof [boolean, second: number], fourth: [boolean, second: number]]]
type NamedToMixed = ToMixedTuple<[first: boolean, second: number]>;
>NamedToMixed : [first: ["0", second: boolean, keyof [first: boolean, second: number], fourth: [first: boolean, second: number]], second: ["1", second: number, keyof [first: boolean, second: number], fourth: [first: boolean, second: number]]]
type MixedSpread = [first: boolean, ...[second: string]];
>MixedSpread : [first: boolean, second: string]
type ConditionalTuple = [
>ConditionalTuple : [first: boolean, second: string]
first: boolean,
...(0 extends 0 ? [second: string] : [])
];
type AddMixedConditional<T> = [
>AddMixedConditional : [first: boolean, null, third: T extends number ? "a" : "b", ...T extends 0 ? [fourth: "c"] : []]
first: boolean,
null,
third: T extends number ? "a" : "b",
...(T extends 0 ? [fourth: "c"] : [])
];
type AddMixedConditionalBoolean = AddMixedConditional<boolean>;
>AddMixedConditionalBoolean : [first: boolean, null, third: "b"]
type AddMixedConditionalLiteral = AddMixedConditional<0>;
>AddMixedConditionalLiteral : [first: boolean, null, third: "a", fourth: "c"]
type AddMixedConditionalNumberPrimitive = AddMixedConditional<number>;
>AddMixedConditionalNumberPrimitive : [first: boolean, null, third: "a"]
declare function test<T extends readonly unknown[]>(
>test : <T extends readonly unknown[]>(arg: [...{ [K in keyof T]: { type: T[K]; }; }]) => T
arg: [
>arg : [...{ [K in keyof T]: { type: T[K]; }; }]
...{
[K in keyof T]: {
type: T[K];
>type : T[K]
};
}
]
): T;
declare const input: [first: { type: number }, { type: string }];
>input : [first: { type: number; }, { type: string; }]
>type : number
>type : string
const output = test(input);
>output : [first: number, string]
>test(input) : [first: number, string]
>test : <T extends readonly unknown[]>(arg: [...{ [K in keyof T]: { type: T[K]; }; }]) => T
>input : [first: { type: number; }, { type: string; }]
@@ -1,13 +1,13 @@
// @declaration: true
export type Segment1 = [length: number, number]; // partially named, disallowed
export type Segment2 = [number, size: number]; // partially named, disallowed
export type Segment1 = [length: number, number];
export type Segment2 = [number, size: number];
export type List = [item: any, ...any]; // partially named, disallowed
export type List2 = [any, ...remainder: any]; // partially named, disallowed
export type List = [item: any, ...any];
export type List2 = [any, ...remainder: any];
export type Pair = [item: any, any?]; // partially named, disallowed
export type Pair2 = [any, last?: any]; // partially named, disallowed
export type Pair = [item: any, any?];
export type Pair2 = [any, last?: any];
export type Opt = [element: string?]; // question mark on element disallowed
@@ -0,0 +1,58 @@
type NamedAndAnonymous = [a: string, number];
function fa1(...args: NamedAndAnonymous) {}
function fa2(a: NamedAndAnonymous, ...args: NamedAndAnonymous) {}
type NamedAnonymousMixed = [a: string, number, c: number, NamedAndAnonymous];
function fb1(...args: NamedAnonymousMixed) {}
function fb2(a: NamedAnonymousMixed, ...args: NamedAnonymousMixed) {}
function fb3(a: NamedAnonymousMixed, ...args: NamedAnonymousMixed[3]) {}
type ToAnonymousTuple<T extends unknown[]> = {
[K in keyof T]: [K, T[K], keyof T, T];
};
type AnonymousToAnonymous = ToAnonymousTuple<[boolean, number]>;
type MixedToAnonymous = ToAnonymousTuple<[boolean, second: number]>;
type NamedToAnonymous = ToAnonymousTuple<[first: boolean, second: number]>;
type ToMixedTuple<T extends unknown[]> = {
[K in keyof T]: [K, second: T[K], keyof T, fourth: T];
};
type AnonymousToMixed = ToMixedTuple<[boolean, number]>;
type MixedToMixed = ToMixedTuple<[boolean, second: number]>;
type NamedToMixed = ToMixedTuple<[first: boolean, second: number]>;
type MixedSpread = [first: boolean, ...[second: string]];
type ConditionalTuple = [
first: boolean,
...(0 extends 0 ? [second: string] : [])
];
type AddMixedConditional<T> = [
first: boolean,
null,
third: T extends number ? "a" : "b",
...(T extends 0 ? [fourth: "c"] : [])
];
type AddMixedConditionalBoolean = AddMixedConditional<boolean>;
type AddMixedConditionalLiteral = AddMixedConditional<0>;
type AddMixedConditionalNumberPrimitive = AddMixedConditional<number>;
declare function test<T extends readonly unknown[]>(
arg: [
...{
[K in keyof T]: {
type: T[K];
};
}
]
): T;
declare const input: [first: { type: number }, { type: string }];
const output = test(input);