From fa9a914648ea0e528a1a3398c1f47fc37e676efc Mon Sep 17 00:00:00 2001 From: Tingan Ho Date: Fri, 5 Jun 2015 16:36:02 +0800 Subject: [PATCH] Adds error for non-return positioned type predicates and changed parse type predicate logic --- src/compiler/checker.ts | 10 ++++-- .../diagnosticInformationMap.generated.ts | 2 +- src/compiler/diagnosticMessages.json | 2 +- src/compiler/parser.ts | 36 ++++++++----------- src/compiler/types.ts | 1 - .../typeGuardFunctionErrors.errors.txt | 23 +++++++++--- .../reference/typeGuardFunctionErrors.js | 16 ++++++++- .../typeGuards/typeGuardFunctionErrors.ts | 7 ++++ 8 files changed, 65 insertions(+), 32 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 02e20888c9a..72bfc80ac49 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8623,8 +8623,7 @@ module ts { let typePredicateNode = node.type; if (typePredicateNode.type.kind === SyntaxKind.TypePredicate) { error(typePredicateNode.type, - Diagnostics.Cannot_define_type_predicate_0_as_a_type_to_a_type_predicate, - getTextOfNode(typePredicateNode.type)); + Diagnostics.Type_predicates_are_only_allowed_in_return_type_position); } else { if (typePredicate.parameterIndex >= 0) { @@ -11303,6 +11302,13 @@ module ts { return checkAccessorDeclaration(node); case SyntaxKind.TypeReference: return checkTypeReferenceNode(node); + case SyntaxKind.TypePredicate: + // Issue an error every time we encounter a type predicate. They are only allowed + // in return type positions in signature declarations. checkSignatureDeclaration(..) + // already have a specific check for type predicates, so every time we encounter a type + // predicate in checkSourceElement it must be in a non return type position. + error(node, Diagnostics.Type_predicates_are_only_allowed_in_return_type_position); + return; case SyntaxKind.TypeQuery: return checkTypeQuery(node); case SyntaxKind.TypeLiteral: diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index a949c5c034e..1457b52ba70 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -183,7 +183,7 @@ module ts { Cannot_find_parameter_0: { code: 1225, category: DiagnosticCategory.Error, key: "Cannot find parameter '{0}'." }, Type_predicate_0_is_not_assignable_to_1: { code: 1226, category: DiagnosticCategory.Error, key: "Type predicate '{0}' is not assignable to '{1}'." }, Parameter_0_is_not_in_the_same_position_as_parameter_1: { code: 1227, category: DiagnosticCategory.Error, key: "Parameter '{0}' is not in the same position as parameter '{1}'." }, - Cannot_define_type_predicate_0_as_a_type_to_a_type_predicate: { code: 1228, category: DiagnosticCategory.Error, key: "Cannot define type predicate '{0}' as a type to a type predicate." }, + Type_predicates_are_only_allowed_in_return_type_position: { code: 1228, category: DiagnosticCategory.Error, key: "Type predicates are only allowed in return type position." }, Duplicate_identifier_0: { code: 2300, category: DiagnosticCategory.Error, key: "Duplicate identifier '{0}'." }, Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor: { code: 2301, category: DiagnosticCategory.Error, key: "Initializer of instance member variable '{0}' cannot reference identifier '{1}' declared in the constructor." }, Static_members_cannot_reference_class_type_parameters: { code: 2302, category: DiagnosticCategory.Error, key: "Static members cannot reference class type parameters." }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index a3e4e8c6e52..3c323f1402b 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -719,7 +719,7 @@ "category": "Error", "code": 1227 }, - "Cannot define type predicate '{0}' as a type to a type predicate.": { + "Type predicates are only allowed in return type position.": { "category": "Error", "code": 1228 }, diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 9a1ec116680..86c7ee98a06 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1900,9 +1900,17 @@ module ts { // TYPES - function parseTypeReference(): TypeReferenceNode { - let node = createNode(SyntaxKind.TypeReference); - node.typeName = parseEntityName(/*allowReservedWords*/ false, Diagnostics.Type_expected); + function parseTypeReferenceOrTypePredicate(): TypeReferenceNode | TypePredicateNode { + let typeName = parseEntityName(/*allowReservedWords*/ false, Diagnostics.Type_expected); + if (typeName.kind === SyntaxKind.Identifier && token === SyntaxKind.IsKeyword) { + nextToken(); + let node = createNode(SyntaxKind.TypePredicate, typeName.pos); + node.parameterName = typeName; + node.type = parseType(); + return finishNode(node); + } + let node = createNode(SyntaxKind.TypeReference, typeName.pos); + node.typeName = typeName; if (!scanner.hasPrecedingLineBreak() && token === SyntaxKind.LessThanToken) { node.typeArguments = parseBracketedList(ParsingContext.TypeArguments, parseType, SyntaxKind.LessThanToken, SyntaxKind.GreaterThanToken); } @@ -2339,7 +2347,7 @@ module ts { case SyntaxKind.SymbolKeyword: // If these are followed by a dot, then parse these out as a dotted type reference instead. let node = tryParse(parseKeywordAndNoDot); - return node || parseTypeReference(); + return node || parseTypeReferenceOrTypePredicate(); case SyntaxKind.VoidKeyword: return parseTokenNode(); case SyntaxKind.TypeOfKeyword: @@ -2351,7 +2359,7 @@ module ts { case SyntaxKind.OpenParenToken: return parseParenthesizedType(); default: - return parseTypeReference(); + return parseTypeReferenceOrTypePredicate(); } } @@ -2464,22 +2472,6 @@ module ts { return result; } - - function parseTypePredicateOrHigher(): TypeNode { - let type = parseUnionTypeOrHigher(); - if (token === SyntaxKind.IsKeyword && - type.kind === SyntaxKind.TypeReference && - (type).typeName.kind === SyntaxKind.Identifier) { - - nextToken(); - - let typePredicate = createNode(SyntaxKind.TypePredicate, type.pos); - typePredicate.parameterName = (type).typeName; - typePredicate.type = parseType(); - return finishNode(typePredicate); - } - return type; - } function parseTypeWorker(): TypeNode { if (isStartOfFunctionType()) { @@ -2488,7 +2480,7 @@ module ts { if (token === SyntaxKind.NewKeyword) { return parseFunctionOrConstructorType(SyntaxKind.ConstructorType); } - return parseTypePredicateOrHigher(); + return parseUnionTypeOrHigher(); } function parseTypeAnnotation(): TypeNode { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index afdfe651c67..cdcdd924b31 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -464,7 +464,6 @@ module ts { typeParameters?: NodeArray; parameters: NodeArray; type?: TypeNode; - typePredicate?: TypePredicateNode; } // SyntaxKind.VariableDeclaration diff --git a/tests/baselines/reference/typeGuardFunctionErrors.errors.txt b/tests/baselines/reference/typeGuardFunctionErrors.errors.txt index 47479b221aa..0d5065395a9 100644 --- a/tests/baselines/reference/typeGuardFunctionErrors.errors.txt +++ b/tests/baselines/reference/typeGuardFunctionErrors.errors.txt @@ -1,5 +1,5 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(15,12): error TS2322: Type 'string' is not assignable to type 'boolean'. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(18,55): error TS1228: Cannot define type predicate 'x is A' as a type to a type predicate. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(18,55): error TS1228: Type predicates are only allowed in return type position. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(22,33): error TS2304: Cannot find name 'x'. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(26,10): error TS2391: Function implementation is missing or not immediately following the declaration. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(27,5): error TS1131: Property or signature expected. @@ -22,9 +22,12 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(81,1): Parameter 'p2' is not in the same position as parameter 'p1'. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(87,1): error TS2322: Type '(p1: any, p2: any, p3: any) => boolean' is not assignable to type '(p1: any, p2: any) => boolean'. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(92,56): error TS1225: Cannot find parameter 'p1'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(95,9): error TS1228: Type predicates are only allowed in return type position. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(96,16): error TS1228: Type predicates are only allowed in return type position. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(97,20): error TS1228: Type predicates are only allowed in return type position. -==== tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts (18 errors) ==== +==== tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts (21 errors) ==== class A { propA: number; @@ -46,7 +49,7 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(92,56) function hasTypeGuardTypeInsideTypeGuardType(x): x is x is A { ~~~~~~ -!!! error TS1228: Cannot define type predicate 'x is A' as a type to a type predicate. +!!! error TS1228: Type predicates are only allowed in return type position. return true; } @@ -159,4 +162,16 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(92,56) declare function destructureParameter({ p1, p2, p3 }): p1 is A; ~~ !!! error TS1225: Cannot find parameter 'p1'. - \ No newline at end of file + + // Type predicates in non-return type positions + var b1: b is A; + ~~~~~~ +!!! error TS1228: Type predicates are only allowed in return type position. + function b2(a: b is A) {}; + ~~~~~~ +!!! error TS1228: Type predicates are only allowed in return type position. + function b3(): A | b is A { + ~~~~~~ +!!! error TS1228: Type predicates are only allowed in return type position. + return true; + }; \ No newline at end of file diff --git a/tests/baselines/reference/typeGuardFunctionErrors.js b/tests/baselines/reference/typeGuardFunctionErrors.js index 84dd534f9d2..0617cc1495a 100644 --- a/tests/baselines/reference/typeGuardFunctionErrors.js +++ b/tests/baselines/reference/typeGuardFunctionErrors.js @@ -91,7 +91,13 @@ assign3 = function(p1, p2, p3): p1 is A { // Type guard paramater referring to a binding pattern declare function destructureParameter({ p1, p2, p3 }): p1 is A; - + +// Type predicates in non-return type positions +var b1: b is A; +function b2(a: b is A) {}; +function b3(): A | b is A { + return true; +}; //// [typeGuardFunctionErrors.js] var __extends = (this && this.__extends) || function (d, b) { @@ -169,3 +175,11 @@ var assign3; assign3 = function (p1, p2, p3) { return true; }; +// Type predicates in non-return type positions +var b1; +function b2(a) { } +; +function b3() { + return true; +} +; diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts index 28a512aca6d..baae822ec2d 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts @@ -90,3 +90,10 @@ assign3 = function(p1, p2, p3): p1 is A { // Type guard paramater referring to a binding pattern declare function destructureParameter({ p1, p2, p3 }): p1 is A; + +// Type predicates in non-return type positions +var b1: b is A; +function b2(a: b is A) {}; +function b3(): A | b is A { + return true; +}; \ No newline at end of file