diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 09e79549d166f..93317b8e7c06f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5998,20 +5998,37 @@ namespace ts { if (type.flags & TypeFlags.Any) { return type; } + let signature = getResolvedSignature(expr); if (signature.typePredicate && - expr.arguments[signature.typePredicate.parameterIndex] && - getSymbolAtLocation(expr.arguments[signature.typePredicate.parameterIndex]) === symbol) { + expr.arguments[signature.typePredicate.parameterIndex]) { + + let arg = expr.arguments[signature.typePredicate.parameterIndex]; + if (arg.kind === SyntaxKind.Identifier && getSymbolAtLocation(arg) === symbol) { + return narrowType(); + } + else if (arg.kind === SyntaxKind.AsExpression || arg.kind === SyntaxKind.TypeAssertionExpression) { + if ((arg).expression) { + if ((arg).expression.kind === SyntaxKind.Identifier && + getSymbolAtLocation((arg).expression) === symbol) { - if (!assumeTrue) { - if (type.flags & TypeFlags.Union) { - return getUnionType(filter((type).types, t => !isTypeSubtypeOf(t, signature.typePredicate.type))); + return narrowType(); + } } - return type; } - return getNarrowedType(type, signature.typePredicate.type); + + function narrowType(): Type { + if (!assumeTrue) { + if (type.flags & TypeFlags.Union) { + return getUnionType(filter((type).types, t => !isTypeSubtypeOf(t, signature.typePredicate.type))); + } + return type; + } + return getNarrowedType(type, signature.typePredicate.type); + } } + return type; } @@ -6765,7 +6782,7 @@ namespace ts { let restArrayType = checkExpression((e).expression, contextualMapper); let restElementType = getIndexTypeOfType(restArrayType, IndexKind.Number) || (languageVersion >= ScriptTarget.ES6 ? getElementTypeOfIterable(restArrayType, /*errorNode*/ undefined) : undefined); - if (restElementType) { + if (restElementType) { elementTypes.push(restElementType); } } @@ -8012,8 +8029,7 @@ namespace ts { return args; } - - /** + /** * Returns the effective argument count for a node that works like a function invocation. * If 'node' is a Decorator, the number of arguments is derived from the decoration * target and the signature: @@ -8090,11 +8106,11 @@ namespace ts { let classSymbol = getSymbolOfNode(node); return getTypeOfSymbol(classSymbol); } - + // fall-through case SyntaxKind.PropertyDeclaration: - case SyntaxKind.MethodDeclaration: + case SyntaxKind.MethodDeclaration: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: // For a property or method decorator, the `target` is the @@ -8137,14 +8153,14 @@ namespace ts { // For a constructor parameter decorator, the `propertyKey` will be `undefined`. return anyType; } - + // For a non-constructor parameter decorator, the `propertyKey` will be either // a string or a symbol, based on the name of the parameter's containing method. // fall-through case SyntaxKind.PropertyDeclaration: - case SyntaxKind.MethodDeclaration: + case SyntaxKind.MethodDeclaration: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: // The `propertyKey` for a property or method decorator will be a @@ -8172,8 +8188,8 @@ namespace ts { return unknownType; } - - default: + + default: Debug.fail("Unsupported decorator target."); return unknownType; } @@ -11459,19 +11475,19 @@ namespace ts { forEach(node.declarationList.declarations, checkSourceElement); } - function checkGrammarDisallowedModifiersOnObjectLiteralExpressionMethod(node: Node) { + function checkGrammarDisallowedModifiersOnObjectLiteralExpressionMethod(node: Node) { // We only disallow modifier on a method declaration if it is a property of object-literal-expression if (node.modifiers && node.parent.kind === SyntaxKind.ObjectLiteralExpression){ if (isAsyncFunctionLike(node)) { if (node.modifiers.length > 1) { - return grammarErrorOnFirstToken(node, Diagnostics.Modifiers_cannot_appear_here); + return grammarErrorOnFirstToken(node, Diagnostics.Modifiers_cannot_appear_here); } } else { return grammarErrorOnFirstToken(node, Diagnostics.Modifiers_cannot_appear_here); } } - } + } function checkExpressionStatement(node: ExpressionStatement) { // Grammar checking @@ -14501,7 +14517,7 @@ namespace ts { } flags |= NodeFlags.Ambient; lastDeclare = modifier; - break; + break; case SyntaxKind.AbstractKeyword: if (flags & NodeFlags.Abstract) { @@ -14537,7 +14553,7 @@ namespace ts { } flags |= NodeFlags.Async; lastAsync = modifier; - break; + break; } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 389eda4ccede0..fdcfa0270eb8f 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -847,8 +847,8 @@ namespace ts { } export interface TypeAssertion extends UnaryExpression { - type: TypeNode; expression: UnaryExpression; + type: TypeNode; } export type AssertionExpression = TypeAssertion | AsExpression; @@ -1755,10 +1755,10 @@ namespace ts { StringLike = String | StringLiteral, NumberLike = Number | Enum, ObjectType = Class | Interface | Reference | Tuple | Anonymous, - UnionOrIntersection = Union | Intersection, + UnionOrIntersection = Union | Intersection, StructuredType = ObjectType | Union | Intersection, /* @internal */ - RequiresWidening = ContainsUndefinedOrNull | ContainsObjectLiteral + RequiresWidening = ContainsUndefinedOrNull | ContainsObjectLiteral } // Properties common to all types diff --git a/tests/baselines/reference/tsxAttributeResolution9.symbols b/tests/baselines/reference/tsxAttributeResolution9.symbols deleted file mode 100644 index 081482d5d47a4..0000000000000 --- a/tests/baselines/reference/tsxAttributeResolution9.symbols +++ /dev/null @@ -1,47 +0,0 @@ -=== tests/cases/conformance/jsx/react.d.ts === - -declare module JSX { ->JSX : Symbol(JSX, Decl(react.d.ts, 0, 0)) - - interface Element { } ->Element : Symbol(Element, Decl(react.d.ts, 1, 20)) - - interface IntrinsicElements { ->IntrinsicElements : Symbol(IntrinsicElements, Decl(react.d.ts, 2, 22)) - } - interface ElementAttributesProperty { ->ElementAttributesProperty : Symbol(ElementAttributesProperty, Decl(react.d.ts, 4, 2)) - - props; ->props : Symbol(props, Decl(react.d.ts, 5, 38)) - } -} - -interface Props { ->Props : Symbol(Props, Decl(react.d.ts, 8, 1)) - - foo: string; ->foo : Symbol(foo, Decl(react.d.ts, 10, 17)) -} - -=== tests/cases/conformance/jsx/file.tsx === -export class MyComponent { ->MyComponent : Symbol(MyComponent, Decl(file.tsx, 0, 0)) - - render() { ->render : Symbol(render, Decl(file.tsx, 0, 26)) - } - - props: { foo: string; } ->props : Symbol(props, Decl(file.tsx, 2, 3)) ->foo : Symbol(foo, Decl(file.tsx, 4, 10)) -} - -; // ok ->MyComponent : Symbol(MyComponent, Decl(file.tsx, 0, 0)) ->foo : Symbol(unknown) - -; // should be an error ->MyComponent : Symbol(MyComponent, Decl(file.tsx, 0, 0)) ->foo : Symbol(unknown) - diff --git a/tests/baselines/reference/tsxAttributeResolution9.types b/tests/baselines/reference/tsxAttributeResolution9.types deleted file mode 100644 index 1b2c6d423896a..0000000000000 --- a/tests/baselines/reference/tsxAttributeResolution9.types +++ /dev/null @@ -1,49 +0,0 @@ -=== tests/cases/conformance/jsx/react.d.ts === - -declare module JSX { ->JSX : any - - interface Element { } ->Element : Element - - interface IntrinsicElements { ->IntrinsicElements : IntrinsicElements - } - interface ElementAttributesProperty { ->ElementAttributesProperty : ElementAttributesProperty - - props; ->props : any - } -} - -interface Props { ->Props : Props - - foo: string; ->foo : string -} - -=== tests/cases/conformance/jsx/file.tsx === -export class MyComponent { ->MyComponent : MyComponent - - render() { ->render : () => void - } - - props: { foo: string; } ->props : { foo: string; } ->foo : string -} - -; // ok -> : JSX.Element ->MyComponent : typeof MyComponent ->foo : any - -; // should be an error -> : JSX.Element ->MyComponent : typeof MyComponent ->foo : any - diff --git a/tests/baselines/reference/typeGuardFunction.js b/tests/baselines/reference/typeGuardFunction.js index bff304682afa5..6077503f6411e 100644 --- a/tests/baselines/reference/typeGuardFunction.js +++ b/tests/baselines/reference/typeGuardFunction.js @@ -38,6 +38,14 @@ if(isA(union)) { union.propA; } +// Type assertions +if (isA(union)) { + a = union; +} +if (isA(union as A)) { + a = union; +} + // Call signature interface I1 { (p1: A): p1 is C; @@ -81,7 +89,8 @@ acceptingTypeGuardFunction(isA); // Binary expressions let union2: C | B; -let union3: boolean | B = isA(union2) || union2; +let union3: boolean | B = isA(union2) || union2; + //// [typeGuardFunction.js] var __extends = (this && this.__extends) || function (d, b) { @@ -122,6 +131,13 @@ var union; if (isA(union)) { union.propA; } +// Type assertions +if (isA(union)) { + a = union; +} +if (isA(union)) { + a = union; +} if (isC_multipleParams(a, 0)) { a.propC; } diff --git a/tests/baselines/reference/typeGuardFunction.symbols b/tests/baselines/reference/typeGuardFunction.symbols index 13ad30de5c548..6d99e2aec64c6 100644 --- a/tests/baselines/reference/typeGuardFunction.symbols +++ b/tests/baselines/reference/typeGuardFunction.symbols @@ -94,28 +94,48 @@ if(isA(union)) { >propA : Symbol(A.propA, Decl(typeGuardFunction.ts, 1, 9)) } +// Type assertions +if (isA(union)) { +>isA : Symbol(isA, Decl(typeGuardFunction.ts, 11, 1)) +>A : Symbol(A, Decl(typeGuardFunction.ts, 0, 0)) +>union : Symbol(union, Decl(typeGuardFunction.ts, 34, 3)) + + a = union; +>a : Symbol(a, Decl(typeGuardFunction.ts, 19, 3)) +>union : Symbol(union, Decl(typeGuardFunction.ts, 34, 3)) +} +if (isA(union as A)) { +>isA : Symbol(isA, Decl(typeGuardFunction.ts, 11, 1)) +>union : Symbol(union, Decl(typeGuardFunction.ts, 34, 3)) +>A : Symbol(A, Decl(typeGuardFunction.ts, 0, 0)) + + a = union; +>a : Symbol(a, Decl(typeGuardFunction.ts, 19, 3)) +>union : Symbol(union, Decl(typeGuardFunction.ts, 34, 3)) +} + // Call signature interface I1 { ->I1 : Symbol(I1, Decl(typeGuardFunction.ts, 37, 1)) +>I1 : Symbol(I1, Decl(typeGuardFunction.ts, 45, 1)) (p1: A): p1 is C; ->p1 : Symbol(p1, Decl(typeGuardFunction.ts, 41, 5)) +>p1 : Symbol(p1, Decl(typeGuardFunction.ts, 49, 5)) >A : Symbol(A, Decl(typeGuardFunction.ts, 0, 0)) ->p1 : Symbol(p1, Decl(typeGuardFunction.ts, 41, 5)) +>p1 : Symbol(p1, Decl(typeGuardFunction.ts, 49, 5)) >C : Symbol(C, Decl(typeGuardFunction.ts, 7, 1)) } // The parameter index and argument index for the type guard target is matching. // The type predicate type is assignable to the parameter type. declare function isC_multipleParams(p1, p2): p1 is C; ->isC_multipleParams : Symbol(isC_multipleParams, Decl(typeGuardFunction.ts, 42, 1)) ->p1 : Symbol(p1, Decl(typeGuardFunction.ts, 46, 36)) ->p2 : Symbol(p2, Decl(typeGuardFunction.ts, 46, 39)) ->p1 : Symbol(p1, Decl(typeGuardFunction.ts, 46, 36)) +>isC_multipleParams : Symbol(isC_multipleParams, Decl(typeGuardFunction.ts, 50, 1)) +>p1 : Symbol(p1, Decl(typeGuardFunction.ts, 54, 36)) +>p2 : Symbol(p2, Decl(typeGuardFunction.ts, 54, 39)) +>p1 : Symbol(p1, Decl(typeGuardFunction.ts, 54, 36)) >C : Symbol(C, Decl(typeGuardFunction.ts, 7, 1)) if (isC_multipleParams(a, 0)) { ->isC_multipleParams : Symbol(isC_multipleParams, Decl(typeGuardFunction.ts, 42, 1)) +>isC_multipleParams : Symbol(isC_multipleParams, Decl(typeGuardFunction.ts, 50, 1)) >a : Symbol(a, Decl(typeGuardFunction.ts, 19, 3)) a.propC; @@ -126,23 +146,23 @@ if (isC_multipleParams(a, 0)) { // Methods var obj: { ->obj : Symbol(obj, Decl(typeGuardFunction.ts, 52, 3)) +>obj : Symbol(obj, Decl(typeGuardFunction.ts, 60, 3)) func1(p1: A): p1 is C; ->func1 : Symbol(func1, Decl(typeGuardFunction.ts, 52, 10)) ->p1 : Symbol(p1, Decl(typeGuardFunction.ts, 53, 10)) +>func1 : Symbol(func1, Decl(typeGuardFunction.ts, 60, 10)) +>p1 : Symbol(p1, Decl(typeGuardFunction.ts, 61, 10)) >A : Symbol(A, Decl(typeGuardFunction.ts, 0, 0)) ->p1 : Symbol(p1, Decl(typeGuardFunction.ts, 53, 10)) +>p1 : Symbol(p1, Decl(typeGuardFunction.ts, 61, 10)) >C : Symbol(C, Decl(typeGuardFunction.ts, 7, 1)) } class D { ->D : Symbol(D, Decl(typeGuardFunction.ts, 54, 1)) +>D : Symbol(D, Decl(typeGuardFunction.ts, 62, 1)) method1(p1: A): p1 is C { ->method1 : Symbol(method1, Decl(typeGuardFunction.ts, 55, 9)) ->p1 : Symbol(p1, Decl(typeGuardFunction.ts, 56, 12)) +>method1 : Symbol(method1, Decl(typeGuardFunction.ts, 63, 9)) +>p1 : Symbol(p1, Decl(typeGuardFunction.ts, 64, 12)) >A : Symbol(A, Decl(typeGuardFunction.ts, 0, 0)) ->p1 : Symbol(p1, Decl(typeGuardFunction.ts, 56, 12)) +>p1 : Symbol(p1, Decl(typeGuardFunction.ts, 64, 12)) >C : Symbol(C, Decl(typeGuardFunction.ts, 7, 1)) return true; @@ -151,27 +171,27 @@ class D { // Arrow function let f1 = (p1: A): p1 is C => false; ->f1 : Symbol(f1, Decl(typeGuardFunction.ts, 62, 3)) ->p1 : Symbol(p1, Decl(typeGuardFunction.ts, 62, 10)) +>f1 : Symbol(f1, Decl(typeGuardFunction.ts, 70, 3)) +>p1 : Symbol(p1, Decl(typeGuardFunction.ts, 70, 10)) >A : Symbol(A, Decl(typeGuardFunction.ts, 0, 0)) ->p1 : Symbol(p1, Decl(typeGuardFunction.ts, 62, 10)) +>p1 : Symbol(p1, Decl(typeGuardFunction.ts, 70, 10)) >C : Symbol(C, Decl(typeGuardFunction.ts, 7, 1)) // Function type declare function f2(p1: (p1: A) => p1 is C); ->f2 : Symbol(f2, Decl(typeGuardFunction.ts, 62, 35)) ->p1 : Symbol(p1, Decl(typeGuardFunction.ts, 65, 20)) ->p1 : Symbol(p1, Decl(typeGuardFunction.ts, 65, 25)) +>f2 : Symbol(f2, Decl(typeGuardFunction.ts, 70, 35)) +>p1 : Symbol(p1, Decl(typeGuardFunction.ts, 73, 20)) +>p1 : Symbol(p1, Decl(typeGuardFunction.ts, 73, 25)) >A : Symbol(A, Decl(typeGuardFunction.ts, 0, 0)) ->p1 : Symbol(p1, Decl(typeGuardFunction.ts, 65, 25)) +>p1 : Symbol(p1, Decl(typeGuardFunction.ts, 73, 25)) >C : Symbol(C, Decl(typeGuardFunction.ts, 7, 1)) // Function expressions f2(function(p1: A): p1 is C { ->f2 : Symbol(f2, Decl(typeGuardFunction.ts, 62, 35)) ->p1 : Symbol(p1, Decl(typeGuardFunction.ts, 68, 12)) +>f2 : Symbol(f2, Decl(typeGuardFunction.ts, 70, 35)) +>p1 : Symbol(p1, Decl(typeGuardFunction.ts, 76, 12)) >A : Symbol(A, Decl(typeGuardFunction.ts, 0, 0)) ->p1 : Symbol(p1, Decl(typeGuardFunction.ts, 68, 12)) +>p1 : Symbol(p1, Decl(typeGuardFunction.ts, 76, 12)) >C : Symbol(C, Decl(typeGuardFunction.ts, 7, 1)) return true; @@ -179,36 +199,36 @@ f2(function(p1: A): p1 is C { // Evaluations are asssignable to boolean. declare function acceptingBoolean(a: boolean); ->acceptingBoolean : Symbol(acceptingBoolean, Decl(typeGuardFunction.ts, 70, 3)) ->a : Symbol(a, Decl(typeGuardFunction.ts, 73, 34)) +>acceptingBoolean : Symbol(acceptingBoolean, Decl(typeGuardFunction.ts, 78, 3)) +>a : Symbol(a, Decl(typeGuardFunction.ts, 81, 34)) acceptingBoolean(isA(a)); ->acceptingBoolean : Symbol(acceptingBoolean, Decl(typeGuardFunction.ts, 70, 3)) +>acceptingBoolean : Symbol(acceptingBoolean, Decl(typeGuardFunction.ts, 78, 3)) >isA : Symbol(isA, Decl(typeGuardFunction.ts, 11, 1)) >a : Symbol(a, Decl(typeGuardFunction.ts, 19, 3)) // Type predicates with different parameter name. declare function acceptingTypeGuardFunction(p1: (item) => item is A); ->acceptingTypeGuardFunction : Symbol(acceptingTypeGuardFunction, Decl(typeGuardFunction.ts, 74, 25)) ->p1 : Symbol(p1, Decl(typeGuardFunction.ts, 77, 44)) ->item : Symbol(item, Decl(typeGuardFunction.ts, 77, 49)) ->item : Symbol(item, Decl(typeGuardFunction.ts, 77, 49)) +>acceptingTypeGuardFunction : Symbol(acceptingTypeGuardFunction, Decl(typeGuardFunction.ts, 82, 25)) +>p1 : Symbol(p1, Decl(typeGuardFunction.ts, 85, 44)) +>item : Symbol(item, Decl(typeGuardFunction.ts, 85, 49)) +>item : Symbol(item, Decl(typeGuardFunction.ts, 85, 49)) >A : Symbol(A, Decl(typeGuardFunction.ts, 0, 0)) acceptingTypeGuardFunction(isA); ->acceptingTypeGuardFunction : Symbol(acceptingTypeGuardFunction, Decl(typeGuardFunction.ts, 74, 25)) +>acceptingTypeGuardFunction : Symbol(acceptingTypeGuardFunction, Decl(typeGuardFunction.ts, 82, 25)) >isA : Symbol(isA, Decl(typeGuardFunction.ts, 11, 1)) // Binary expressions let union2: C | B; ->union2 : Symbol(union2, Decl(typeGuardFunction.ts, 81, 3)) +>union2 : Symbol(union2, Decl(typeGuardFunction.ts, 89, 3)) >C : Symbol(C, Decl(typeGuardFunction.ts, 7, 1)) >B : Symbol(B, Decl(typeGuardFunction.ts, 3, 1)) let union3: boolean | B = isA(union2) || union2; ->union3 : Symbol(union3, Decl(typeGuardFunction.ts, 82, 3)) +>union3 : Symbol(union3, Decl(typeGuardFunction.ts, 90, 3)) >B : Symbol(B, Decl(typeGuardFunction.ts, 3, 1)) >isA : Symbol(isA, Decl(typeGuardFunction.ts, 11, 1)) ->union2 : Symbol(union2, Decl(typeGuardFunction.ts, 81, 3)) ->union2 : Symbol(union2, Decl(typeGuardFunction.ts, 81, 3)) +>union2 : Symbol(union2, Decl(typeGuardFunction.ts, 89, 3)) +>union2 : Symbol(union2, Decl(typeGuardFunction.ts, 89, 3)) diff --git a/tests/baselines/reference/typeGuardFunction.types b/tests/baselines/reference/typeGuardFunction.types index 6cc278f122f8c..fa41e17e6302d 100644 --- a/tests/baselines/reference/typeGuardFunction.types +++ b/tests/baselines/reference/typeGuardFunction.types @@ -97,6 +97,32 @@ if(isA(union)) { >propA : number } +// Type assertions +if (isA(union)) { +>isA(union) : boolean +>isA : (p1: any) => p1 is A +>union : A +>A : A +>union : A | B + + a = union; +>a = union : A +>a : A +>union : A +} +if (isA(union as A)) { +>isA(union as A) : boolean +>isA : (p1: any) => p1 is A +>union as A : A +>union : A | B +>A : A + + a = union; +>a = union : A +>a : A +>union : A +} + // Call signature interface I1 { >I1 : I1 diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardFunction.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardFunction.ts index 57d56ccc3b68d..04c281608283f 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardFunction.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardFunction.ts @@ -37,6 +37,14 @@ if(isA(union)) { union.propA; } +// Type assertions +if (isA(union)) { + a = union; +} +if (isA(union as A)) { + a = union; +} + // Call signature interface I1 { (p1: A): p1 is C; @@ -80,4 +88,4 @@ acceptingTypeGuardFunction(isA); // Binary expressions let union2: C | B; -let union3: boolean | B = isA(union2) || union2; \ No newline at end of file +let union3: boolean | B = isA(union2) || union2;