mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Better error message for unparenthesized function/constructor type notation in union/intersection types (#39570)
* add graceful error message for unparenthesized function types in union and intersection * add unparenthesizedFunctionTypeInUnionOrIntersection test * add unparenthesizedConstructorTypeInUnionOrIntersection test * Update src/compiler/parser.ts Co-authored-by: Daniel Rosenwasser <DanielRosenwasser@users.noreply.github.com> * pass isInUnionType to parseFunctionOrConstructorTypeToError * Apply suggestions from code review Co-authored-by: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> * syntax fix * refactor isStartOfFunctionType into isStartOfFunctionTypeOrConstructorType * Update src/compiler/parser.ts Co-authored-by: Daniel Rosenwasser <DanielRosenwasser@users.noreply.github.com> * hoist isUnionType Co-authored-by: Daniel Rosenwasser <DanielRosenwasser@users.noreply.github.com> Co-authored-by: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
This commit is contained in:
co-authored by
Daniel Rosenwasser
Nathan Shively-Sanders
parent
37e6e2761c
commit
b6f09ccf06
+36
-5
@@ -3569,18 +3569,46 @@ namespace ts {
|
||||
return parsePostfixTypeOrHigher();
|
||||
}
|
||||
|
||||
function parseFunctionOrConstructorTypeToError(
|
||||
isInUnionType: boolean
|
||||
): TypeNode | undefined {
|
||||
// the function type and constructor type shorthand notation
|
||||
// are not allowed directly in unions and intersections, but we'll
|
||||
// try to parse them gracefully and issue a helpful message.
|
||||
if (isStartOfFunctionTypeOrConstructorType()) {
|
||||
const type = parseFunctionOrConstructorType();
|
||||
let diagnostic: DiagnosticMessage;
|
||||
if (isFunctionTypeNode(type)) {
|
||||
diagnostic = isInUnionType
|
||||
? Diagnostics.Function_type_notation_must_be_parenthesized_when_used_in_a_union_type
|
||||
: Diagnostics.Function_type_notation_must_be_parenthesized_when_used_in_an_intersection_type;
|
||||
}
|
||||
else {
|
||||
diagnostic = isInUnionType
|
||||
? Diagnostics.Constructor_type_notation_must_be_parenthesized_when_used_in_a_union_type
|
||||
: Diagnostics.Constructor_type_notation_must_be_parenthesized_when_used_in_an_intersection_type;
|
||||
|
||||
}
|
||||
parseErrorAtRange(type, diagnostic);
|
||||
return type;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function parseUnionOrIntersectionType(
|
||||
operator: SyntaxKind.BarToken | SyntaxKind.AmpersandToken,
|
||||
parseConstituentType: () => TypeNode,
|
||||
createTypeNode: (types: NodeArray<TypeNode>) => UnionOrIntersectionTypeNode
|
||||
): TypeNode {
|
||||
const pos = getNodePos();
|
||||
const isUnionType = operator === SyntaxKind.BarToken;
|
||||
const hasLeadingOperator = parseOptional(operator);
|
||||
let type = parseConstituentType();
|
||||
let type = hasLeadingOperator && parseFunctionOrConstructorTypeToError(isUnionType)
|
||||
|| parseConstituentType();
|
||||
if (token() === operator || hasLeadingOperator) {
|
||||
const types = [type];
|
||||
while (parseOptional(operator)) {
|
||||
types.push(parseConstituentType());
|
||||
types.push(parseFunctionOrConstructorTypeToError(isUnionType) || parseConstituentType());
|
||||
}
|
||||
type = finishNode(createTypeNode(createNodeArray(types, pos)), pos);
|
||||
}
|
||||
@@ -3595,11 +3623,14 @@ namespace ts {
|
||||
return parseUnionOrIntersectionType(SyntaxKind.BarToken, parseIntersectionTypeOrHigher, factory.createUnionTypeNode);
|
||||
}
|
||||
|
||||
function isStartOfFunctionType(): boolean {
|
||||
function isStartOfFunctionTypeOrConstructorType(): boolean {
|
||||
if (token() === SyntaxKind.LessThanToken) {
|
||||
return true;
|
||||
}
|
||||
return token() === SyntaxKind.OpenParenToken && lookAhead(isUnambiguouslyStartOfFunctionType);
|
||||
if (token() === SyntaxKind.OpenParenToken && lookAhead(isUnambiguouslyStartOfFunctionType)) {
|
||||
return true;
|
||||
}
|
||||
return token() === SyntaxKind.NewKeyword;
|
||||
}
|
||||
|
||||
function skipParameterStart(): boolean {
|
||||
@@ -3684,7 +3715,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function parseTypeWorker(noConditionalTypes?: boolean): TypeNode {
|
||||
if (isStartOfFunctionType() || token() === SyntaxKind.NewKeyword) {
|
||||
if (isStartOfFunctionTypeOrConstructorType()) {
|
||||
return parseFunctionOrConstructorType();
|
||||
}
|
||||
const pos = getNodePos();
|
||||
|
||||
Reference in New Issue
Block a user