From c9ee51136efdcb53fd01bb935f19f35f548330aa Mon Sep 17 00:00:00 2001 From: Andrew Leppard Date: Mon, 26 Feb 2024 06:29:55 +1030 Subject: [PATCH] Upgraded supported TypeScript version to v2.7. (#3978) * Added object function properties. * Added yield*. * Upgraded to TypeScript v2.7. * Added another example. --------- Co-authored-by: Andrew Leppard --- javascript/javascript/JavaScriptLexer.g4 | 3 +- javascript/javascript/JavaScriptParser.g4 | 9 +- .../javascript/examples/ObjectInitializer.js | 6 + javascript/jsx/JavaScriptLexer.g4 | 1 + javascript/jsx/JavaScriptParser.g4 | 2 +- javascript/typescript/TypeScriptLexer.g4 | 53 ++++++- javascript/typescript/TypeScriptParser.g4 | 136 +++++++++++------- javascript/typescript/examples/Enum.ts | 5 + javascript/typescript/examples/Export.ts | 10 +- javascript/typescript/examples/Function.ts | 10 +- javascript/typescript/examples/Generic.ts | 7 +- javascript/typescript/examples/Loop.ts | 4 + .../typescript/examples/NonNullAssertOp.js | 4 +- .../typescript/examples/ObjectInitializer.ts | 7 + javascript/typescript/examples/Type.ts | 19 ++- 15 files changed, 207 insertions(+), 69 deletions(-) create mode 100644 javascript/typescript/examples/Enum.ts diff --git a/javascript/javascript/JavaScriptLexer.g4 b/javascript/javascript/JavaScriptLexer.g4 index 6c4f3381b7..1ed7b1b9d9 100644 --- a/javascript/javascript/JavaScriptLexer.g4 +++ b/javascript/javascript/JavaScriptLexer.g4 @@ -169,6 +169,8 @@ Try : 'try'; As : 'as'; From : 'from'; Of : 'of'; +Yield : 'yield'; +YieldStar : 'yield*'; /// Future Reserved Words @@ -182,7 +184,6 @@ Import : 'import'; Async : 'async'; Await : 'await'; -Yield : 'yield'; /// The following tokens are also considered to be FutureReservedWords /// when parsing strict mode diff --git a/javascript/javascript/JavaScriptParser.g4 b/javascript/javascript/JavaScriptParser.g4 index 0ddd9e2305..e615e29487 100644 --- a/javascript/javascript/JavaScriptParser.g4 +++ b/javascript/javascript/JavaScriptParser.g4 @@ -200,7 +200,7 @@ returnStatement ; yieldStatement - : Yield ({this.notLineTerminator()}? expressionSequence)? eos + : (Yield | YieldStar) ({this.notLineTerminator()}? expressionSequence)? eos ; withStatement @@ -314,8 +314,9 @@ arrayLiteral : ('[' elementList ']') ; +// JavaScript supports arrasys like [,,1,2,,]. elementList - : ','* arrayElement? (','+ arrayElement)* ','* // Yes, everything is optional + : ','* arrayElement? (','+ arrayElement) * ','* // Yes, everything is optional ; arrayElement @@ -420,7 +421,8 @@ objectLiteral ; anonymousFunction - : Async? Function_ '*'? '(' formalParameterList? ')' functionBody # AnonymousFunctionDecl + : functionDeclaration # NamedFunction + | Async? Function_ '*'? '(' formalParameterList? ')' functionBody # AnonymousFunctionDecl | Async? arrowFunctionParameters '=>' arrowFunctionBody # ArrowFunction ; @@ -556,6 +558,7 @@ keyword | Protected | Static | Yield + | YieldStar | Async | Await | From diff --git a/javascript/javascript/examples/ObjectInitializer.js b/javascript/javascript/examples/ObjectInitializer.js index c3bd499395..6a5dfb7f48 100644 --- a/javascript/javascript/examples/ObjectInitializer.js +++ b/javascript/javascript/examples/ObjectInitializer.js @@ -9,4 +9,10 @@ obj = { }; obj = { item1: "item1", item2: "item2" }; obj = { item1: "item1", item2: "item2", }; +obj = { item1: "item1", + item2: "item2", + item3: function(arg1) { return arg1; }, + item4: function myFunction(arg1) { return arg1; }, + item5: (arg1) => { return arg1; } + }; diff --git a/javascript/jsx/JavaScriptLexer.g4 b/javascript/jsx/JavaScriptLexer.g4 index 8bd90f963c..64b91007b9 100644 --- a/javascript/jsx/JavaScriptLexer.g4 +++ b/javascript/jsx/JavaScriptLexer.g4 @@ -161,6 +161,7 @@ In : 'in'; Try : 'try'; As : 'as'; From : 'from'; +YieldStar : 'yield*'; /// Future Reserved Words diff --git a/javascript/jsx/JavaScriptParser.g4 b/javascript/jsx/JavaScriptParser.g4 index 0b0cf841e1..7e1d449cda 100644 --- a/javascript/jsx/JavaScriptParser.g4 +++ b/javascript/jsx/JavaScriptParser.g4 @@ -177,7 +177,7 @@ returnStatement ; yieldStatement - : Yield ({this.notLineTerminator()}? expressionSequence)? eos + : (Yield | YieldStar) ({this.notLineTerminator()}? expressionSequence)? eos ; withStatement diff --git a/javascript/typescript/TypeScriptLexer.g4 b/javascript/typescript/TypeScriptLexer.g4 index f9e0b3db55..b15a8d411d 100644 --- a/javascript/typescript/TypeScriptLexer.g4 +++ b/javascript/typescript/TypeScriptLexer.g4 @@ -1,3 +1,35 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014 by Bart Kiers (original author) and Alexandre Vitorelli (contributor -> ported to CSharp) + * Copyright (c) 2017 by Ivan Kochurkin (Positive Technologies): + added ECMAScript 6 support, cleared and transformed to the universal grammar. + * Copyright (c) 2018 by Juan Alvarez (contributor -> ported to Go) + * Copyright (c) 2019 by Andrii Artiushok (contributor -> added TypeScript support) + * Copyright (c) 2024 by Andrew Leppard (www.wegrok.review) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + // $antlr-format alignTrailingComments true, columnLimit 150, maxEmptyLinesToKeep 1, reflowComments false, useTab false // $antlr-format allowShortRulesOnASingleLine true, allowShortBlocksOnASingleLine true, minEmptyLines 0, alignSemicolons ownLine // $antlr-format alignColons trailing, singleLineOverrulesHangingColon true, alignLexerCommands true, alignLabels true, alignTrailers true @@ -139,6 +171,8 @@ From : 'from'; ReadOnly : 'readonly'; Async : 'async'; Await : 'await'; +Yield : 'yield'; +YieldStar : 'yield*'; /// Future Reserved Words @@ -161,15 +195,20 @@ Interface : 'interface'; Package : 'package'; Protected : 'protected'; Static : 'static'; -Yield : 'yield'; //keywords: - -Any : 'any'; -Number : 'number'; -Boolean : 'boolean'; -String : 'string'; -Symbol : 'symbol'; +Any : 'any'; +Number : 'number'; +Never : 'never'; +Boolean : 'boolean'; +String : 'string'; +Unique : 'unique'; +Symbol : 'symbol'; +Undefined : 'undefined'; +Object : 'object'; + +Of : 'of'; +KeyOf : 'keyof'; TypeAlias: 'type'; diff --git a/javascript/typescript/TypeScriptParser.g4 b/javascript/typescript/TypeScriptParser.g4 index 68a9a564e4..c9f2d76a9a 100644 --- a/javascript/typescript/TypeScriptParser.g4 +++ b/javascript/typescript/TypeScriptParser.g4 @@ -61,7 +61,8 @@ typeParameterList ; typeParameter - : Identifier constraint? + : identifier constraint? + | identifier '=' typeArgument | typeParameters ; @@ -81,12 +82,13 @@ typeArgument : type_ ; +// Union and intersection types can have a leading '|' or '&' +// See https://github.com/microsoft/TypeScript/pull/12386 type_ - : unionOrIntersectionOrPrimaryType + : ('|' | '&')? unionOrIntersectionOrPrimaryType | functionType | constructorType | typeGeneric - | StringLiteral ; unionOrIntersectionOrPrimaryType @@ -100,20 +102,27 @@ primaryType | predefinedType # PredefinedPrimType | typeReference # ReferencePrimType | objectType # ObjectPrimType - | primaryType {notLineTerminator()}? '[' ']' # ArrayPrimType + | primaryType {notLineTerminator()}? '[' primaryType? ']' # ArrayPrimType | '[' tupleElementTypes ']' # TuplePrimType | typeQuery # QueryPrimType | This # ThisPrimType | typeReference Is primaryType # RedefinitionOfType + | KeyOf primaryType # KeyOfType ; predefinedType : Any | NullLiteral | Number + | DecimalLiteral | Boolean + | BooleanLiteral | String - | Symbol + | StringLiteral + | Unique? Symbol + | Never + | Undefined + | Object | Void ; @@ -126,7 +135,7 @@ typeGeneric ; typeName - : Identifier + : identifier | namespaceName ; @@ -158,8 +167,9 @@ tupleType : '[' tupleElementTypes ']' ; +// Tuples can have a trailing comma. See https://github.com/Microsoft/TypeScript/issues/28893 tupleElementTypes - : type_ (',' type_)* + : type_ (',' type_)* ','? ; functionType @@ -175,7 +185,7 @@ typeQuery ; typeQueryExpression - : Identifier + : identifier | (identifierName '.')+ identifierName ; @@ -191,9 +201,11 @@ callSignature : typeParameters? '(' parameterList? ')' typeAnnotation? ; +// Function parameter list can have a trailing comma. +// See https://github.com/Microsoft/TypeScript/issues/16152 parameterList : restParameter - | parameter (',' parameter)* (',' restParameter)? + | parameter (',' parameter)* (',' restParameter)? ','? ; requiredParameterList @@ -238,7 +250,7 @@ constructSignature ; indexSignature - : '[' Identifier ':' (Number | String) ']' typeAnnotation + : '[' identifier ':' (Number | String) ']' typeAnnotation ; methodSignature @@ -246,7 +258,7 @@ methodSignature ; typeAliasDeclaration - : 'type' Identifier typeParameters? '=' type_ SemiColon + : Export? 'type' identifier typeParameters? '=' type_ eos ; constructorDeclaration @@ -259,7 +271,7 @@ constructorDeclaration // A.5 Interface interfaceDeclaration - : Export? Declare? Interface Identifier typeParameters? interfaceExtendsClause? objectType SemiColon? + : Export? Declare? Interface identifier typeParameters? interfaceExtendsClause? objectType SemiColon? ; interfaceExtendsClause @@ -273,7 +285,7 @@ classOrInterfaceTypeList // A.7 Interface enumDeclaration - : Const? Enum Identifier '{' enumBody? '}' + : Const? Enum identifier '{' enumBody? '}' ; enumBody @@ -291,15 +303,15 @@ enumMember // A.8 Namespaces namespaceDeclaration - : Namespace namespaceName '{' statementList? '}' + : Declare? Namespace namespaceName '{' statementList? '}' ; namespaceName - : Identifier ('.'+ Identifier)* + : identifier ('.'+ identifier)* ; importAliasDeclaration - : Identifier '=' namespaceName SemiColon + : identifier '=' namespaceName SemiColon ; // Ext.2 Additions to 1.8: Decorators @@ -313,7 +325,7 @@ decorator ; decoratorMemberExpression - : Identifier + : identifier | decoratorMemberExpression '.' identifierName | '(' singleExpression ')' ; @@ -371,7 +383,7 @@ statementList ; abstractDeclaration - : Abstract (Identifier callSignature | variableStatement) eos + : Abstract (identifier callSignature | variableStatement) eos ; importStatement @@ -478,8 +490,8 @@ iterationStatement | For '(' varModifier variableDeclarationList SemiColon expressionSequence? SemiColon expressionSequence? ')' statement # ForVarStatement | For '(' singleExpression In expressionSequence ')' statement # ForInStatement | For '(' varModifier variableDeclaration In expressionSequence ')' statement # ForVarInStatement - | For Await? '(' singleExpression Identifier {this.p("of")}? expressionSequence ')' statement # ForOfStatement - | For Await? '(' varModifier variableDeclaration Identifier {this.p("of")}? expressionSequence ')' statement # ForVarOfStatement + | For Await? '(' singleExpression identifier {this.p("of")}? expressionSequence (As type_)? ')' statement # ForOfStatement + | For Await? '(' varModifier variableDeclaration identifier {this.p("of")}? expressionSequence (As type_)? ')' statement # ForVarOfStatement ; varModifier @@ -489,11 +501,11 @@ varModifier ; continueStatement - : Continue ({this.notLineTerminator()}? Identifier)? eos + : Continue ({this.notLineTerminator()}? identifier)? eos ; breakStatement - : Break ({this.notLineTerminator()}? Identifier)? eos + : Break ({this.notLineTerminator()}? identifier)? eos ; returnStatement @@ -501,7 +513,7 @@ returnStatement ; yieldStatement - : Yield ({this.notLineTerminator()}? expressionSequence)? eos + : (Yield | YieldStar) ({this.notLineTerminator()}? expressionSequence)? eos ; withStatement @@ -529,7 +541,7 @@ defaultClause ; labelledStatement - : Identifier ':' statement + : identifier ':' statement ; throwStatement @@ -541,7 +553,7 @@ tryStatement ; catchProduction - : Catch ('(' Identifier typeAnnotation? ')')? block + : Catch ('(' identifier typeAnnotation? ')')? block ; finallyProduction @@ -553,12 +565,12 @@ debuggerStatement ; functionDeclaration - : Async? Function_ Identifier callSignature (('{' functionBody '}') | SemiColon) + : Async? Function_ identifier callSignature (('{' functionBody '}') | SemiColon) ; //Ovveride ECMA classDeclaration - : decoratorList? (Export Default?)? Abstract? Class Identifier typeParameters? classHeritage classTail + : decoratorList? (Export Default?)? Abstract? Class identifier typeParameters? classHeritage classTail ; classHeritage @@ -601,11 +613,11 @@ indexMemberDeclaration ; generatorMethod - : (Async {this.notLineTerminator()}?)? '*'? Identifier '(' formalParameterList? ')' '{' functionBody '}' + : (Async {this.notLineTerminator()}?)? '*'? identifier '(' formalParameterList? ')' '{' functionBody '}' ; generatorFunctionDeclaration - : Async? Function_ '*' Identifier? '(' formalParameterList? ')' '{' functionBody '}' + : Async? Function_ '*' identifier? '(' formalParameterList? ')' '{' functionBody '}' ; generatorBlock @@ -634,7 +646,7 @@ privateIdentifier ; formalParameterList - : formalParameterArg (',' formalParameterArg)* (',' lastFormalParameterArg)? + : formalParameterArg (',' formalParameterArg)* (',' lastFormalParameterArg)? ','? | lastFormalParameterArg | arrayLiteral // ECMAScript 6: Parameter Context Matching | objectLiteral (':' formalParameterList)? // ECMAScript 6: Parameter Context Matching @@ -647,7 +659,7 @@ formalParameterArg ; lastFormalParameterArg // ECMAScript 6: Rest Parameter - : Ellipsis Identifier typeAnnotation? + : Ellipsis identifier typeAnnotation? ; functionBody @@ -659,15 +671,16 @@ sourceElements ; arrayLiteral - : ('[' elementList? ']') + : ('[' elementList ']') ; +// JavaScript supports arrasys like [,,1,2,,]. elementList - : arrayElement (','+ arrayElement)* + : ','* arrayElement? (','+ arrayElement) * ','* // Yes, everything is optional ; arrayElement // ECMAScript 6: Spread Operator - : Ellipsis? (singleExpression | Identifier) ','? + : Ellipsis? (singleExpression | identifier) ','? ; objectLiteral @@ -691,7 +704,7 @@ getAccessor ; setAccessor - : setter '(' (Identifier | bindingPattern) typeAnnotation? ')' '{' functionBody '}' + : setter '(' (identifier | bindingPattern) typeAnnotation? ')' '{' functionBody '}' ; propertyName @@ -710,21 +723,16 @@ argumentList ; argument // ECMAScript 6: Spread Operator - : Ellipsis? (singleExpression | Identifier) + : Ellipsis? (singleExpression | identifier) ; expressionSequence : singleExpression (',' singleExpression)* ; -functionExpressionDeclaration - : Function_ Identifier? '(' formalParameterList? ')' typeAnnotation? '{' functionBody '}' - ; - singleExpression - : functionExpressionDeclaration # FunctionExpression - | arrowFunctionDeclaration # ArrowFunctionExpression // ECMAScript 6 - | Class Identifier? typeParameters? classHeritage classTail # ClassExpression + : anonymousFunction # FunctionExpression + | Class identifier? typeParameters? classHeritage classTail # ClassExpression | singleExpression '?.'? '[' expressionSequence ']' # MemberIndexExpression | singleExpression '?.' singleExpression # OptionalChainExpression | singleExpression '!'? '.' '#'? identifierName typeGeneric? # MemberDotExpression @@ -776,6 +784,8 @@ singleExpression | '(' expressionSequence ')' # ParenthesizedExpression | typeArguments expressionSequence? # GenericTypes | singleExpression As asExpression # CastAsExpression +// TypeScript v2.0 + | singleExpression '!' # NonNullAssertionExpression ; asExpression @@ -783,12 +793,18 @@ asExpression | singleExpression ; +anonymousFunction + : functionDeclaration + | Async? Function_ '*'? '(' formalParameterList? ')' typeAnnotation? '{' functionBody '}' + | arrowFunctionDeclaration + ; + arrowFunctionDeclaration : Async? arrowFunctionParameters typeAnnotation? '=>' arrowFunctionBody ; arrowFunctionParameters - : Identifier + : identifier | '(' formalParameterList? ')' ; @@ -849,20 +865,44 @@ bigintLiteral ; getter - : {this.n("get")}? Identifier classElementName + : {this.n("get")}? identifier classElementName ; setter - : {this.n("set")}? Identifier classElementName + : {this.n("set")}? identifier classElementName ; identifierName - : Identifier + : identifier | reservedWord ; -identifierOrKeyWord +identifier : Identifier + | Async + | As + | From + | Yield + | Of + | Any + | Any + | Number + | Boolean + | String + | Unique + | Symbol + | Never + | Undefined + | Object + | KeyOf + | TypeAlias + | Constructor + | Namespace + | Abstract + ; + +identifierOrKeyWord + : identifier | TypeAlias | Require ; diff --git a/javascript/typescript/examples/Enum.ts b/javascript/typescript/examples/Enum.ts new file mode 100644 index 0000000000..cb61906713 --- /dev/null +++ b/javascript/typescript/examples/Enum.ts @@ -0,0 +1,5 @@ +enum Colors { + Red = "RED", + Green = "GREEN", + Blue = "BLUE" +} diff --git a/javascript/typescript/examples/Export.ts b/javascript/typescript/examples/Export.ts index 9d434cb649..460f9abd49 100644 --- a/javascript/typescript/examples/Export.ts +++ b/javascript/typescript/examples/Export.ts @@ -1,6 +1,3 @@ - - - namespace StringUtility { function ToCapital(str: string): string { @@ -17,4 +14,9 @@ namespace StringUtility export function Eported2(str: string, length: number = 0): string { return str.toUpperCase(); } -} \ No newline at end of file +} + +export type MyType = { + field: number; + field2: string; +} diff --git a/javascript/typescript/examples/Function.ts b/javascript/typescript/examples/Function.ts index 4a04282372..b8b1b7f30a 100644 --- a/javascript/typescript/examples/Function.ts +++ b/javascript/typescript/examples/Function.ts @@ -48,10 +48,16 @@ function buildName(firstName: string, lastName?: string) { else return firstName; } -// Try passing a nested type to the function. This tests we don't match ">>" and ">>>" symbols. +// Try passing a nested type to the function. This tests we don't match ">>" and ">>>" operators +// when closing nested types. function nestedType(map: Map>>) { // Check that we can parse these too. let a = 12; let b = a >> 5; let c = b >>> 5; -} \ No newline at end of file +} + +// Function parameter lists can have a trailing comma. +// See https://github.com/Microsoft/TypeScript/issues/16152 +function TrailingComma(arg1: string, arg2: number,) {} +var myFunction = function(arg1: string, arg2: number,) {}; \ No newline at end of file diff --git a/javascript/typescript/examples/Generic.ts b/javascript/typescript/examples/Generic.ts index c9fd76c689..ab5f14b6a7 100644 --- a/javascript/typescript/examples/Generic.ts +++ b/javascript/typescript/examples/Generic.ts @@ -27,4 +27,9 @@ function displayNames(names:T[]): void { function display(per: T): void { console.log(`${ per.firstName} ${per.lastName}` ); -} \ No newline at end of file +} + +function genericWithKeyOf(list: T[], field: K): T[] {} + +// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-3.html#generic-parameter-defaults +function genericParameterWithDefault(field: T) {} \ No newline at end of file diff --git a/javascript/typescript/examples/Loop.ts b/javascript/typescript/examples/Loop.ts index 971b1ab635..462410ea2c 100644 --- a/javascript/typescript/examples/Loop.ts +++ b/javascript/typescript/examples/Loop.ts @@ -15,4 +15,8 @@ for await (const document of array) { for await (const document: string of array) { +}; + +for (let [field, value] of Object.entries(object) as [string, any]) { + }; \ No newline at end of file diff --git a/javascript/typescript/examples/NonNullAssertOp.js b/javascript/typescript/examples/NonNullAssertOp.js index 418caa4891..e7e207b084 100644 --- a/javascript/typescript/examples/NonNullAssertOp.js +++ b/javascript/typescript/examples/NonNullAssertOp.js @@ -11,7 +11,9 @@ function processEntity(e?: Entity) { let s = e!.name; let t = e.name; let o = e!.obj!.i; - let p = e?.name; + let p = e?.name; + + let i = p!; } var e = null; diff --git a/javascript/typescript/examples/ObjectInitializer.ts b/javascript/typescript/examples/ObjectInitializer.ts index 50a4f6d073..2866acabde 100644 --- a/javascript/typescript/examples/ObjectInitializer.ts +++ b/javascript/typescript/examples/ObjectInitializer.ts @@ -10,3 +10,10 @@ let obj = { }; obj = { item1: "item1", item2: "item2" }; obj = { item1: "item1", item2: "item2", }; +// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#constant-named-properties +const Foo = "Foo"; +const Bar = "Bar"; +let x = { + [Foo]: 100, + [Bar]: "hello" +}; diff --git a/javascript/typescript/examples/Type.ts b/javascript/typescript/examples/Type.ts index 4c6f2ac96d..8698e68610 100644 --- a/javascript/typescript/examples/Type.ts +++ b/javascript/typescript/examples/Type.ts @@ -1,10 +1,25 @@ // TypeDefinition + +// TypeAlias type Employee = { + type: "employee" | "manager"; + typeId: 1 | 2; id: string; name: string; address?: string; // Optional phone?: string | null; -}; +} + +// https://github.com/microsoft/TypeScript/pull/12386 +type EmployeeType = + | "employee" + | "manager"; + +type EmployeeNameType = Employee["name"]; + +// keyof +type EmployeeMap = Map; +type EmployeeMapKey = keyof EmployeeMap; // TypeAnotation var age: number = 32; // number variable @@ -22,6 +37,8 @@ employee1 = { } var employee2: Employee = { + type: "employee", + typeId: 1, id: 101, name: "Steve" }