From af7df838253b7b8489fcd9137e8f4220786d2be5 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 8 Dec 2015 08:57:10 -0800 Subject: [PATCH 01/10] Parse type predicates only in return types. --- src/compiler/parser.ts | 53 +++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index e4262458d30..10d4a47c3df 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -908,17 +908,19 @@ namespace ts { return result; } - // Invokes the provided callback then unconditionally restores the parser to the state it - // was in immediately prior to invoking the callback. The result of invoking the callback - // is returned from this function. + /** Invokes the provided callback then unconditionally restores the parser to the state it + * was in immediately prior to invoking the callback. The result of invoking the callback + * is returned from this function. + */ function lookAhead(callback: () => T): T { return speculationHelper(callback, /*isLookAhead*/ true); } - // Invokes the provided callback. If the callback returns something falsy, then it restores - // the parser to the state it was in immediately prior to invoking the callback. If the - // callback returns something truthy, then the parser state is not rolled back. The result - // of invoking the callback is returned from this function. + /** Invokes the provided callback. If the callback returns something falsy, then it restores + * the parser to the state it was in immediately prior to invoking the callback. If the + * callback returns something truthy, then the parser state is not rolled back. The result + * of invoking the callback is returned from this function. + */ function tryParse(callback: () => T): T { return speculationHelper(callback, /*isLookAhead*/ false); } @@ -1960,15 +1962,8 @@ namespace ts { // TYPES - function parseTypeReferenceOrTypePredicate(): TypeReferenceNode | TypePredicateNode { + function parseTypeReference(): TypeReferenceNode { const typeName = parseEntityName(/*allowReservedWords*/ false, Diagnostics.Type_expected); - if (typeName.kind === SyntaxKind.Identifier && token === SyntaxKind.IsKeyword && !scanner.hasPrecedingLineBreak()) { - nextToken(); - const node = createNode(SyntaxKind.TypePredicate, typeName.pos); - node.parameterName = typeName; - node.type = parseType(); - return finishNode(node); - } const node = createNode(SyntaxKind.TypeReference, typeName.pos); node.typeName = typeName; if (!scanner.hasPrecedingLineBreak() && token === SyntaxKind.LessThanToken) { @@ -2100,10 +2095,10 @@ namespace ts { if (returnTokenRequired) { parseExpected(returnToken); - signature.type = parseType(); + signature.type = parseTypeOrTypePredicate(); } else if (parseOptional(returnToken)) { - signature.type = parseType(); + signature.type = parseTypeOrTypePredicate(); } } @@ -2419,7 +2414,7 @@ namespace ts { case SyntaxKind.SymbolKeyword: // If these are followed by a dot, then parse these out as a dotted type reference instead. const node = tryParse(parseKeywordAndNoDot); - return node || parseTypeReferenceOrTypePredicate(); + return node || parseTypeReference(); case SyntaxKind.StringLiteral: return parseStringLiteralTypeNode(); case SyntaxKind.VoidKeyword: @@ -2435,7 +2430,7 @@ namespace ts { case SyntaxKind.OpenParenToken: return parseParenthesizedType(); default: - return parseTypeReferenceOrTypePredicate(); + return parseTypeReference(); } } @@ -2541,6 +2536,26 @@ namespace ts { } return false; } + + function parseTypeOrTypePredicate(): TypeNode { + const typePredicateVariable = tryParse(() => { + const id = parseIdentifier(); + if (token === SyntaxKind.IsKeyword && !scanner.hasPrecedingLineBreak()) { + nextToken(); + return id; + } + }); + const t = parseType(); + if(typePredicateVariable) { + const node = createNode(SyntaxKind.TypePredicate, typePredicateVariable.pos); + node.parameterName = typePredicateVariable; + node.type = t; + return finishNode(node); + } + else { + return t; + } + } function parseType(): TypeNode { // The rules about 'yield' only apply to actual code/expression contexts. They don't From fd311d4e274251208c9dc795b4d071f6ffebf822 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 8 Dec 2015 09:11:37 -0800 Subject: [PATCH 02/10] Fix lint --- src/compiler/parser.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 10d4a47c3df..1c41003a7a1 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -2536,7 +2536,7 @@ namespace ts { } return false; } - + function parseTypeOrTypePredicate(): TypeNode { const typePredicateVariable = tryParse(() => { const id = parseIdentifier(); @@ -2546,7 +2546,7 @@ namespace ts { } }); const t = parseType(); - if(typePredicateVariable) { + if (typePredicateVariable) { const node = createNode(SyntaxKind.TypePredicate, typePredicateVariable.pos); node.parameterName = typePredicateVariable; node.type = t; From 1b63040d3654585d075bb0f69fc0f5aae41fc20b Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 8 Dec 2015 09:13:41 -0800 Subject: [PATCH 03/10] Add tests and accept baselines --- .../reference/typeAssertions.errors.txt | 52 ++++++++++++++++++- tests/baselines/reference/typeAssertions.js | 22 ++++++++ .../typeAssertions/typeAssertions.ts | 9 ++++ 3 files changed, 82 insertions(+), 1 deletion(-) diff --git a/tests/baselines/reference/typeAssertions.errors.txt b/tests/baselines/reference/typeAssertions.errors.txt index a2ad28801c8..c7401e4fcf9 100644 --- a/tests/baselines/reference/typeAssertions.errors.txt +++ b/tests/baselines/reference/typeAssertions.errors.txt @@ -7,9 +7,23 @@ tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(37,13): err Property 'q' is missing in type 'SomeDerived'. tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(38,13): error TS2352: Neither type 'SomeBase' nor type 'SomeOther' is assignable to the other. Property 'q' is missing in type 'SomeBase'. +tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(44,5): error TS2304: Cannot find name 'numOrStr'. +tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(44,14): error TS1005: '>' expected. +tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(44,14): error TS2304: Cannot find name 'is'. +tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(44,17): error TS1005: ')' expected. +tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(44,17): error TS2304: Cannot find name 'string'. +tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(44,48): error TS1005: ';' expected. +tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(45,2): error TS2322: Type 'number | string' is not assignable to type 'string'. + Type 'number' is not assignable to type 'string'. +tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(48,32): error TS2304: Cannot find name 'numOrStr'. +tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(48,41): error TS1005: ')' expected. +tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(48,41): error TS2304: Cannot find name 'is'. +tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(48,44): error TS1005: ';' expected. +tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(48,44): error TS2304: Cannot find name 'string'. +tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(48,50): error TS1005: ';' expected. -==== tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts (5 errors) ==== +==== tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts (18 errors) ==== // Function call whose argument is a 1 arg generic function call with explicit type arguments function fn1(t: T) { } function fn2(t: any) { } @@ -64,5 +78,41 @@ tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(38,13): err !!! error TS2352: Property 'q' is missing in type 'SomeBase'. someOther = someOther; + // Type assertion cannot be a type-predicate type + var numOrStr: number | string; + var str: string; + if((numOrStr === undefined)) { // Error + ~~~~~~~~ +!!! error TS2304: Cannot find name 'numOrStr'. + ~~ +!!! error TS1005: '>' expected. + ~~ +!!! error TS2304: Cannot find name 'is'. + ~~~~~~ +!!! error TS1005: ')' expected. + ~~~~~~ +!!! error TS2304: Cannot find name 'string'. + ~ +!!! error TS1005: ';' expected. + str = numOrStr; // Error, no narrowing occurred + ~~~ +!!! error TS2322: Type 'number | string' is not assignable to type 'string'. +!!! error TS2322: Type 'number' is not assignable to type 'string'. + } + + if((numOrStr === undefined) as numOrStr is string) { // Error + ~~~~~~~~ +!!! error TS2304: Cannot find name 'numOrStr'. + ~~ +!!! error TS1005: ')' expected. + ~~ +!!! error TS2304: Cannot find name 'is'. + ~~~~~~ +!!! error TS1005: ';' expected. + ~~~~~~ +!!! error TS2304: Cannot find name 'string'. + ~ +!!! error TS1005: ';' expected. + } \ No newline at end of file diff --git a/tests/baselines/reference/typeAssertions.js b/tests/baselines/reference/typeAssertions.js index 3645e61f2ba..8bdd927c99e 100644 --- a/tests/baselines/reference/typeAssertions.js +++ b/tests/baselines/reference/typeAssertions.js @@ -39,6 +39,15 @@ someOther = someDerived; // Error someOther = someBase; // Error someOther = someOther; +// Type assertion cannot be a type-predicate type +var numOrStr: number | string; +var str: string; +if((numOrStr === undefined)) { // Error + str = numOrStr; // Error, no narrowing occurred +} + +if((numOrStr === undefined) as numOrStr is string) { // Error +} @@ -87,3 +96,16 @@ someDerived = someOther; // Error someOther = someDerived; // Error someOther = someBase; // Error someOther = someOther; +// Type assertion cannot be a type-predicate type +var numOrStr; +var str; +if (is) + string > (numOrStr === undefined); +{ + str = numOrStr; // Error, no narrowing occurred +} +if ((numOrStr === undefined)) + is; +string; +{ +} diff --git a/tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts b/tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts index 2acbbe53789..f30eafb3918 100644 --- a/tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts +++ b/tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts @@ -38,4 +38,13 @@ someOther = someDerived; // Error someOther = someBase; // Error someOther = someOther; +// Type assertion cannot be a type-predicate type +var numOrStr: number | string; +var str: string; +if((numOrStr === undefined)) { // Error + str = numOrStr; // Error, no narrowing occurred +} + +if((numOrStr === undefined) as numOrStr is string) { // Error +} From 47d267cdde0344ba6c0ec18cc871494fb46963bf Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 8 Dec 2015 09:36:46 -0800 Subject: [PATCH 04/10] Move type predicate checking to checkTypePredicate Also remove now-unused "Type predicate is only allowed as a return type" diagnostic. --- src/compiler/checker.ts | 130 +++++++++++++-------------- src/compiler/diagnosticMessages.json | 4 - 2 files changed, 60 insertions(+), 74 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 30d4818592b..66ba4fcee90 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10985,7 +10985,61 @@ namespace ts { return -1; } - function isInLegalTypePredicatePosition(node: Node): boolean { + function checkTypePredicate(node: TypePredicateNode) { + const parent = getTypePredicateParent(node); + if (!parent) { + return; + } + const typePredicate = getSignatureFromDeclaration(parent).typePredicate; + if (typePredicate.parameterIndex >= 0) { + if (parent.parameters[typePredicate.parameterIndex].dotDotDotToken) { + error(node.parameterName, + Diagnostics.A_type_predicate_cannot_reference_a_rest_parameter); + } + else { + checkTypeAssignableTo(typePredicate.type, + getTypeOfNode(parent.parameters[typePredicate.parameterIndex]), + node.type); + } + } + else if (node.parameterName) { + let hasReportedError = false; + for (var param of parent.parameters) { + if (hasReportedError) { + break; + } + if (param.name.kind === SyntaxKind.ObjectBindingPattern || + param.name.kind === SyntaxKind.ArrayBindingPattern) { + + (function checkBindingPattern(pattern: BindingPattern) { + for (const element of pattern.elements) { + if (element.name.kind === SyntaxKind.Identifier && + (element.name).text === typePredicate.parameterName) { + + error(node.parameterName, + Diagnostics.A_type_predicate_cannot_reference_element_0_in_a_binding_pattern, + typePredicate.parameterName); + hasReportedError = true; + break; + } + else if (element.name.kind === SyntaxKind.ArrayBindingPattern || + element.name.kind === SyntaxKind.ObjectBindingPattern) { + + checkBindingPattern(element.name); + } + } + })(param.name); + } + } + if (!hasReportedError) { + error(node.parameterName, + Diagnostics.Cannot_find_parameter_0, + typePredicate.parameterName); + } + } + } + + function getTypePredicateParent(node: Node): SignatureDeclaration { switch (node.parent.kind) { case SyntaxKind.ArrowFunction: case SyntaxKind.CallSignature: @@ -10994,9 +11048,11 @@ namespace ts { case SyntaxKind.FunctionType: case SyntaxKind.MethodDeclaration: case SyntaxKind.MethodSignature: - return node === (node.parent).type; + const parent = node.parent; + if (node === parent.type) { + return parent; + } } - return false; } function checkSignatureDeclaration(node: SignatureDeclaration) { @@ -11015,67 +11071,7 @@ namespace ts { forEach(node.parameters, checkParameter); - if (node.type) { - if (node.type.kind === SyntaxKind.TypePredicate) { - const typePredicate = getSignatureFromDeclaration(node).typePredicate; - const typePredicateNode = node.type; - if (isInLegalTypePredicatePosition(typePredicateNode)) { - if (typePredicate.parameterIndex >= 0) { - if (node.parameters[typePredicate.parameterIndex].dotDotDotToken) { - error(typePredicateNode.parameterName, - Diagnostics.A_type_predicate_cannot_reference_a_rest_parameter); - } - else { - checkTypeAssignableTo(typePredicate.type, - getTypeOfNode(node.parameters[typePredicate.parameterIndex]), - typePredicateNode.type); - } - } - else if (typePredicateNode.parameterName) { - let hasReportedError = false; - for (var param of node.parameters) { - if (hasReportedError) { - break; - } - if (param.name.kind === SyntaxKind.ObjectBindingPattern || - param.name.kind === SyntaxKind.ArrayBindingPattern) { - - (function checkBindingPattern(pattern: BindingPattern) { - for (const element of pattern.elements) { - if (element.name.kind === SyntaxKind.Identifier && - (element.name).text === typePredicate.parameterName) { - - error(typePredicateNode.parameterName, - Diagnostics.A_type_predicate_cannot_reference_element_0_in_a_binding_pattern, - typePredicate.parameterName); - hasReportedError = true; - break; - } - else if (element.name.kind === SyntaxKind.ArrayBindingPattern || - element.name.kind === SyntaxKind.ObjectBindingPattern) { - - checkBindingPattern(element.name); - } - } - })(param.name); - } - } - if (!hasReportedError) { - error(typePredicateNode.parameterName, - Diagnostics.Cannot_find_parameter_0, - typePredicate.parameterName); - } - } - } - else { - error(typePredicateNode, - Diagnostics.A_type_predicate_is_only_allowed_in_return_type_position_for_functions_and_methods); - } - } - else { - checkSourceElement(node.type); - } - } + checkSourceElement(node.type); if (produceDiagnostics) { checkCollisionWithArgumentsInGeneratedCode(node); @@ -14217,12 +14213,6 @@ namespace ts { } } - function checkTypePredicate(node: TypePredicateNode) { - if (!isInLegalTypePredicatePosition(node)) { - error(node, Diagnostics.A_type_predicate_is_only_allowed_in_return_type_position_for_functions_and_methods); - } - } - function checkSourceElement(node: Node): void { if (!node) { return; diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 331568eae22..bc5a252b304 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -711,10 +711,6 @@ "category": "Error", "code": 1227 }, - "A type predicate is only allowed in return type position for functions and methods.": { - "category": "Error", - "code": 1228 - }, "A type predicate cannot reference a rest parameter.": { "category": "Error", "code": 1229 From 3306ee8a91ff8114238d5c990468e21cd0b21257 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 8 Dec 2015 09:39:25 -0800 Subject: [PATCH 05/10] Accept baselines --- .../typeGuardFunctionErrors.errors.txt | 107 +++++++++++++----- .../reference/typeGuardFunctionErrors.js | 16 ++- 2 files changed, 91 insertions(+), 32 deletions(-) diff --git a/tests/baselines/reference/typeGuardFunctionErrors.errors.txt b/tests/baselines/reference/typeGuardFunctionErrors.errors.txt index 92d9d21f0cc..470ad00bcf7 100644 --- a/tests/baselines/reference/typeGuardFunctionErrors.errors.txt +++ b/tests/baselines/reference/typeGuardFunctionErrors.errors.txt @@ -1,15 +1,21 @@ +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(2,7): error TS2300: Duplicate identifier 'A'. 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 TS2304: Cannot find name 'x'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(18,57): error TS1144: '{' or ';' expected. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(18,57): error TS2304: Cannot find name 'is'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(18,60): error TS1005: ';' expected. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(18,62): error TS1005: ';' expected. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(22,33): error TS2304: Cannot find name 'x'. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(26,33): error TS1225: Cannot find parameter 'x'. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(30,10): error TS2391: Function implementation is missing or not immediately following the declaration. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(31,5): error TS1131: Property or signature expected. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(31,5): error TS7027: Unreachable code detected. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(32,1): error TS1128: Declaration or statement expected. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(34,38): error TS1225: Cannot find parameter 'x'. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(38,51): error TS2322: Type 'B' is not assignable to type 'A'. Property 'propA' is missing in type 'B'. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(42,56): error TS2322: Type 'number' is not assignable to type 'string'. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(46,56): error TS2322: Type 'T[]' is not assignable to type 'string'. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(50,1): error TS7027: Unreachable code detected. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(60,7): error TS2339: Property 'propB' does not exist on type 'A'. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(65,7): error TS2339: Property 'propB' does not exist on type 'A'. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(70,7): error TS2339: Property 'propB' does not exist on type 'A'. @@ -22,27 +28,40 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(85,1): Type predicate 'p2 is A' is not assignable to 'p1 is A'. Parameter 'p2' is not in the same position as parameter 'p1'. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(91,1): error TS2322: Type '(p1: any, p2: any, p3: any) => p1 is A' is not assignable to type '(p1: any, p2: any) => p1 is A'. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(96,9): error TS1228: A type predicate is only allowed in return type position for functions and methods. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(97,16): error TS1228: A type predicate is only allowed in return type position for functions and methods. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(98,20): error TS1228: A type predicate is only allowed in return type position for functions and methods. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(104,25): error TS1228: A type predicate is only allowed in return type position for functions and methods. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(96,9): error TS2304: Cannot find name 'b'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(96,11): error TS1005: '=' expected. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(96,11): error TS2304: Cannot find name 'is'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(96,14): error TS1005: ',' expected. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(96,14): error TS2300: Duplicate identifier 'A'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(97,16): error TS2304: Cannot find name 'b'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(97,18): error TS1005: '=' expected. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(97,18): error TS2304: Cannot find name 'is'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(97,21): error TS1005: ',' expected. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(98,20): error TS2304: Cannot find name 'b'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(98,22): error TS1144: '{' or ';' expected. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(98,22): error TS2304: Cannot find name 'is'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(98,25): error TS1005: ';' expected. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(98,27): error TS1005: ';' expected. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(105,16): error TS2322: Type 'boolean' is not assignable to type 'D'. Property 'm1' is missing in type 'Boolean'. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(105,16): error TS2409: Return type of constructor signature must be assignable to the instance type of the class -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(107,20): error TS1228: A type predicate is only allowed in return type position for functions and methods. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(110,20): error TS1228: A type predicate is only allowed in return type position for functions and methods. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(111,16): error TS2408: Setters cannot return a value. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(116,18): error TS1228: A type predicate is only allowed in return type position for functions and methods. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(120,22): error TS1228: A type predicate is only allowed in return type position for functions and methods. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(120,22): error TS2304: Cannot find name 'p1'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(120,25): error TS1005: ';' expected. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(120,25): error TS2304: Cannot find name 'is'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(120,28): error TS1005: ';' expected. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(121,1): error TS1128: Declaration or statement expected. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(124,20): error TS1229: A type predicate cannot reference a rest parameter. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(129,34): error TS1230: A type predicate cannot reference element 'p1' in a binding pattern. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(133,34): error TS1230: A type predicate cannot reference element 'p1' in a binding pattern. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(137,39): error TS1230: A type predicate cannot reference element 'p1' in a binding pattern. -==== tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts (33 errors) ==== +==== tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts (50 errors) ==== class A { + ~ +!!! error TS2300: Duplicate identifier 'A'. propA: number; } @@ -61,6 +80,16 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(137,39 } function hasTypeGuardTypeInsideTypeGuardType(x): x is x is A { + ~ +!!! error TS2304: Cannot find name 'x'. + ~~ +!!! error TS1144: '{' or ';' expected. + ~~ +!!! error TS2304: Cannot find name 'is'. + ~ +!!! error TS1005: ';' expected. + ~ +!!! error TS1005: ';' expected. return true; } @@ -82,6 +111,8 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(137,39 return true; ~~~~~~ !!! error TS1131: Property or signature expected. + ~~~~~~ +!!! error TS7027: Unreachable code detected. } ~ !!! error TS1128: Declaration or statement expected. @@ -112,8 +143,6 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(137,39 } let a: A; - ~~~ -!!! error TS7027: Unreachable code detected. let b: B; declare function isB(p1): p1 is B; @@ -179,22 +208,42 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(137,39 // Type predicates in non-return type positions var b1: b is A; - ~~~~~~ -!!! error TS1228: A type predicate is only allowed in return type position for functions and methods. + ~ +!!! error TS2304: Cannot find name 'b'. + ~~ +!!! error TS1005: '=' expected. + ~~ +!!! error TS2304: Cannot find name 'is'. + ~ +!!! error TS1005: ',' expected. + ~ +!!! error TS2300: Duplicate identifier 'A'. function b2(a: b is A) {}; - ~~~~~~ -!!! error TS1228: A type predicate is only allowed in return type position for functions and methods. + ~ +!!! error TS2304: Cannot find name 'b'. + ~~ +!!! error TS1005: '=' expected. + ~~ +!!! error TS2304: Cannot find name 'is'. + ~ +!!! error TS1005: ',' expected. function b3(): A | b is A { - ~~~~~~ -!!! error TS1228: A type predicate is only allowed in return type position for functions and methods. + ~ +!!! error TS2304: Cannot find name 'b'. + ~~ +!!! error TS1144: '{' or ';' expected. + ~~ +!!! error TS2304: Cannot find name 'is'. + ~ +!!! error TS1005: ';' expected. + ~ +!!! error TS1005: ';' expected. return true; }; // Non-compatiable type predicate positions for signature declarations class D { constructor(p1: A): p1 is C { - ~~~~~~~ -!!! error TS1228: A type predicate is only allowed in return type position for functions and methods. return true; ~~~~ !!! error TS2322: Type 'boolean' is not assignable to type 'D'. @@ -203,13 +252,9 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(137,39 !!! error TS2409: Return type of constructor signature must be assignable to the instance type of the class } get m1(p1: A): p1 is C { - ~~~~~~~ -!!! error TS1228: A type predicate is only allowed in return type position for functions and methods. return true; } set m2(p1: A): p1 is C { - ~~~~~~~ -!!! error TS1228: A type predicate is only allowed in return type position for functions and methods. return true; ~~~~ !!! error TS2408: Setters cannot return a value. @@ -218,15 +263,21 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(137,39 interface I1 { new (p1: A): p1 is C; - ~~~~~~~ -!!! error TS1228: A type predicate is only allowed in return type position for functions and methods. } interface I2 { [index: number]: p1 is C; - ~~~~~~~ -!!! error TS1228: A type predicate is only allowed in return type position for functions and methods. + ~~ +!!! error TS2304: Cannot find name 'p1'. + ~~ +!!! error TS1005: ';' expected. + ~~ +!!! error TS2304: Cannot find name 'is'. + ~ +!!! error TS1005: ';' expected. } + ~ +!!! error TS1128: Declaration or statement expected. // Reference to rest parameter function b4(...a): a is A { diff --git a/tests/baselines/reference/typeGuardFunctionErrors.js b/tests/baselines/reference/typeGuardFunctionErrors.js index 4923f544345..1d1660fe924 100644 --- a/tests/baselines/reference/typeGuardFunctionErrors.js +++ b/tests/baselines/reference/typeGuardFunctionErrors.js @@ -171,7 +171,9 @@ var C = (function (_super) { function hasANonBooleanReturnStatement(x) { return ''; } -function hasTypeGuardTypeInsideTypeGuardType(x) { +is; +A; +{ return true; } function hasMissingIsKeyword() { @@ -224,10 +226,14 @@ assign3 = function (p1, p2, p3) { return true; }; // Type predicates in non-return type positions -var b1; -function b2(a) { } +var b1 = is, A; +function b2(a, A) { + if (a === void 0) { a = is; } +} ; -function b3() { +is; +A; +{ return true; } ; @@ -252,6 +258,8 @@ var D = (function () { }); return D; })(); +is; +C; // Reference to rest parameter function b4() { var a = []; From a4e21d78582a4aa47835f660a683bfbf49351de3 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 8 Dec 2015 12:52:19 -0800 Subject: [PATCH 06/10] Address comments --- src/compiler/checker.ts | 62 ++++++++++++++++++++++------------------- src/compiler/parser.ts | 6 ++-- 2 files changed, 37 insertions(+), 31 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 66ba4fcee90..fed9c026717 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11004,37 +11004,19 @@ namespace ts { } else if (node.parameterName) { let hasReportedError = false; - for (var param of parent.parameters) { - if (hasReportedError) { - break; - } - if (param.name.kind === SyntaxKind.ObjectBindingPattern || - param.name.kind === SyntaxKind.ArrayBindingPattern) { - - (function checkBindingPattern(pattern: BindingPattern) { - for (const element of pattern.elements) { - if (element.name.kind === SyntaxKind.Identifier && - (element.name).text === typePredicate.parameterName) { - - error(node.parameterName, - Diagnostics.A_type_predicate_cannot_reference_element_0_in_a_binding_pattern, - typePredicate.parameterName); - hasReportedError = true; - break; - } - else if (element.name.kind === SyntaxKind.ArrayBindingPattern || - element.name.kind === SyntaxKind.ObjectBindingPattern) { - - checkBindingPattern(element.name); - } - } - })(param.name); + for (const param of parent.parameters) { + if ((param.name.kind === SyntaxKind.ObjectBindingPattern || + param.name.kind === SyntaxKind.ArrayBindingPattern) && + checkBindingPatternForTypePredicateVariable( + param.name, + node.parameterName, + typePredicate.parameterName)) { + hasReportedError = true; + break; } } if (!hasReportedError) { - error(node.parameterName, - Diagnostics.Cannot_find_parameter_0, - typePredicate.parameterName); + error(node.parameterName, Diagnostics.Cannot_find_parameter_0, typePredicate.parameterName); } } } @@ -11055,6 +11037,30 @@ namespace ts { } } + function checkBindingPatternForTypePredicateVariable( + pattern: BindingPattern, + predicateVariableNode: Node, + predicateVariableName: string) { + for (const element of pattern.elements) { + if (element.name.kind === SyntaxKind.Identifier && + (element.name).text === predicateVariableName) { + error(predicateVariableNode, + Diagnostics.A_type_predicate_cannot_reference_element_0_in_a_binding_pattern, + predicateVariableName); + return true; + } + else if (element.name.kind === SyntaxKind.ArrayBindingPattern || + element.name.kind === SyntaxKind.ObjectBindingPattern) { + if (checkBindingPatternForTypePredicateVariable( + element.name, + predicateVariableNode, + predicateVariableName)) { + return true; + } + } + } + } + function checkSignatureDeclaration(node: SignatureDeclaration) { // Grammar checking if (node.kind === SyntaxKind.IndexSignature) { diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 1c41003a7a1..e146e809122 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -2545,15 +2545,15 @@ namespace ts { return id; } }); - const t = parseType(); + const type = parseType(); if (typePredicateVariable) { const node = createNode(SyntaxKind.TypePredicate, typePredicateVariable.pos); node.parameterName = typePredicateVariable; - node.type = t; + node.type = type; return finishNode(node); } else { - return t; + return type; } } From f9846ff2bc2a253a0ff900b508c3cb25164225bb Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 8 Dec 2015 14:11:46 -0800 Subject: [PATCH 07/10] Address comments --- src/compiler/parser.ts | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index e146e809122..f28f82ed401 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -2538,13 +2538,11 @@ namespace ts { } function parseTypeOrTypePredicate(): TypeNode { - const typePredicateVariable = tryParse(() => { - const id = parseIdentifier(); - if (token === SyntaxKind.IsKeyword && !scanner.hasPrecedingLineBreak()) { - nextToken(); - return id; - } - }); + let typePredicateVariable: Identifier; + if (isIdentifier()) { + typePredicateVariable = tryParse(parseTypePredicatePrefix); + } + const type = parseType(); if (typePredicateVariable) { const node = createNode(SyntaxKind.TypePredicate, typePredicateVariable.pos); @@ -2557,6 +2555,14 @@ namespace ts { } } + function parseTypePredicatePrefix() { + const id = parseIdentifier(); + if (token === SyntaxKind.IsKeyword && !scanner.hasPrecedingLineBreak()) { + nextToken(); + return id; + } + } + function parseType(): TypeNode { // The rules about 'yield' only apply to actual code/expression contexts. They don't // apply to 'type' contexts. So we disable these parameters here before moving on. From 9ab9940fd03d4eb11422a0ab49bf96df19ad2a13 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 22 Dec 2015 11:48:01 -0800 Subject: [PATCH 08/10] Remove unused error for this-type predicates. Also: 1. Remove notes I wrote myself for merging. 2. Switch to pattern matching on properties in a few places. --- src/compiler/checker.ts | 51 ++++++++-------------------- src/compiler/diagnosticMessages.json | 4 --- 2 files changed, 14 insertions(+), 41 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 63e44bad83c..ce40f8b7348 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11138,29 +11138,19 @@ namespace ts { if (!parent) { return; } - // NEW we now get and check the return type -- is this needed? - // Because of Wesley's change, sigs no longer have a special typePred member, - // they have a type that extends PredicateType (with flags including PredicateType) const returnType = getReturnTypeOfSignature(getSignatureFromDeclaration(parent)); if (!returnType || !(returnType.flags & TypeFlags.PredicateType)) { return; } const { parameterName } = node; if (parameterName.kind === SyntaxKind.ThisType) { - if (!isInLegalThisTypePredicatePosition(node)) { - error(node, Diagnostics.A_this_based_type_predicate_is_only_allowed_within_a_class_or_interface_s_members_get_accessors_or_return_type_positions_for_functions_and_methods); - } - else { - getTypeFromThisTypeNode(parameterName as ThisTypeNode); - // TODO: Should probably skip past the other error checking now - // ... because I bet the above function is the equivalent of this one. - } + getTypeFromThisTypeNode(parameterName as ThisTypeNode); } else { const typePredicate = (returnType).predicate; if (typePredicate.parameterIndex >= 0) { if (parent.parameters[typePredicate.parameterIndex].dotDotDotToken) { - error(node.parameterName, + error(parameterName, Diagnostics.A_type_predicate_cannot_reference_a_rest_parameter); } else { @@ -11169,14 +11159,14 @@ namespace ts { node.type); } } - else if (node.parameterName) { + else if (parameterName) { let hasReportedError = false; - for (const param of parent.parameters) { - if ((param.name.kind === SyntaxKind.ObjectBindingPattern || - param.name.kind === SyntaxKind.ArrayBindingPattern) && + for (const { name } of parent.parameters) { + if ((name.kind === SyntaxKind.ObjectBindingPattern || + name.kind === SyntaxKind.ArrayBindingPattern) && checkBindingPatternForTypePredicateVariable( - param.name, - node.parameterName, + name, + parameterName, typePredicate.parameterName)) { hasReportedError = true; break; @@ -11209,18 +11199,18 @@ namespace ts { pattern: BindingPattern, predicateVariableNode: Node, predicateVariableName: string) { - for (const element of pattern.elements) { - if (element.name.kind === SyntaxKind.Identifier && - (element.name).text === predicateVariableName) { + for (const { name } of pattern.elements) { + if (name.kind === SyntaxKind.Identifier && + (name).text === predicateVariableName) { error(predicateVariableNode, Diagnostics.A_type_predicate_cannot_reference_element_0_in_a_binding_pattern, predicateVariableName); return true; } - else if (element.name.kind === SyntaxKind.ArrayBindingPattern || - element.name.kind === SyntaxKind.ObjectBindingPattern) { + else if (name.kind === SyntaxKind.ArrayBindingPattern || + name.kind === SyntaxKind.ObjectBindingPattern) { if (checkBindingPatternForTypePredicateVariable( - element.name, + name, predicateVariableNode, predicateVariableName)) { return true; @@ -11229,19 +11219,6 @@ namespace ts { } } - function isInLegalThisTypePredicatePosition(node: Node): boolean { - if (getTypePredicateParent(node)) { - return true; - } - switch (node.parent.kind) { - case SyntaxKind.PropertyDeclaration: - case SyntaxKind.PropertySignature: - case SyntaxKind.GetAccessor: - return node === (node.parent as (PropertyDeclaration | GetAccessorDeclaration | PropertySignature)).type; - } - return false; - } - function checkSignatureDeclaration(node: SignatureDeclaration) { // Grammar checking if (node.kind === SyntaxKind.IndexSignature) { diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 1a5b2f3751d..47d85edfbe9 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1651,10 +1651,6 @@ "category": "Error", "code": 2518 }, - "A 'this'-based type predicate is only allowed within a class or interface's members, get accessors, or return type positions for functions and methods.": { - "category": "Error", - "code": 2519 - }, "Duplicate identifier '{0}'. Compiler uses declaration '{1}' to support async functions.": { "category": "Error", "code": 2520 From 9b13a0c5b9796a10d9847068f2cdd89bd8523468 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 6 Jan 2016 10:58:57 -0800 Subject: [PATCH 09/10] Better name for checkTypePredicate helper function checkBindingPatternForTypeVariable -> checkIfTypeVariableIsDeclaredInBindingPattern --- src/compiler/checker.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ce40f8b7348..6b508c57dc6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11164,7 +11164,7 @@ namespace ts { for (const { name } of parent.parameters) { if ((name.kind === SyntaxKind.ObjectBindingPattern || name.kind === SyntaxKind.ArrayBindingPattern) && - checkBindingPatternForTypePredicateVariable( + checkIfTypePredicateVariableIsDeclaredInBindingPattern( name, parameterName, typePredicate.parameterName)) { @@ -11195,7 +11195,7 @@ namespace ts { } } - function checkBindingPatternForTypePredicateVariable( + function checkIfTypePredicateVariableIsDeclaredInBindingPattern( pattern: BindingPattern, predicateVariableNode: Node, predicateVariableName: string) { @@ -11209,7 +11209,7 @@ namespace ts { } else if (name.kind === SyntaxKind.ArrayBindingPattern || name.kind === SyntaxKind.ObjectBindingPattern) { - if (checkBindingPatternForTypePredicateVariable( + if (checkIfTypePredicateVariableIsDeclaredInBindingPattern( name, predicateVariableNode, predicateVariableName)) { From a9f2cb6d6e791a0c1e7d588369da4bae6811653b Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 13 Jan 2016 09:31:06 -0800 Subject: [PATCH 10/10] Make `parseTypeOrTypePredicate` terser. --- src/compiler/parser.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index eaeb6452044..bfa5fb6ee1a 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -2541,11 +2541,7 @@ namespace ts { } function parseTypeOrTypePredicate(): TypeNode { - let typePredicateVariable: Identifier; - if (isIdentifier()) { - typePredicateVariable = tryParse(parseTypePredicatePrefix); - } - + const typePredicateVariable = isIdentifier() && tryParse(parseTypePredicatePrefix); const type = parseType(); if (typePredicateVariable) { const node = createNode(SyntaxKind.TypePredicate, typePredicateVariable.pos);