diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bdf2dff585a47..166b50ce8b3d4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6876,21 +6876,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]) : @@ -12636,19 +12636,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] : @@ -12663,10 +12664,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); @@ -15934,8 +15935,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 @@ -16025,21 +16029,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)); @@ -16054,7 +16059,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; @@ -16136,7 +16141,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; @@ -16179,7 +16184,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 : @@ -16198,12 +16203,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); } } @@ -34793,7 +34793,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; } @@ -34808,7 +34813,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; } @@ -38821,12 +38826,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); diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 65ccc4cb18fdc..b89eeeb8d5b9f 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -4237,10 +4237,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 diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 008d4d710bea5..330f05107ba90 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6430,7 +6430,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 { diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 56c4b49ff6df2..f25295c367d2c 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -6948,7 +6948,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; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 2b642a091ce2e..5e3133bf0f733 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2895,7 +2895,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; diff --git a/tests/baselines/reference/genericRestParameters2.types b/tests/baselines/reference/genericRestParameters2.types index 6ad95193ba359..3fb55995e1c12 100644 --- a/tests/baselines/reference/genericRestParameters2.types +++ b/tests/baselines/reference/genericRestParameters2.types @@ -444,7 +444,7 @@ type T05 = 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 (head: infer A, ...tail: infer B) => any ? { head: A, tail: B } : any[]; >P1 : P1 diff --git a/tests/baselines/reference/genericRestParameters3.types b/tests/baselines/reference/genericRestParameters3.types index 7b5612921d0ec..f68aff7833e40 100644 --- a/tests/baselines/reference/genericRestParameters3.types +++ b/tests/baselines/reference/genericRestParameters3.types @@ -264,19 +264,19 @@ ff2 = ff1; function ff3(s1: (...args: [x: string, ...rest: A | [number]]) => void, s2: (x: string, ...rest: A | [number]) => void) { >ff3 : (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 } diff --git a/tests/baselines/reference/inferTypesWithFixedTupleExtendsAtVariadicPosition.types b/tests/baselines/reference/inferTypesWithFixedTupleExtendsAtVariadicPosition.types index 79354eccf2ab4..47027e0428628 100644 --- a/tests/baselines/reference/inferTypesWithFixedTupleExtendsAtVariadicPosition.types +++ b/tests/baselines/reference/inferTypesWithFixedTupleExtendsAtVariadicPosition.types @@ -40,7 +40,7 @@ type SubTup2VariadicAndRest = 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 [ >SubTup2TrailingVariadic : SubTup2TrailingVariadic @@ -67,7 +67,7 @@ type SubTup2RestAndTrailingVariadic2 = 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 [ >SubTup2VariadicWithLeadingFixedElements : SubTup2VariadicWithLeadingFixedElements diff --git a/tests/baselines/reference/namedTupleMembersErrors.errors.txt b/tests/baselines/reference/namedTupleMembersErrors.errors.txt index 3cb737741448f..a8cdf98ca30b8 100644 --- a/tests/baselines/reference/namedTupleMembersErrors.errors.txt +++ b/tests/baselines/reference/namedTupleMembersErrors.errors.txt @@ -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 ~~~~~~~ diff --git a/tests/baselines/reference/namedTupleMembersErrors.js b/tests/baselines/reference/namedTupleMembersErrors.js index caa410346fb82..b31332e4a2cbf 100644 --- a/tests/baselines/reference/namedTupleMembersErrors.js +++ b/tests/baselines/reference/namedTupleMembersErrors.js @@ -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 diff --git a/tests/baselines/reference/namedTupleMembersErrors.symbols b/tests/baselines/reference/namedTupleMembersErrors.symbols index 19bfa42afeb83..865bfc9842e50 100644 --- a/tests/baselines/reference/namedTupleMembersErrors.symbols +++ b/tests/baselines/reference/namedTupleMembersErrors.symbols @@ -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 diff --git a/tests/baselines/reference/namedTupleMembersErrors.types b/tests/baselines/reference/namedTupleMembersErrors.types index 060753947189c..46f0bc4ca60a4 100644 --- a/tests/baselines/reference/namedTupleMembersErrors.types +++ b/tests/baselines/reference/namedTupleMembersErrors.types @@ -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] diff --git a/tests/baselines/reference/partiallyNamedTuples.js b/tests/baselines/reference/partiallyNamedTuples.js new file mode 100644 index 0000000000000..8d497fe5dd55c --- /dev/null +++ b/tests/baselines/reference/partiallyNamedTuples.js @@ -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 = { + [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 = { + [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 = [ + first: boolean, + null, + third: T extends number ? "a" : "b", + ...(T extends 0 ? [fourth: "c"] : []) +]; + +type AddMixedConditionalBoolean = AddMixedConditional; +type AddMixedConditionalLiteral = AddMixedConditional<0>; +type AddMixedConditionalNumberPrimitive = AddMixedConditional; + +declare function test( + 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); diff --git a/tests/baselines/reference/partiallyNamedTuples.symbols b/tests/baselines/reference/partiallyNamedTuples.symbols new file mode 100644 index 0000000000000..89e2e81b463f5 --- /dev/null +++ b/tests/baselines/reference/partiallyNamedTuples.symbols @@ -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 = { +>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 = { +>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 = [ +>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; +>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; +>AddMixedConditionalNumberPrimitive : Symbol(AddMixedConditionalNumberPrimitive, Decl(partiallyNamedTuples.ts, 42, 57)) +>AddMixedConditional : Symbol(AddMixedConditional, Decl(partiallyNamedTuples.ts, 32, 2)) + +declare function test( +>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)) + diff --git a/tests/baselines/reference/partiallyNamedTuples.types b/tests/baselines/reference/partiallyNamedTuples.types new file mode 100644 index 0000000000000..5f1b72e5e5c90 --- /dev/null +++ b/tests/baselines/reference/partiallyNamedTuples.types @@ -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 = { +>ToAnonymousTuple : ToAnonymousTuple + + [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 = { +>ToMixedTuple : ToMixedTuple + + [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 = [ +>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; +>AddMixedConditionalBoolean : [first: boolean, null, third: "b"] + +type AddMixedConditionalLiteral = AddMixedConditional<0>; +>AddMixedConditionalLiteral : [first: boolean, null, third: "a", fourth: "c"] + +type AddMixedConditionalNumberPrimitive = AddMixedConditional; +>AddMixedConditionalNumberPrimitive : [first: boolean, null, third: "a"] + +declare function test( +>test : (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 : (arg: [...{ [K in keyof T]: { type: T[K]; }; }]) => T +>input : [first: { type: number; }, { type: string; }] + diff --git a/tests/cases/conformance/types/tuple/named/namedTupleMembersErrors.ts b/tests/cases/conformance/types/tuple/named/namedTupleMembersErrors.ts index 6a1f5a0537085..ecd90aebd7ddd 100644 --- a/tests/cases/conformance/types/tuple/named/namedTupleMembersErrors.ts +++ b/tests/cases/conformance/types/tuple/named/namedTupleMembersErrors.ts @@ -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 diff --git a/tests/cases/conformance/types/tuple/named/partiallyNamedTuples.ts b/tests/cases/conformance/types/tuple/named/partiallyNamedTuples.ts new file mode 100644 index 0000000000000..0c6e3752844fd --- /dev/null +++ b/tests/cases/conformance/types/tuple/named/partiallyNamedTuples.ts @@ -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 = { + [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 = { + [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 = [ + first: boolean, + null, + third: T extends number ? "a" : "b", + ...(T extends 0 ? [fourth: "c"] : []) +]; + +type AddMixedConditionalBoolean = AddMixedConditional; +type AddMixedConditionalLiteral = AddMixedConditional<0>; +type AddMixedConditionalNumberPrimitive = AddMixedConditional; + +declare function test( + arg: [ + ...{ + [K in keyof T]: { + type: T[K]; + }; + } + ] +): T; + +declare const input: [first: { type: number }, { type: string }]; + +const output = test(input); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy