From 97cb4ce3179647dd8dd926941122e92d856f80b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BChler?= Date: Thu, 5 Jul 2018 10:46:38 +0200 Subject: [PATCH] feat(parsing): parse object and array destructurings correctly (#76) --- .appveyor.yml | 3 +- src/declarations/ParameterDeclaration.ts | 38 ++ src/node-parser/function-parser.ts | 72 +- test/TypescriptParser.spec.ts | 85 +++ .../TypescriptParser.spec.ts.snap | 620 ++++++++++++++---- .../typescript-parser/parameters.ts | 21 + 6 files changed, 693 insertions(+), 146 deletions(-) create mode 100644 test/_workspace/typescript-parser/parameters.ts diff --git a/.appveyor.yml b/.appveyor.yml index cc809ee..864722f 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -8,8 +8,6 @@ matrix: environment: matrix: - nodejs_version: "10" - - nodejs_version: "9" - - nodejs_version: "8" install: - ps: Install-Product node $env:nodejs_version @@ -19,5 +17,6 @@ test_script: - npm test - npm install -g codecov - codecov + - npm run build build: off diff --git a/src/declarations/ParameterDeclaration.ts b/src/declarations/ParameterDeclaration.ts index c0bb2cd..49a8c1f 100644 --- a/src/declarations/ParameterDeclaration.ts +++ b/src/declarations/ParameterDeclaration.ts @@ -11,3 +11,41 @@ import { TypedDeclaration } from './Declaration'; export class ParameterDeclaration implements TypedDeclaration { constructor(public name: string, public type: string | undefined, public start?: number, public end?: number) { } } + +export class BoundParameterDeclaration extends ParameterDeclaration { + public parameters: ParameterDeclaration[] = []; + public typeReference: string | undefined; + + public get name(): string { + return this.parameters.length ? + `${this.startCharacter} ${this.parameters.map(p => p.name).join(', ')} ${this.endCharacter}` : + this.startCharacter + this.endCharacter; + } + + public set name(_: string) { } + + public get type(): string { + return this.typeReference || + this.parameters.length ? + `{ ${this.parameters.map(p => p.type).join(', ')} }` : + this.startCharacter + this.endCharacter; + } + + public set type(_: string) { } + + constructor(private startCharacter: string, private endCharacter: string, start?: number, end?: number) { + super('', '', start, end); + } +} + +export class ObjectBoundParameterDeclaration extends BoundParameterDeclaration { + constructor(start?: number, end?: number) { + super('{', '}', start, end); + } +} + +export class ArrayBoundParameterDeclaration extends BoundParameterDeclaration { + constructor(start?: number, end?: number) { + super('[', ']', start, end); + } +} diff --git a/src/node-parser/function-parser.ts b/src/node-parser/function-parser.ts index 70f6f88..fb10c5d 100644 --- a/src/node-parser/function-parser.ts +++ b/src/node-parser/function-parser.ts @@ -1,12 +1,14 @@ import { - ArrayBindingPattern, FunctionDeclaration, Identifier, + isTupleTypeNode, + isTypeLiteralNode, + isTypeReferenceNode, MethodDeclaration, MethodSignature, Node, - ObjectBindingPattern, ParameterDeclaration, + PropertySignature, SyntaxKind, VariableStatement, } from 'typescript'; @@ -15,9 +17,18 @@ import { ConstructorDeclaration as TshConstructor } from '../declarations/Constr import { DefaultDeclaration as TshDefault } from '../declarations/DefaultDeclaration'; import { FunctionDeclaration as TshFunction } from '../declarations/FunctionDeclaration'; import { MethodDeclaration as TshMethod } from '../declarations/MethodDeclaration'; -import { ParameterDeclaration as TshParameter } from '../declarations/ParameterDeclaration'; +import { + ArrayBoundParameterDeclaration, + ObjectBoundParameterDeclaration, + ParameterDeclaration as TshParameter, +} from '../declarations/ParameterDeclaration'; import { Resource } from '../resources/Resource'; -import { isArrayBindingPattern, isIdentifier, isObjectBindingPattern } from '../type-guards/TypescriptGuards'; +import { + isArrayBindingPattern, + isIdentifier, + isObjectBindingPattern, + isPropertySignature, +} from '../type-guards/TypescriptGuards'; import { parseIdentifier } from './identifier-parser'; import { getDefaultResourceIdentifier, getNodeType, isNodeDefaultExported, isNodeExported } from './parse-utilities'; import { parseVariable } from './variable-parser'; @@ -63,22 +74,51 @@ export function parseMethodParams( ): TshParameter[] { return node.parameters.reduce( (all: TshParameter[], cur: ParameterDeclaration) => { - let params = all; + const params = all; if (isIdentifier(cur.name)) { params.push(new TshParameter( (cur.name as Identifier).text, getNodeType(cur.type), cur.getStart(), cur.getEnd(), )); - } else if (isObjectBindingPattern(cur.name) || isArrayBindingPattern(cur.name)) { - const identifiers = cur.name as ObjectBindingPattern | ArrayBindingPattern; - const elements = [...identifiers.elements]; - // TODO: BindingElement - params = params.concat(elements.map((o: any) => { - if (isIdentifier(o.name)) { - return new TshParameter( - (o.name as Identifier).text, undefined, o.getStart(), o.getEnd(), - ); - } - }).filter(Boolean)); + } else if (isObjectBindingPattern(cur.name)) { + const elements = cur.name.elements; + let types: (string | undefined)[] = []; + const boundParam = new ObjectBoundParameterDeclaration(cur.getStart(), cur.getEnd()); + + if (cur.type && isTypeReferenceNode(cur.type)) { + boundParam.typeReference = getNodeType(cur.type); + } else if (cur.type && isTypeLiteralNode(cur.type)) { + types = cur.type.members + .filter(member => isPropertySignature(member)) + .map((signature: any) => getNodeType((signature as PropertySignature).type)); + } + + boundParam.parameters = elements.map((bindingElement, index) => new TshParameter( + bindingElement.name.getText(), + types[index], + bindingElement.getStart(), + bindingElement.getEnd(), + )); + + params.push(boundParam); + } else if (isArrayBindingPattern(cur.name)) { + const elements = cur.name.elements; + let types: (string | undefined)[] = []; + const boundParam = new ArrayBoundParameterDeclaration(cur.getStart(), cur.getEnd()); + + if (cur.type && isTypeReferenceNode(cur.type)) { + boundParam.typeReference = getNodeType(cur.type); + } else if (cur.type && isTupleTypeNode(cur.type)) { + types = cur.type.elementTypes.map(type => getNodeType(type)); + } + + boundParam.parameters = elements.map((bindingElement, index) => new TshParameter( + bindingElement.getText(), + types[index], + bindingElement.getStart(), + bindingElement.getEnd(), + )); + + params.push(boundParam); } return params; }, diff --git a/test/TypescriptParser.spec.ts b/test/TypescriptParser.spec.ts index 92555dd..c552d6d 100644 --- a/test/TypescriptParser.spec.ts +++ b/test/TypescriptParser.spec.ts @@ -253,6 +253,91 @@ describe('TypescriptParser', () => { }); + describe('Parameters', () => { + + const file = getWorkspaceFile('typescript-parser/parameters.ts'); + let parsed: Resource; + + beforeEach(async () => { + parsed = await parser.parseFile(file, rootPath); + }); + + it('should parse a normal parameter', () => { + const func = parsed.declarations[0] as FunctionDeclaration; + expect(func.parameters[0]).toMatchSnapshot(); + }); + + it('should parse a simple array binding pattern', () => { + const func = parsed.declarations[1] as FunctionDeclaration; + expect(func.parameters[0]).toMatchSnapshot(); + }); + + it('should parse an array with tuple type', () => { + const func = parsed.declarations[2] as FunctionDeclaration; + expect(func.parameters[0]).toMatchSnapshot(); + }); + + it('should parse an array with undertyped tuple type', () => { + const func = parsed.declarations[3] as FunctionDeclaration; + expect(func.parameters[0]).toMatchSnapshot(); + }); + + it('should parse an array with overtyped tuple type', () => { + const func = parsed.declarations[4] as FunctionDeclaration; + expect(func.parameters[0]).toMatchSnapshot(); + }); + + it('should parse a simple object binding pattern ', () => { + const func = parsed.declarations[5] as FunctionDeclaration; + expect(func.parameters[0]).toMatchSnapshot(); + }); + + it('should parse an object with type reference', () => { + const func = parsed.declarations[6] as FunctionDeclaration; + expect(func.parameters[0]).toMatchSnapshot(); + }); + + it('should parse an object with type literal', () => { + const func = parsed.declarations[7] as FunctionDeclaration; + expect(func.parameters[0]).toMatchSnapshot(); + }); + + it('should parse an object with undertyped type literal', () => { + const func = parsed.declarations[8] as FunctionDeclaration; + expect(func.parameters[0]).toMatchSnapshot(); + }); + + it('should parse an object with overtyped type literal', () => { + const func = parsed.declarations[9] as FunctionDeclaration; + expect(func.parameters[0]).toMatchSnapshot(); + }); + + it('should parse some mixed parameters (all above)', () => { + expect(parsed.declarations[10]).toMatchSnapshot(); + }); + + it('should generate the correct name for an object', () => { + const func = parsed.declarations[9] as FunctionDeclaration; + expect(func.parameters[0].name).toMatchSnapshot(); + }); + + it('should generate the correct name for an array', () => { + const func = parsed.declarations[2] as FunctionDeclaration; + expect(func.parameters[0].name).toMatchSnapshot(); + }); + + it('should generate the correct type for an object', () => { + const func = parsed.declarations[9] as FunctionDeclaration; + expect(func.parameters[0].type).toMatchSnapshot(); + }); + + it('should generate the correct type for an array', () => { + const func = parsed.declarations[2] as FunctionDeclaration; + expect(func.parameters[0].type).toMatchSnapshot(); + }); + + }); + describe('Variables', () => { const file = getWorkspaceFile('typescript-parser/variable.ts'); diff --git a/test/__snapshots__/TypescriptParser.spec.ts.snap b/test/__snapshots__/TypescriptParser.spec.ts.snap index 5b550ac..6992ce8 100644 --- a/test/__snapshots__/TypescriptParser.spec.ts.snap +++ b/test/__snapshots__/TypescriptParser.spec.ts.snap @@ -237,23 +237,31 @@ Array [ "isAbstract": false, "name": "objMethod", "parameters": Array [ - ParameterDeclaration { - "end": 1225, - "name": "p1", - "start": 1223, - "type": undefined, - }, - ParameterDeclaration { - "end": 1229, - "name": "p2", - "start": 1227, - "type": undefined, - }, - ParameterDeclaration { - "end": 1233, - "name": "p3", - "start": 1231, - "type": undefined, + ObjectBoundParameterDeclaration { + "end": 1240, + "endCharacter": "}", + "parameters": Array [ + ParameterDeclaration { + "end": 1225, + "name": "p1", + "start": 1223, + "type": undefined, + }, + ParameterDeclaration { + "end": 1229, + "name": "p2", + "start": 1227, + "type": undefined, + }, + ParameterDeclaration { + "end": 1233, + "name": "p3", + "start": 1231, + "type": undefined, + }, + ], + "start": 1221, + "startCharacter": "{", }, ], "start": 1204, @@ -266,23 +274,31 @@ Array [ "isAbstract": false, "name": "arrMethod", "parameters": Array [ - ParameterDeclaration { - "end": 1277, - "name": "p1", - "start": 1275, - "type": undefined, - }, - ParameterDeclaration { - "end": 1281, - "name": "p2", - "start": 1279, - "type": undefined, - }, - ParameterDeclaration { - "end": 1285, - "name": "p3", - "start": 1283, - "type": undefined, + ArrayBoundParameterDeclaration { + "end": 1296, + "endCharacter": "]", + "parameters": Array [ + ParameterDeclaration { + "end": 1277, + "name": "p1", + "start": 1275, + "type": undefined, + }, + ParameterDeclaration { + "end": 1281, + "name": "p2", + "start": 1279, + "type": undefined, + }, + ParameterDeclaration { + "end": 1285, + "name": "p3", + "start": 1283, + "type": undefined, + }, + ], + "start": 1274, + "startCharacter": "[", }, ], "start": 1257, @@ -295,35 +311,51 @@ Array [ "isAbstract": false, "name": "objAndArrMethod", "parameters": Array [ - ParameterDeclaration { - "end": 1339, - "name": "p1", - "start": 1337, - "type": undefined, - }, - ParameterDeclaration { - "end": 1343, - "name": "p2", - "start": 1341, - "type": undefined, - }, - ParameterDeclaration { - "end": 1347, - "name": "p3", - "start": 1345, - "type": undefined, - }, - ParameterDeclaration { - "end": 1364, - "name": "p4", - "start": 1362, - "type": undefined, + ArrayBoundParameterDeclaration { + "end": 1358, + "endCharacter": "]", + "parameters": Array [ + ParameterDeclaration { + "end": 1339, + "name": "p1", + "start": 1337, + "type": undefined, + }, + ParameterDeclaration { + "end": 1343, + "name": "p2", + "start": 1341, + "type": undefined, + }, + ParameterDeclaration { + "end": 1347, + "name": "p3", + "start": 1345, + "type": undefined, + }, + ], + "start": 1336, + "startCharacter": "[", }, - ParameterDeclaration { - "end": 1368, - "name": "p5", - "start": 1366, - "type": undefined, + ObjectBoundParameterDeclaration { + "end": 1375, + "endCharacter": "}", + "parameters": Array [ + ParameterDeclaration { + "end": 1364, + "name": "p4", + "start": 1362, + "type": undefined, + }, + ParameterDeclaration { + "end": 1368, + "name": "p5", + "start": 1366, + "type": undefined, + }, + ], + "start": 1360, + "startCharacter": "{", }, ], "start": 1313, @@ -438,29 +470,45 @@ FunctionDeclaration { "start": 112, "type": "string", }, - ParameterDeclaration { - "end": 139, - "name": "objParam1", - "start": 130, - "type": undefined, - }, - ParameterDeclaration { - "end": 150, - "name": "objParam2", - "start": 141, - "type": undefined, - }, - ParameterDeclaration { - "end": 164, - "name": "arrParam1", - "start": 155, - "type": undefined, + ObjectBoundParameterDeclaration { + "end": 152, + "endCharacter": "}", + "parameters": Array [ + ParameterDeclaration { + "end": 139, + "name": "objParam1", + "start": 130, + "type": undefined, + }, + ParameterDeclaration { + "end": 150, + "name": "objParam2", + "start": 141, + "type": undefined, + }, + ], + "start": 128, + "startCharacter": "{", }, - ParameterDeclaration { - "end": 175, - "name": "arrParam2", - "start": 166, - "type": undefined, + ArrayBoundParameterDeclaration { + "end": 176, + "endCharacter": "]", + "parameters": Array [ + ParameterDeclaration { + "end": 164, + "name": "arrParam1", + "start": 155, + "type": undefined, + }, + ParameterDeclaration { + "end": 175, + "name": "arrParam2", + "start": 166, + "type": undefined, + }, + ], + "start": 154, + "startCharacter": "[", }, ], "start": 86, @@ -497,29 +545,45 @@ Array [ "start": 112, "type": "string", }, - ParameterDeclaration { - "end": 139, - "name": "objParam1", - "start": 130, - "type": undefined, - }, - ParameterDeclaration { - "end": 150, - "name": "objParam2", - "start": 141, - "type": undefined, - }, - ParameterDeclaration { - "end": 164, - "name": "arrParam1", - "start": 155, - "type": undefined, + ObjectBoundParameterDeclaration { + "end": 152, + "endCharacter": "}", + "parameters": Array [ + ParameterDeclaration { + "end": 139, + "name": "objParam1", + "start": 130, + "type": undefined, + }, + ParameterDeclaration { + "end": 150, + "name": "objParam2", + "start": 141, + "type": undefined, + }, + ], + "start": 128, + "startCharacter": "{", }, - ParameterDeclaration { - "end": 175, - "name": "arrParam2", - "start": 166, - "type": undefined, + ArrayBoundParameterDeclaration { + "end": 176, + "endCharacter": "]", + "parameters": Array [ + ParameterDeclaration { + "end": 164, + "name": "arrParam1", + "start": 155, + "type": undefined, + }, + ParameterDeclaration { + "end": 175, + "name": "arrParam2", + "start": 166, + "type": undefined, + }, + ], + "start": 154, + "startCharacter": "[", }, ] `; @@ -616,17 +680,25 @@ InterfaceDeclaration { "isAbstract": true, "name": "method1", "parameters": Array [ - ParameterDeclaration { - "end": 235, - "name": "param1", - "start": 229, - "type": undefined, - }, - ParameterDeclaration { - "end": 243, - "name": "param2", - "start": 237, - "type": undefined, + ObjectBoundParameterDeclaration { + "end": 245, + "endCharacter": "}", + "parameters": Array [ + ParameterDeclaration { + "end": 235, + "name": "param1", + "start": 229, + "type": undefined, + }, + ParameterDeclaration { + "end": 243, + "name": "param2", + "start": 237, + "type": undefined, + }, + ], + "start": 227, + "startCharacter": "{", }, ], "start": 219, @@ -639,17 +711,25 @@ InterfaceDeclaration { "isAbstract": true, "name": "method2", "parameters": Array [ - ParameterDeclaration { - "end": 267, - "name": "param1", - "start": 261, - "type": undefined, - }, - ParameterDeclaration { - "end": 275, - "name": "param2", - "start": 269, - "type": undefined, + ArrayBoundParameterDeclaration { + "end": 276, + "endCharacter": "]", + "parameters": Array [ + ParameterDeclaration { + "end": 267, + "name": "param1", + "start": 261, + "type": undefined, + }, + ParameterDeclaration { + "end": 275, + "name": "param2", + "start": 269, + "type": undefined, + }, + ], + "start": 260, + "startCharacter": "[", }, ], "start": 252, @@ -739,6 +819,290 @@ Namespace { } `; +exports[`TypescriptParser Declaration parsing Parameters should generate the correct name for an array 1`] = `"[ p1, p2 ]"`; + +exports[`TypescriptParser Declaration parsing Parameters should generate the correct name for an object 1`] = `"{ p1, p2 }"`; + +exports[`TypescriptParser Declaration parsing Parameters should generate the correct type for an array 1`] = `"{ string, Type }"`; + +exports[`TypescriptParser Declaration parsing Parameters should generate the correct type for an object 1`] = `"{ string, number }"`; + +exports[`TypescriptParser Declaration parsing Parameters should parse a normal parameter 1`] = ` +ParameterDeclaration { + "end": 26, + "name": "p1", + "start": 16, + "type": "string", +} +`; + +exports[`TypescriptParser Declaration parsing Parameters should parse a simple array binding pattern 1`] = ` +ArrayBoundParameterDeclaration { + "end": 66, + "endCharacter": "]", + "parameters": Array [ + ParameterDeclaration { + "end": 61, + "name": "p1", + "start": 59, + "type": undefined, + }, + ParameterDeclaration { + "end": 65, + "name": "p2", + "start": 63, + "type": undefined, + }, + ], + "start": 58, + "startCharacter": "[", +} +`; + +exports[`TypescriptParser Declaration parsing Parameters should parse a simple object binding pattern 1`] = ` +ObjectBoundParameterDeclaration { + "end": 281, + "endCharacter": "}", + "parameters": Array [ + ParameterDeclaration { + "end": 275, + "name": "p1", + "start": 273, + "type": undefined, + }, + ParameterDeclaration { + "end": 279, + "name": "p2", + "start": 277, + "type": undefined, + }, + ], + "start": 271, + "startCharacter": "{", +} +`; + +exports[`TypescriptParser Declaration parsing Parameters should parse an array with overtyped tuple type 1`] = ` +ArrayBoundParameterDeclaration { + "end": 239, + "endCharacter": "]", + "parameters": Array [ + ParameterDeclaration { + "end": 207, + "name": "p1", + "start": 205, + "type": "string", + }, + ParameterDeclaration { + "end": 211, + "name": "p2", + "start": 209, + "type": "number", + }, + ], + "start": 204, + "startCharacter": "[", +} +`; + +exports[`TypescriptParser Declaration parsing Parameters should parse an array with tuple type 1`] = ` +ArrayBoundParameterDeclaration { + "end": 122, + "endCharacter": "]", + "parameters": Array [ + ParameterDeclaration { + "end": 101, + "name": "p1", + "start": 99, + "type": "string", + }, + ParameterDeclaration { + "end": 105, + "name": "p2", + "start": 103, + "type": "Type", + }, + ], + "start": 98, + "startCharacter": "[", +} +`; + +exports[`TypescriptParser Declaration parsing Parameters should parse an array with undertyped tuple type 1`] = ` +ArrayBoundParameterDeclaration { + "end": 172, + "endCharacter": "]", + "parameters": Array [ + ParameterDeclaration { + "end": 157, + "name": "p1", + "start": 155, + "type": "string", + }, + ParameterDeclaration { + "end": 161, + "name": "p2", + "start": 159, + "type": undefined, + }, + ], + "start": 154, + "startCharacter": "[", +} +`; + +exports[`TypescriptParser Declaration parsing Parameters should parse an object with overtyped type literal 1`] = ` +ObjectBoundParameterDeclaration { + "end": 538, + "endCharacter": "}", + "parameters": Array [ + ParameterDeclaration { + "end": 491, + "name": "p1", + "start": 489, + "type": "string", + }, + ParameterDeclaration { + "end": 495, + "name": "p2", + "start": 493, + "type": "number", + }, + ], + "start": 487, + "startCharacter": "{", +} +`; + +exports[`TypescriptParser Declaration parsing Parameters should parse an object with type literal 1`] = ` +ObjectBoundParameterDeclaration { + "end": 397, + "endCharacter": "}", + "parameters": Array [ + ParameterDeclaration { + "end": 365, + "name": "p1", + "start": 363, + "type": "string", + }, + ParameterDeclaration { + "end": 369, + "name": "p2", + "start": 367, + "type": "Type", + }, + ], + "start": 361, + "startCharacter": "{", +} +`; + +exports[`TypescriptParser Declaration parsing Parameters should parse an object with type reference 1`] = ` +ObjectBoundParameterDeclaration { + "end": 329, + "endCharacter": "}", + "parameters": Array [ + ParameterDeclaration { + "end": 317, + "name": "p1", + "start": 315, + "type": undefined, + }, + ParameterDeclaration { + "end": 321, + "name": "p2", + "start": 319, + "type": undefined, + }, + ], + "start": 313, + "startCharacter": "{", + "typeReference": "Type", +} +`; + +exports[`TypescriptParser Declaration parsing Parameters should parse an object with undertyped type literal 1`] = ` +ObjectBoundParameterDeclaration { + "end": 455, + "endCharacter": "}", + "parameters": Array [ + ParameterDeclaration { + "end": 433, + "name": "p1", + "start": 431, + "type": "string", + }, + ParameterDeclaration { + "end": 437, + "name": "p2", + "start": 435, + "type": undefined, + }, + ], + "start": 429, + "startCharacter": "{", +} +`; + +exports[`TypescriptParser Declaration parsing Parameters should parse some mixed parameters (all above) 1`] = ` +FunctionDeclaration { + "end": 654, + "isExported": false, + "name": "mixed", + "parameters": Array [ + ParameterDeclaration { + "end": 576, + "name": "p1", + "start": 566, + "type": "string", + }, + ObjectBoundParameterDeclaration { + "end": 616, + "endCharacter": "}", + "parameters": Array [ + ParameterDeclaration { + "end": 582, + "name": "p2", + "start": 580, + "type": "string", + }, + ParameterDeclaration { + "end": 586, + "name": "p3", + "start": 584, + "type": "number", + }, + ], + "start": 578, + "startCharacter": "{", + }, + ArrayBoundParameterDeclaration { + "end": 643, + "endCharacter": "]", + "parameters": Array [ + ParameterDeclaration { + "end": 621, + "name": "p4", + "start": 619, + "type": "Type", + }, + ParameterDeclaration { + "end": 625, + "name": "p5", + "start": 623, + "type": "boolean", + }, + ], + "start": 618, + "startCharacter": "[", + }, + ], + "start": 551, + "type": "void", + "variables": Array [], +} +`; + exports[`TypescriptParser Declaration parsing Type aliases should parse a type alias correctly 1`] = ` TypeAliasDeclaration { "end": 16, diff --git a/test/_workspace/typescript-parser/parameters.ts b/test/_workspace/typescript-parser/parameters.ts new file mode 100644 index 0000000..21f823c --- /dev/null +++ b/test/_workspace/typescript-parser/parameters.ts @@ -0,0 +1,21 @@ +function normal(p1: string): void { } + +function arrBound1([p1, p2]): void { } + +function arrBound2([p1, p2]: [string, Type]): void { } + +function arrBound3([p1, p2]: [string]): void { } + +function arrBound4([p1, p2]: [string, number, boolean]): void { } + +function objBound1({ p1, p2 }): void { } + +function objBound2({ p1, p2 }: Type): void { } + +function objBound3({ p1, p2 }: { p1: string, p2: Type }): void { } + +function objBound4({ p1, p2 }: { p1: string }): void { } + +function objBound5({ p1, p2 }: { p1: string, p2: number, p3: boolean }): void { } + +function mixed(p1: string, { p2, p3 }: { p2: string, p3: number }, [p4, p5]: [Type, boolean]): void { }