diff --git a/common/define-grammar.js b/common/define-grammar.js index efb0b76a..8f5c065b 100644 --- a/common/define-grammar.js +++ b/common/define-grammar.js @@ -694,7 +694,7 @@ module.exports = function defineGrammar(dialect) { _type_query_member_expression_in_type_annotation: $ => seq( field('object', choice( $.import, - alias($._type_query_member_expression_in_type_annotation, $.member_expression), + prec.left(1, alias($._type_query_member_expression_in_type_annotation, $.member_expression)), alias($._type_query_call_expression_in_type_annotation, $.call_expression), )), '.', @@ -847,7 +847,7 @@ module.exports = function defineGrammar(dialect) { $.identifier, $.this, alias($._type_query_subscript_expression, $.subscript_expression), - alias($._type_query_member_expression, $.member_expression), + prec.left(1, alias($._type_query_member_expression, $.member_expression)), alias($._type_query_call_expression, $.call_expression), )), choice('.', '?.'), @@ -860,7 +860,7 @@ module.exports = function defineGrammar(dialect) { field('object', choice( $.identifier, $.this, - alias($._type_query_subscript_expression, $.subscript_expression), + prec.left(1, alias($._type_query_subscript_expression, $.subscript_expression)), alias($._type_query_member_expression, $.member_expression), alias($._type_query_call_expression, $.call_expression), )), diff --git a/test/corpus/type-query-regression.txt b/test/corpus/type-query-regression.txt new file mode 100644 index 00000000..d1657ee4 --- /dev/null +++ b/test/corpus/type-query-regression.txt @@ -0,0 +1,256 @@ +================== +Type query with ReturnType and typeof (regression test) +================== + +interface LoggerInstance { + debug: ReturnType; + info: ReturnType; + warn: ReturnType; + error: ReturnType; +} + +--- + +(program + (interface_declaration + name: (type_identifier) + body: (interface_body + (property_signature + name: (property_identifier) + type: (type_annotation + (generic_type + name: (type_identifier) + type_arguments: (type_arguments + (type_query + (member_expression + object: (identifier) + property: (property_identifier))))))) + (property_signature + name: (property_identifier) + type: (type_annotation + (generic_type + name: (type_identifier) + type_arguments: (type_arguments + (type_query + (member_expression + object: (identifier) + property: (property_identifier))))))) + (property_signature + name: (property_identifier) + type: (type_annotation + (generic_type + name: (type_identifier) + type_arguments: (type_arguments + (type_query + (member_expression + object: (identifier) + property: (property_identifier))))))) + (property_signature + name: (property_identifier) + type: (type_annotation + (generic_type + name: (type_identifier) + type_arguments: (type_arguments + (type_query + (member_expression + object: (identifier) + property: (property_identifier)))))))))) + +================== +Complex nested member expressions +================== + +type T1 = typeof obj.prop.nested; +type T2 = typeof this.method.result; + +--- + +(program + (type_alias_declaration + name: (type_identifier) + value: (type_query + (member_expression + object: (member_expression + object: (identifier) + property: (property_identifier)) + property: (property_identifier)))) + (type_alias_declaration + name: (type_identifier) + value: (type_query + (member_expression + object: (member_expression + object: (this) + property: (property_identifier)) + property: (property_identifier))))) + +================== +Nested object access +================== + +type Obj = typeof foo.bar.baz.qux; + +--- + +(program + (type_alias_declaration + name: (type_identifier) + value: (type_query + (member_expression + object: (member_expression + object: (member_expression + object: (identifier) + property: (property_identifier)) + property: (property_identifier)) + property: (property_identifier))))) + +================== +Optional chaining in type queries +================== + +type T = typeof obj?.prop; + +--- + +(program + (type_alias_declaration + name: (type_identifier) + value: (type_query + (member_expression + object: (identifier) + property: (property_identifier))))) + +================== +Array subscript in type queries +================== + +type T = typeof arr[0]; + +--- + +(program + (type_alias_declaration + name: (type_identifier) + value: (lookup_type + (type_query + (identifier)) + (literal_type + (number))))) + +================== +Call expression with import in type query +================== + +type T = typeof import("module").default; + +--- + +(program + (type_alias_declaration + name: (type_identifier) + value: (type_query + (member_expression + object: (call_expression + function: (import) + arguments: (arguments + (string + (string_fragment)))) + property: (property_identifier))))) + +================== +Real-world vitest mock interface (full reproduction) +================== + +interface LoggerInstance { + debug: ReturnType; + info: ReturnType; + warn: ReturnType; + error: ReturnType; +} + +const mockLogger: LoggerInstance = { + debug: vi.fn(), + info: vi.fn(), + warn: vi.fn(), + error: vi.fn(), +}; + +--- + +(program + (interface_declaration + name: (type_identifier) + body: (interface_body + (property_signature + name: (property_identifier) + type: (type_annotation + (generic_type + name: (type_identifier) + type_arguments: (type_arguments + (type_query + (member_expression + object: (identifier) + property: (property_identifier))))))) + (property_signature + name: (property_identifier) + type: (type_annotation + (generic_type + name: (type_identifier) + type_arguments: (type_arguments + (type_query + (member_expression + object: (identifier) + property: (property_identifier))))))) + (property_signature + name: (property_identifier) + type: (type_annotation + (generic_type + name: (type_identifier) + type_arguments: (type_arguments + (type_query + (member_expression + object: (identifier) + property: (property_identifier))))))) + (property_signature + name: (property_identifier) + type: (type_annotation + (generic_type + name: (type_identifier) + type_arguments: (type_arguments + (type_query + (member_expression + object: (identifier) + property: (property_identifier))))))))) + (lexical_declaration + (variable_declarator + name: (identifier) + type: (type_annotation + (type_identifier)) + value: (object + (pair + key: (property_identifier) + value: (call_expression + function: (member_expression + object: (identifier) + property: (property_identifier)) + arguments: (arguments))) + (pair + key: (property_identifier) + value: (call_expression + function: (member_expression + object: (identifier) + property: (property_identifier)) + arguments: (arguments))) + (pair + key: (property_identifier) + value: (call_expression + function: (member_expression + object: (identifier) + property: (property_identifier)) + arguments: (arguments))) + (pair + key: (property_identifier) + value: (call_expression + function: (member_expression + object: (identifier) + property: (property_identifier)) + arguments: (arguments))))))) diff --git a/tsx/src/grammar.json b/tsx/src/grammar.json index 794ae1e5..b106eadd 100644 --- a/tsx/src/grammar.json +++ b/tsx/src/grammar.json @@ -8940,13 +8940,17 @@ "name": "import" }, { - "type": "ALIAS", + "type": "PREC_LEFT", + "value": 1, "content": { - "type": "SYMBOL", - "name": "_type_query_member_expression_in_type_annotation" - }, - "named": true, - "value": "member_expression" + "type": "ALIAS", + "content": { + "type": "SYMBOL", + "name": "_type_query_member_expression_in_type_annotation" + }, + "named": true, + "value": "member_expression" + } }, { "type": "ALIAS", @@ -9658,13 +9662,17 @@ "value": "subscript_expression" }, { - "type": "ALIAS", + "type": "PREC_LEFT", + "value": 1, "content": { - "type": "SYMBOL", - "name": "_type_query_member_expression" - }, - "named": true, - "value": "member_expression" + "type": "ALIAS", + "content": { + "type": "SYMBOL", + "name": "_type_query_member_expression" + }, + "named": true, + "value": "member_expression" + } }, { "type": "ALIAS", @@ -9733,13 +9741,17 @@ "name": "this" }, { - "type": "ALIAS", + "type": "PREC_LEFT", + "value": 1, "content": { - "type": "SYMBOL", - "name": "_type_query_subscript_expression" - }, - "named": true, - "value": "subscript_expression" + "type": "ALIAS", + "content": { + "type": "SYMBOL", + "name": "_type_query_subscript_expression" + }, + "named": true, + "value": "subscript_expression" + } }, { "type": "ALIAS", diff --git a/typescript/src/grammar.json b/typescript/src/grammar.json index 3ac060d9..ba651a8d 100644 --- a/typescript/src/grammar.json +++ b/typescript/src/grammar.json @@ -8940,13 +8940,17 @@ "name": "import" }, { - "type": "ALIAS", + "type": "PREC_LEFT", + "value": 1, "content": { - "type": "SYMBOL", - "name": "_type_query_member_expression_in_type_annotation" - }, - "named": true, - "value": "member_expression" + "type": "ALIAS", + "content": { + "type": "SYMBOL", + "name": "_type_query_member_expression_in_type_annotation" + }, + "named": true, + "value": "member_expression" + } }, { "type": "ALIAS", @@ -9658,13 +9662,17 @@ "value": "subscript_expression" }, { - "type": "ALIAS", + "type": "PREC_LEFT", + "value": 1, "content": { - "type": "SYMBOL", - "name": "_type_query_member_expression" - }, - "named": true, - "value": "member_expression" + "type": "ALIAS", + "content": { + "type": "SYMBOL", + "name": "_type_query_member_expression" + }, + "named": true, + "value": "member_expression" + } }, { "type": "ALIAS", @@ -9733,13 +9741,17 @@ "name": "this" }, { - "type": "ALIAS", + "type": "PREC_LEFT", + "value": 1, "content": { - "type": "SYMBOL", - "name": "_type_query_subscript_expression" - }, - "named": true, - "value": "subscript_expression" + "type": "ALIAS", + "content": { + "type": "SYMBOL", + "name": "_type_query_subscript_expression" + }, + "named": true, + "value": "subscript_expression" + } }, { "type": "ALIAS",