From c4cff9833df7a90868473d1461a38dbc7be49896 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 2 Dec 2015 18:35:49 -0800 Subject: [PATCH 01/15] first pass at this type predicates --- src/compiler/checker.ts | 273 +++++++++++++++++---------- src/compiler/diagnosticMessages.json | 4 + src/compiler/parser.ts | 29 ++- src/compiler/types.ts | 28 ++- src/compiler/utilities.ts | 4 + 5 files changed, 227 insertions(+), 111 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e737cd58bd560..a5a904ab3a91f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2058,7 +2058,12 @@ namespace ts { let returnType: Type; if (signature.typePredicate) { - writer.writeParameter(signature.typePredicate.parameterName); + if (signature.typePredicate.kind === TypePredicateKind.Identifier) { + writer.writeParameter((signature.typePredicate as IdentifierTypePredicate).parameterName); + } + else { + writeKeyword(writer, SyntaxKind.ThisKeyword); + } writeSpace(writer); writeKeyword(writer, SyntaxKind.IsKeyword); writeSpace(writer); @@ -3356,7 +3361,7 @@ namespace ts { } function createSignature(declaration: SignatureDeclaration, typeParameters: TypeParameter[], parameters: Symbol[], - resolvedReturnType: Type, typePredicate: TypePredicate, minArgumentCount: number, hasRestParameter: boolean, hasStringLiterals: boolean): Signature { + resolvedReturnType: Type, typePredicate: IdentifierTypePredicate | ThisTypePredicate, minArgumentCount: number, hasRestParameter: boolean, hasStringLiterals: boolean): Signature { const sig = new Signature(checker); sig.declaration = declaration; sig.typeParameters = typeParameters; @@ -3884,19 +3889,29 @@ namespace ts { } let returnType: Type; - let typePredicate: TypePredicate; + let typePredicate: IdentifierTypePredicate | ThisTypePredicate; if (classType) { returnType = classType; } else if (declaration.type) { returnType = getTypeFromTypeNode(declaration.type); if (declaration.type.kind === SyntaxKind.TypePredicate) { - const typePredicateNode = declaration.type; - typePredicate = { - parameterName: typePredicateNode.parameterName ? typePredicateNode.parameterName.text : undefined, - parameterIndex: typePredicateNode.parameterName ? getTypePredicateParameterIndex(declaration.parameters, typePredicateNode.parameterName) : undefined, - type: getTypeFromTypeNode(typePredicateNode.type) - }; + const typePredicateNode = declaration.type as TypePredicateNode; + if (typePredicateNode.parameterName.kind === SyntaxKind.Identifier) { + const parameterName = typePredicateNode.parameterName as Identifier; + typePredicate = { + kind: TypePredicateKind.Identifier, + parameterName: parameterName ? parameterName.text : undefined, + parameterIndex: parameterName ? getTypePredicateParameterIndex(declaration.parameters, parameterName) : undefined, + type: getTypeFromTypeNode(typePredicateNode.type) + }; + } + else { + typePredicate = { + kind: TypePredicateKind.This, + type: getTypeFromTypeNode(typePredicateNode.type) + } as ThisTypePredicate; + } } } else { @@ -4716,19 +4731,33 @@ namespace ts { return result; } + function cloneTypePredicate(predicate: TypePredicate, mapper: TypeMapper): ThisTypePredicate | IdentifierTypePredicate { + if (predicate.kind === TypePredicateKind.Identifier) { + const identifierPredicate = predicate as IdentifierTypePredicate; + return { + kind: TypePredicateKind.Identifier, + parameterName: identifierPredicate.parameterName, + parameterIndex: identifierPredicate.parameterIndex, + type: instantiateType(predicate.type, mapper) + } as IdentifierTypePredicate; + } + else { + return { + kind: TypePredicateKind.This, + type: instantiateType(predicate.type, mapper) + } as ThisTypePredicate; + } + } + function instantiateSignature(signature: Signature, mapper: TypeMapper, eraseTypeParameters?: boolean): Signature { let freshTypeParameters: TypeParameter[]; - let freshTypePredicate: TypePredicate; + let freshTypePredicate: ThisTypePredicate | IdentifierTypePredicate; if (signature.typeParameters && !eraseTypeParameters) { freshTypeParameters = instantiateList(signature.typeParameters, mapper, instantiateTypeParameter); mapper = combineTypeMappers(createTypeMapper(signature.typeParameters, freshTypeParameters), mapper); } if (signature.typePredicate) { - freshTypePredicate = { - parameterName: signature.typePredicate.parameterName, - parameterIndex: signature.typePredicate.parameterIndex, - type: instantiateType(signature.typePredicate.type, mapper) - }; + freshTypePredicate = cloneTypePredicate(signature.typePredicate, mapper); } const result = createSignature(signature.declaration, freshTypeParameters, instantiateList(signature.parameters, mapper, instantiateSymbol), @@ -5548,33 +5577,55 @@ namespace ts { } if (source.typePredicate && target.typePredicate) { - const hasDifferentParameterIndex = source.typePredicate.parameterIndex !== target.typePredicate.parameterIndex; - let hasDifferentTypes: boolean; - if (hasDifferentParameterIndex || - (hasDifferentTypes = !isTypeIdenticalTo(source.typePredicate.type, target.typePredicate.type))) { - + if (source.typePredicate.kind !== target.typePredicate.kind) { if (reportErrors) { - const sourceParamText = source.typePredicate.parameterName; - const targetParamText = target.typePredicate.parameterName; - const sourceTypeText = typeToString(source.typePredicate.type); - const targetTypeText = typeToString(target.typePredicate.type); - - if (hasDifferentParameterIndex) { - reportError(Diagnostics.Parameter_0_is_not_in_the_same_position_as_parameter_1, - sourceParamText, - targetParamText); - } - else if (hasDifferentTypes) { + reportError(Diagnostics.A_this_based_type_guard_is_not_assignable_to_a_parameter_based_type_guard); + } + return Ternary.False; + } + if (source.typePredicate.kind === TypePredicateKind.This) { + if (!isTypeIdenticalTo(source.typePredicate.type, target.typePredicate.type)) { + if (reportErrors) { + const sourceTypeText = typeToString(source.typePredicate.type); + const targetTypeText = typeToString(target.typePredicate.type); reportError(Diagnostics.Type_0_is_not_assignable_to_type_1, sourceTypeText, targetTypeText); } + return Ternary.False; + } + } + else { + const sourcePredicate = source.typePredicate as IdentifierTypePredicate; + const targetPredicate = target.typePredicate as IdentifierTypePredicate; + const hasDifferentParameterIndex = sourcePredicate.parameterIndex !== targetPredicate.parameterIndex; + let hasDifferentTypes: boolean; + if (hasDifferentParameterIndex || + (hasDifferentTypes = !isTypeIdenticalTo(sourcePredicate.type, targetPredicate.type))) { - reportError(Diagnostics.Type_predicate_0_is_not_assignable_to_1, - `${sourceParamText} is ${sourceTypeText}`, - `${targetParamText} is ${targetTypeText}`); + if (reportErrors) { + const sourceParamText = sourcePredicate.parameterName; + const targetParamText = targetPredicate.parameterName; + const sourceTypeText = typeToString(sourcePredicate.type); + const targetTypeText = typeToString(targetPredicate.type); + + if (hasDifferentParameterIndex) { + reportError(Diagnostics.Parameter_0_is_not_in_the_same_position_as_parameter_1, + sourceParamText, + targetParamText); + } + else if (hasDifferentTypes) { + reportError(Diagnostics.Type_0_is_not_assignable_to_type_1, + sourceTypeText, + targetTypeText); + } + + reportError(Diagnostics.Type_predicate_0_is_not_assignable_to_1, + `${sourceParamText} is ${sourceTypeText}`, + `${targetParamText} is ${targetTypeText}`); + } + return Ternary.False; } - return Ternary.False; } } else if (!source.typePredicate && target.typePredicate) { @@ -6235,11 +6286,15 @@ namespace ts { function inferFromSignature(source: Signature, target: Signature) { forEachMatchingParameterType(source, target, inferFromTypes); if (source.typePredicate && target.typePredicate) { - if (target.typePredicate.parameterIndex === source.typePredicate.parameterIndex) { - // Return types from type predicates are treated as booleans. In order to infer types - // from type predicates we would need to infer using the type within the type predicate - // (i.e. 'Foo' from 'x is Foo'). - inferFromTypes(source.typePredicate.type, target.typePredicate.type); + if (target.typePredicate.kind === source.typePredicate.kind) { + if ((target.typePredicate.kind === TypePredicateKind.Identifier + && (target.typePredicate as IdentifierTypePredicate).parameterIndex === (source.typePredicate as IdentifierTypePredicate).parameterIndex) + || target.typePredicate.kind === TypePredicateKind.This) { + // Return types from type predicates are treated as booleans. In order to infer types + // from type predicates we would need to infer using the type within the type predicate + // (i.e. 'Foo' from 'x is Foo'). + inferFromTypes(source.typePredicate.type, target.typePredicate.type); + } } } else { @@ -6654,20 +6709,20 @@ namespace ts { } if (targetType) { - if (!assumeTrue) { - if (type.flags & TypeFlags.Union) { - return getUnionType(filter((type).types, t => !isTypeSubtypeOf(t, targetType))); - } - return type; - } - - return getNarrowedType(type, targetType); + return getNarrowedType(type, targetType, assumeTrue); } return type; } - function getNarrowedType(originalType: Type, narrowedTypeCandidate: Type) { + function getNarrowedType(originalType: Type, narrowedTypeCandidate: Type, assumeTrue: boolean) { + if (!assumeTrue) { + if (originalType.flags & TypeFlags.Union) { + return getUnionType(filter((originalType).types, t => !isTypeSubtypeOf(t, narrowedTypeCandidate))); + } + return originalType; + } + // If the current type is a union type, remove all constituents that aren't assignable to target. If that produces // 0 candidates, fall back to the assignability check if (originalType.flags & TypeFlags.Union) { @@ -6691,19 +6746,35 @@ namespace ts { } const signature = getResolvedSignature(expr); - if (signature.typePredicate && - expr.arguments[signature.typePredicate.parameterIndex] && - getSymbolAtLocation(expr.arguments[signature.typePredicate.parameterIndex]) === symbol) { + if (!signature.typePredicate) { + return type; + } + const predicate = signature.typePredicate; + if (isIdentifierTypePredicate(predicate)) { + if (expr.arguments[predicate.parameterIndex] && + getSymbolAtLocation(expr.arguments[predicate.parameterIndex]) === symbol) { + return getNarrowedType(type, predicate.type, assumeTrue); + } + } + else { + const expression = skipParenthesizedNodes(expr.expression); - if (!assumeTrue) { - if (type.flags & TypeFlags.Union) { - return getUnionType(filter((type).types, t => !isTypeSubtypeOf(t, signature.typePredicate.type))); + if (expression.kind === SyntaxKind.ElementAccessExpression || expression.kind === SyntaxKind.PropertyAccessExpression) { + const accessExpression = expression as ElementAccessExpression | PropertyAccessExpression; + const possibleIdentifier = skipParenthesizedNodes(accessExpression.expression); + if (possibleIdentifier.kind === SyntaxKind.Identifier && getSymbolAtLocation(possibleIdentifier) === symbol) { + return getNarrowedType(type, predicate.type, assumeTrue); } - return type; } - return getNarrowedType(type, signature.typePredicate.type); } return type; + + function skipParenthesizedNodes(expression: Expression): Expression { + while (expression.kind === SyntaxKind.ParenthesizedExpression) { + expression = (expression as ParenthesizedExpression).expression; + } + return expression; + } } // Narrow the given type based on the given expression having the assumed boolean value. The returned type @@ -10962,52 +11033,58 @@ namespace ts { 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 (isIdentifierTypePredicate(typePredicate)) { + if (typePredicate.parameterIndex >= 0) { + if (node.parameters[typePredicate.parameterIndex].dotDotDotToken) { + error(typePredicateNode.parameterName, + Diagnostics.A_type_predicate_cannot_reference_a_rest_parameter); } - 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); + else { + checkTypeAssignableTo(typePredicate.type, + getTypeOfNode(node.parameters[typePredicate.parameterIndex]), + typePredicateNode.type); } } - if (!hasReportedError) { - error(typePredicateNode.parameterName, - Diagnostics.Cannot_find_parameter_0, - typePredicate.parameterName); + 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 { + // Reuse this type diagnostics on the this type node to determine if a this type predicate is valid + getTypeFromThisTypeNode(typePredicateNode.parameterName as ThisTypeNode); + } } else { error(typePredicateNode, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 0c19f6d8247a0..f48a37968ca22 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1632,6 +1632,10 @@ "category": "Error", "code": 2517 }, + "A this based type guard is not assignable to a parameter based type guard": { + "category": "Error", + "code": 2518 + }, "Duplicate identifier '{0}'. Compiler uses declaration '{1}' to support async functions.": { "category": "Error", "code": 2520 diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 3a3350f2166a8..8b735ad0aa3da 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1959,11 +1959,7 @@ namespace ts { function parseTypeReferenceOrTypePredicate(): TypeReferenceNode | TypePredicateNode { 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); + return parseTypePredicate(typeName as Identifier); } const node = createNode(SyntaxKind.TypeReference, typeName.pos); node.typeName = typeName; @@ -1973,8 +1969,16 @@ namespace ts { return finishNode(node); } - function parseThisTypeNode(): TypeNode { - const node = createNode(SyntaxKind.ThisType); + function parseTypePredicate(lhs: Identifier | ThisTypeNode): TypePredicateNode { + nextToken(); + const node = createNode(SyntaxKind.TypePredicate, lhs.pos) as TypePredicateNode; + node.parameterName = lhs; + node.type = parseType(); + return finishNode(node); + } + + function parseThisTypeNode(): ThisTypeNode { + const node = createNode(SyntaxKind.ThisType) as ThisTypeNode; nextToken(); return finishNode(node); } @@ -2420,8 +2424,15 @@ namespace ts { return parseStringLiteralTypeNode(); case SyntaxKind.VoidKeyword: return parseTokenNode(); - case SyntaxKind.ThisKeyword: - return parseThisTypeNode(); + case SyntaxKind.ThisKeyword: { + const thisKeyword = parseThisTypeNode(); + if (token === SyntaxKind.IsKeyword && !scanner.hasPrecedingLineBreak()) { + return parseTypePredicate(thisKeyword); + } + else { + return thisKeyword; + } + } case SyntaxKind.TypeOfKeyword: return parseTypeQuery(); case SyntaxKind.OpenBraceToken: diff --git a/src/compiler/types.ts b/src/compiler/types.ts index dbd0322aa1802..a55373224b980 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -733,11 +733,15 @@ namespace ts { // @kind(SyntaxKind.StringKeyword) // @kind(SyntaxKind.SymbolKeyword) // @kind(SyntaxKind.VoidKeyword) - // @kind(SyntaxKind.ThisType) export interface TypeNode extends Node { _typeNodeBrand: any; } + // @kind(SyntaxKind.ThisType) + export interface ThisTypeNode extends TypeNode { + _thisTypeNodeBrand: any; + } + export interface FunctionOrConstructorTypeNode extends TypeNode, SignatureDeclaration { _functionOrConstructorTypeNodeBrand: any; } @@ -756,7 +760,7 @@ namespace ts { // @kind(SyntaxKind.TypePredicate) export interface TypePredicateNode extends TypeNode { - parameterName: Identifier; + parameterName: Identifier | ThisTypeNode; type: TypeNode; } @@ -1820,10 +1824,26 @@ namespace ts { CannotBeNamed } + /* @internal */ + export const enum TypePredicateKind { + This, + Identifier + } + export interface TypePredicate { + kind: TypePredicateKind; + type: Type; + } + + // @kind (TypePredicateKind.This) + export interface ThisTypePredicate extends TypePredicate { + _thisTypePredicateBrand: any; + } + + // @kind (TypePredicateKind.Identifier) + export interface IdentifierTypePredicate extends TypePredicate { parameterName: string; parameterIndex: number; - type: Type; } /* @internal */ @@ -2237,7 +2257,7 @@ namespace ts { declaration: SignatureDeclaration; // Originating declaration typeParameters: TypeParameter[]; // Type parameters (undefined if non-generic) parameters: Symbol[]; // Parameters - typePredicate?: TypePredicate; // Type predicate + typePredicate?: ThisTypePredicate | IdentifierTypePredicate; // Type predicate /* @internal */ resolvedReturnType: Type; // Resolved return type /* @internal */ diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 280b8203ff458..5a35bcbdee138 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -692,6 +692,10 @@ namespace ts { return node && node.kind === SyntaxKind.MethodDeclaration && node.parent.kind === SyntaxKind.ObjectLiteralExpression; } + export function isIdentifierTypePredicate(predicate: TypePredicate): predicate is IdentifierTypePredicate { + return predicate.kind === TypePredicateKind.Identifier; + } + export function getContainingFunction(node: Node): FunctionLikeDeclaration { while (true) { node = node.parent; From 859cc53c1105be721e66d4811430604d342931c7 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 2 Dec 2015 19:49:43 -0800 Subject: [PATCH 02/15] Add tests, remove internal annotation form enum --- src/compiler/types.ts | 1 - .../reference/typeGuardFunctionOfFormThis.js | 281 ++++++++++++ .../typeGuardFunctionOfFormThis.symbols | 371 +++++++++++++++ .../typeGuardFunctionOfFormThis.types | 427 ++++++++++++++++++ ...peGuardFunctionOfFormThisErrors.errors.txt | 76 ++++ .../typeGuardFunctionOfFormThisErrors.js | 113 +++++ .../typeGuards/typeGuardFunctionOfFormThis.ts | 134 ++++++ .../typeGuardFunctionOfFormThisErrors.ts | 50 ++ 8 files changed, 1452 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/typeGuardFunctionOfFormThis.js create mode 100644 tests/baselines/reference/typeGuardFunctionOfFormThis.symbols create mode 100644 tests/baselines/reference/typeGuardFunctionOfFormThis.types create mode 100644 tests/baselines/reference/typeGuardFunctionOfFormThisErrors.errors.txt create mode 100644 tests/baselines/reference/typeGuardFunctionOfFormThisErrors.js create mode 100644 tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThis.ts create mode 100644 tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors.ts diff --git a/src/compiler/types.ts b/src/compiler/types.ts index a55373224b980..4bde87d0ff5b2 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1824,7 +1824,6 @@ namespace ts { CannotBeNamed } - /* @internal */ export const enum TypePredicateKind { This, Identifier diff --git a/tests/baselines/reference/typeGuardFunctionOfFormThis.js b/tests/baselines/reference/typeGuardFunctionOfFormThis.js new file mode 100644 index 0000000000000..16ac876be8c08 --- /dev/null +++ b/tests/baselines/reference/typeGuardFunctionOfFormThis.js @@ -0,0 +1,281 @@ +//// [typeGuardFunctionOfFormThis.ts] +class RoyalGuard { + isLeader(): this is LeadGuard { + return this instanceof LeadGuard; + } + isFollower(): this is FollowerGuard { + return this instanceof FollowerGuard; + } +} + +class LeadGuard extends RoyalGuard { + lead(): void {}; +} + +class FollowerGuard extends RoyalGuard { + follow(): void {}; +} + +let a: RoyalGuard = new FollowerGuard(); +if (a.isLeader()) { + a.lead(); +} +else if (a.isFollower()) { + a.follow(); +} + +interface GuardInterface extends RoyalGuard {} + +let b: GuardInterface; +if (b.isLeader()) { + b.lead(); +} +else if (b.isFollower()) { + b.follow(); +} + +if (((a.isLeader)())) { + a.lead(); +} +else if (((a).isFollower())) { + a.follow(); +} + +if (((a["isLeader"])())) { + a.lead(); +} +else if (((a)["isFollower"]())) { + a.follow(); +} + +var holder2 = {a}; + +if (holder2.a.isLeader()) { + holder2.a; +} +else { + holder2.a; +} + +class ArrowGuard { + isElite = (): this is ArrowElite => { + return this instanceof ArrowElite; + } + isMedic = (): this is ArrowMedic => { + return this instanceof ArrowMedic; + } +} + +class ArrowElite extends ArrowGuard { + defend(): void {} +} + +class ArrowMedic extends ArrowGuard { + heal(): void {} +} + +let guard = new ArrowGuard(); +if (guard.isElite()) { + guard.defend(); +} +else if (guard.isMedic()) { + guard.heal(); +} + +interface Supplies { + spoiled: boolean; +} + +interface Sundries { + broken: boolean; +} + +interface Crate { + contents: T; + volume: number; + isSupplies(): this is Crate; + isSundries(): this is Crate; +} + +let crate: Crate<{}>; + +if (crate.isSundries()) { + crate.contents.broken = true; +} +else if (crate.isSupplies()) { + crate.contents.spoiled = true; +} + +// Matching guards should be assignable + +a.isFollower = b.isFollower; +a.isLeader = b.isLeader; + +class MimicGuard { + isLeader(): this is MimicLeader { return this instanceof MimicLeader; }; + isFollower(): this is MimicFollower { return this instanceof MimicFollower; }; +} + +class MimicLeader extends MimicGuard { + lead(): void {} +} + +class MimicFollower extends MimicGuard { + follow(): void {} +} + +let mimic = new MimicGuard(); + +a.isLeader = mimic.isLeader; +a.isFollower = mimic.isFollower; + +if (mimic.isFollower()) { + mimic.follow(); + mimic.isFollower = a.isFollower; +} + +//// [typeGuardFunctionOfFormThis.js] +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var RoyalGuard = (function () { + function RoyalGuard() { + } + RoyalGuard.prototype.isLeader = function () { + return this instanceof LeadGuard; + }; + RoyalGuard.prototype.isFollower = function () { + return this instanceof FollowerGuard; + }; + return RoyalGuard; +})(); +var LeadGuard = (function (_super) { + __extends(LeadGuard, _super); + function LeadGuard() { + _super.apply(this, arguments); + } + LeadGuard.prototype.lead = function () { }; + ; + return LeadGuard; +})(RoyalGuard); +var FollowerGuard = (function (_super) { + __extends(FollowerGuard, _super); + function FollowerGuard() { + _super.apply(this, arguments); + } + FollowerGuard.prototype.follow = function () { }; + ; + return FollowerGuard; +})(RoyalGuard); +var a = new FollowerGuard(); +if (a.isLeader()) { + a.lead(); +} +else if (a.isFollower()) { + a.follow(); +} +var b; +if (b.isLeader()) { + b.lead(); +} +else if (b.isFollower()) { + b.follow(); +} +if (((a.isLeader)())) { + a.lead(); +} +else if (((a).isFollower())) { + a.follow(); +} +if (((a["isLeader"])())) { + a.lead(); +} +else if (((a)["isFollower"]())) { + a.follow(); +} +var holder2 = { a: a }; +if (holder2.a.isLeader()) { + holder2.a; +} +else { + holder2.a; +} +var ArrowGuard = (function () { + function ArrowGuard() { + var _this = this; + this.isElite = function () { + return _this instanceof ArrowElite; + }; + this.isMedic = function () { + return _this instanceof ArrowMedic; + }; + } + return ArrowGuard; +})(); +var ArrowElite = (function (_super) { + __extends(ArrowElite, _super); + function ArrowElite() { + _super.apply(this, arguments); + } + ArrowElite.prototype.defend = function () { }; + return ArrowElite; +})(ArrowGuard); +var ArrowMedic = (function (_super) { + __extends(ArrowMedic, _super); + function ArrowMedic() { + _super.apply(this, arguments); + } + ArrowMedic.prototype.heal = function () { }; + return ArrowMedic; +})(ArrowGuard); +var guard = new ArrowGuard(); +if (guard.isElite()) { + guard.defend(); +} +else if (guard.isMedic()) { + guard.heal(); +} +var crate; +if (crate.isSundries()) { + crate.contents.broken = true; +} +else if (crate.isSupplies()) { + crate.contents.spoiled = true; +} +// Matching guards should be assignable +a.isFollower = b.isFollower; +a.isLeader = b.isLeader; +var MimicGuard = (function () { + function MimicGuard() { + } + MimicGuard.prototype.isLeader = function () { return this instanceof MimicLeader; }; + ; + MimicGuard.prototype.isFollower = function () { return this instanceof MimicFollower; }; + ; + return MimicGuard; +})(); +var MimicLeader = (function (_super) { + __extends(MimicLeader, _super); + function MimicLeader() { + _super.apply(this, arguments); + } + MimicLeader.prototype.lead = function () { }; + return MimicLeader; +})(MimicGuard); +var MimicFollower = (function (_super) { + __extends(MimicFollower, _super); + function MimicFollower() { + _super.apply(this, arguments); + } + MimicFollower.prototype.follow = function () { }; + return MimicFollower; +})(MimicGuard); +var mimic = new MimicGuard(); +a.isLeader = mimic.isLeader; +a.isFollower = mimic.isFollower; +if (mimic.isFollower()) { + mimic.follow(); + mimic.isFollower = a.isFollower; +} diff --git a/tests/baselines/reference/typeGuardFunctionOfFormThis.symbols b/tests/baselines/reference/typeGuardFunctionOfFormThis.symbols new file mode 100644 index 0000000000000..9bda32c455700 --- /dev/null +++ b/tests/baselines/reference/typeGuardFunctionOfFormThis.symbols @@ -0,0 +1,371 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThis.ts === +class RoyalGuard { +>RoyalGuard : Symbol(RoyalGuard, Decl(typeGuardFunctionOfFormThis.ts, 0, 0)) + + isLeader(): this is LeadGuard { +>isLeader : Symbol(isLeader, Decl(typeGuardFunctionOfFormThis.ts, 0, 18)) +>LeadGuard : Symbol(LeadGuard, Decl(typeGuardFunctionOfFormThis.ts, 7, 1)) + + return this instanceof LeadGuard; +>this : Symbol(RoyalGuard, Decl(typeGuardFunctionOfFormThis.ts, 0, 0)) +>LeadGuard : Symbol(LeadGuard, Decl(typeGuardFunctionOfFormThis.ts, 7, 1)) + } + isFollower(): this is FollowerGuard { +>isFollower : Symbol(isFollower, Decl(typeGuardFunctionOfFormThis.ts, 3, 5)) +>FollowerGuard : Symbol(FollowerGuard, Decl(typeGuardFunctionOfFormThis.ts, 11, 1)) + + return this instanceof FollowerGuard; +>this : Symbol(RoyalGuard, Decl(typeGuardFunctionOfFormThis.ts, 0, 0)) +>FollowerGuard : Symbol(FollowerGuard, Decl(typeGuardFunctionOfFormThis.ts, 11, 1)) + } +} + +class LeadGuard extends RoyalGuard { +>LeadGuard : Symbol(LeadGuard, Decl(typeGuardFunctionOfFormThis.ts, 7, 1)) +>RoyalGuard : Symbol(RoyalGuard, Decl(typeGuardFunctionOfFormThis.ts, 0, 0)) + + lead(): void {}; +>lead : Symbol(lead, Decl(typeGuardFunctionOfFormThis.ts, 9, 36)) +} + +class FollowerGuard extends RoyalGuard { +>FollowerGuard : Symbol(FollowerGuard, Decl(typeGuardFunctionOfFormThis.ts, 11, 1)) +>RoyalGuard : Symbol(RoyalGuard, Decl(typeGuardFunctionOfFormThis.ts, 0, 0)) + + follow(): void {}; +>follow : Symbol(follow, Decl(typeGuardFunctionOfFormThis.ts, 13, 40)) +} + +let a: RoyalGuard = new FollowerGuard(); +>a : Symbol(a, Decl(typeGuardFunctionOfFormThis.ts, 17, 3)) +>RoyalGuard : Symbol(RoyalGuard, Decl(typeGuardFunctionOfFormThis.ts, 0, 0)) +>FollowerGuard : Symbol(FollowerGuard, Decl(typeGuardFunctionOfFormThis.ts, 11, 1)) + +if (a.isLeader()) { +>a.isLeader : Symbol(RoyalGuard.isLeader, Decl(typeGuardFunctionOfFormThis.ts, 0, 18)) +>a : Symbol(a, Decl(typeGuardFunctionOfFormThis.ts, 17, 3)) +>isLeader : Symbol(RoyalGuard.isLeader, Decl(typeGuardFunctionOfFormThis.ts, 0, 18)) + + a.lead(); +>a.lead : Symbol(LeadGuard.lead, Decl(typeGuardFunctionOfFormThis.ts, 9, 36)) +>a : Symbol(a, Decl(typeGuardFunctionOfFormThis.ts, 17, 3)) +>lead : Symbol(LeadGuard.lead, Decl(typeGuardFunctionOfFormThis.ts, 9, 36)) +} +else if (a.isFollower()) { +>a.isFollower : Symbol(RoyalGuard.isFollower, Decl(typeGuardFunctionOfFormThis.ts, 3, 5)) +>a : Symbol(a, Decl(typeGuardFunctionOfFormThis.ts, 17, 3)) +>isFollower : Symbol(RoyalGuard.isFollower, Decl(typeGuardFunctionOfFormThis.ts, 3, 5)) + + a.follow(); +>a.follow : Symbol(FollowerGuard.follow, Decl(typeGuardFunctionOfFormThis.ts, 13, 40)) +>a : Symbol(a, Decl(typeGuardFunctionOfFormThis.ts, 17, 3)) +>follow : Symbol(FollowerGuard.follow, Decl(typeGuardFunctionOfFormThis.ts, 13, 40)) +} + +interface GuardInterface extends RoyalGuard {} +>GuardInterface : Symbol(GuardInterface, Decl(typeGuardFunctionOfFormThis.ts, 23, 1)) +>RoyalGuard : Symbol(RoyalGuard, Decl(typeGuardFunctionOfFormThis.ts, 0, 0)) + +let b: GuardInterface; +>b : Symbol(b, Decl(typeGuardFunctionOfFormThis.ts, 27, 3)) +>GuardInterface : Symbol(GuardInterface, Decl(typeGuardFunctionOfFormThis.ts, 23, 1)) + +if (b.isLeader()) { +>b.isLeader : Symbol(RoyalGuard.isLeader, Decl(typeGuardFunctionOfFormThis.ts, 0, 18)) +>b : Symbol(b, Decl(typeGuardFunctionOfFormThis.ts, 27, 3)) +>isLeader : Symbol(RoyalGuard.isLeader, Decl(typeGuardFunctionOfFormThis.ts, 0, 18)) + + b.lead(); +>b.lead : Symbol(LeadGuard.lead, Decl(typeGuardFunctionOfFormThis.ts, 9, 36)) +>b : Symbol(b, Decl(typeGuardFunctionOfFormThis.ts, 27, 3)) +>lead : Symbol(LeadGuard.lead, Decl(typeGuardFunctionOfFormThis.ts, 9, 36)) +} +else if (b.isFollower()) { +>b.isFollower : Symbol(RoyalGuard.isFollower, Decl(typeGuardFunctionOfFormThis.ts, 3, 5)) +>b : Symbol(b, Decl(typeGuardFunctionOfFormThis.ts, 27, 3)) +>isFollower : Symbol(RoyalGuard.isFollower, Decl(typeGuardFunctionOfFormThis.ts, 3, 5)) + + b.follow(); +>b.follow : Symbol(FollowerGuard.follow, Decl(typeGuardFunctionOfFormThis.ts, 13, 40)) +>b : Symbol(b, Decl(typeGuardFunctionOfFormThis.ts, 27, 3)) +>follow : Symbol(FollowerGuard.follow, Decl(typeGuardFunctionOfFormThis.ts, 13, 40)) +} + +if (((a.isLeader)())) { +>a.isLeader : Symbol(RoyalGuard.isLeader, Decl(typeGuardFunctionOfFormThis.ts, 0, 18)) +>a : Symbol(a, Decl(typeGuardFunctionOfFormThis.ts, 17, 3)) +>isLeader : Symbol(RoyalGuard.isLeader, Decl(typeGuardFunctionOfFormThis.ts, 0, 18)) + + a.lead(); +>a.lead : Symbol(LeadGuard.lead, Decl(typeGuardFunctionOfFormThis.ts, 9, 36)) +>a : Symbol(a, Decl(typeGuardFunctionOfFormThis.ts, 17, 3)) +>lead : Symbol(LeadGuard.lead, Decl(typeGuardFunctionOfFormThis.ts, 9, 36)) +} +else if (((a).isFollower())) { +>(a).isFollower : Symbol(RoyalGuard.isFollower, Decl(typeGuardFunctionOfFormThis.ts, 3, 5)) +>a : Symbol(a, Decl(typeGuardFunctionOfFormThis.ts, 17, 3)) +>isFollower : Symbol(RoyalGuard.isFollower, Decl(typeGuardFunctionOfFormThis.ts, 3, 5)) + + a.follow(); +>a.follow : Symbol(FollowerGuard.follow, Decl(typeGuardFunctionOfFormThis.ts, 13, 40)) +>a : Symbol(a, Decl(typeGuardFunctionOfFormThis.ts, 17, 3)) +>follow : Symbol(FollowerGuard.follow, Decl(typeGuardFunctionOfFormThis.ts, 13, 40)) +} + +if (((a["isLeader"])())) { +>a : Symbol(a, Decl(typeGuardFunctionOfFormThis.ts, 17, 3)) +>"isLeader" : Symbol(RoyalGuard.isLeader, Decl(typeGuardFunctionOfFormThis.ts, 0, 18)) + + a.lead(); +>a.lead : Symbol(LeadGuard.lead, Decl(typeGuardFunctionOfFormThis.ts, 9, 36)) +>a : Symbol(a, Decl(typeGuardFunctionOfFormThis.ts, 17, 3)) +>lead : Symbol(LeadGuard.lead, Decl(typeGuardFunctionOfFormThis.ts, 9, 36)) +} +else if (((a)["isFollower"]())) { +>a : Symbol(a, Decl(typeGuardFunctionOfFormThis.ts, 17, 3)) +>"isFollower" : Symbol(RoyalGuard.isFollower, Decl(typeGuardFunctionOfFormThis.ts, 3, 5)) + + a.follow(); +>a.follow : Symbol(FollowerGuard.follow, Decl(typeGuardFunctionOfFormThis.ts, 13, 40)) +>a : Symbol(a, Decl(typeGuardFunctionOfFormThis.ts, 17, 3)) +>follow : Symbol(FollowerGuard.follow, Decl(typeGuardFunctionOfFormThis.ts, 13, 40)) +} + +var holder2 = {a}; +>holder2 : Symbol(holder2, Decl(typeGuardFunctionOfFormThis.ts, 49, 3)) +>a : Symbol(a, Decl(typeGuardFunctionOfFormThis.ts, 49, 15)) + +if (holder2.a.isLeader()) { +>holder2.a.isLeader : Symbol(RoyalGuard.isLeader, Decl(typeGuardFunctionOfFormThis.ts, 0, 18)) +>holder2.a : Symbol(a, Decl(typeGuardFunctionOfFormThis.ts, 49, 15)) +>holder2 : Symbol(holder2, Decl(typeGuardFunctionOfFormThis.ts, 49, 3)) +>a : Symbol(a, Decl(typeGuardFunctionOfFormThis.ts, 49, 15)) +>isLeader : Symbol(RoyalGuard.isLeader, Decl(typeGuardFunctionOfFormThis.ts, 0, 18)) + + holder2.a; +>holder2.a : Symbol(a, Decl(typeGuardFunctionOfFormThis.ts, 49, 15)) +>holder2 : Symbol(holder2, Decl(typeGuardFunctionOfFormThis.ts, 49, 3)) +>a : Symbol(a, Decl(typeGuardFunctionOfFormThis.ts, 49, 15)) +} +else { + holder2.a; +>holder2.a : Symbol(a, Decl(typeGuardFunctionOfFormThis.ts, 49, 15)) +>holder2 : Symbol(holder2, Decl(typeGuardFunctionOfFormThis.ts, 49, 3)) +>a : Symbol(a, Decl(typeGuardFunctionOfFormThis.ts, 49, 15)) +} + +class ArrowGuard { +>ArrowGuard : Symbol(ArrowGuard, Decl(typeGuardFunctionOfFormThis.ts, 56, 1)) + + isElite = (): this is ArrowElite => { +>isElite : Symbol(isElite, Decl(typeGuardFunctionOfFormThis.ts, 58, 18)) +>ArrowElite : Symbol(ArrowElite, Decl(typeGuardFunctionOfFormThis.ts, 65, 1)) + + return this instanceof ArrowElite; +>this : Symbol(ArrowGuard, Decl(typeGuardFunctionOfFormThis.ts, 56, 1)) +>ArrowElite : Symbol(ArrowElite, Decl(typeGuardFunctionOfFormThis.ts, 65, 1)) + } + isMedic = (): this is ArrowMedic => { +>isMedic : Symbol(isMedic, Decl(typeGuardFunctionOfFormThis.ts, 61, 5)) +>ArrowMedic : Symbol(ArrowMedic, Decl(typeGuardFunctionOfFormThis.ts, 69, 1)) + + return this instanceof ArrowMedic; +>this : Symbol(ArrowGuard, Decl(typeGuardFunctionOfFormThis.ts, 56, 1)) +>ArrowMedic : Symbol(ArrowMedic, Decl(typeGuardFunctionOfFormThis.ts, 69, 1)) + } +} + +class ArrowElite extends ArrowGuard { +>ArrowElite : Symbol(ArrowElite, Decl(typeGuardFunctionOfFormThis.ts, 65, 1)) +>ArrowGuard : Symbol(ArrowGuard, Decl(typeGuardFunctionOfFormThis.ts, 56, 1)) + + defend(): void {} +>defend : Symbol(defend, Decl(typeGuardFunctionOfFormThis.ts, 67, 37)) +} + +class ArrowMedic extends ArrowGuard { +>ArrowMedic : Symbol(ArrowMedic, Decl(typeGuardFunctionOfFormThis.ts, 69, 1)) +>ArrowGuard : Symbol(ArrowGuard, Decl(typeGuardFunctionOfFormThis.ts, 56, 1)) + + heal(): void {} +>heal : Symbol(heal, Decl(typeGuardFunctionOfFormThis.ts, 71, 37)) +} + +let guard = new ArrowGuard(); +>guard : Symbol(guard, Decl(typeGuardFunctionOfFormThis.ts, 75, 3)) +>ArrowGuard : Symbol(ArrowGuard, Decl(typeGuardFunctionOfFormThis.ts, 56, 1)) + +if (guard.isElite()) { +>guard.isElite : Symbol(ArrowGuard.isElite, Decl(typeGuardFunctionOfFormThis.ts, 58, 18)) +>guard : Symbol(guard, Decl(typeGuardFunctionOfFormThis.ts, 75, 3)) +>isElite : Symbol(ArrowGuard.isElite, Decl(typeGuardFunctionOfFormThis.ts, 58, 18)) + + guard.defend(); +>guard.defend : Symbol(ArrowElite.defend, Decl(typeGuardFunctionOfFormThis.ts, 67, 37)) +>guard : Symbol(guard, Decl(typeGuardFunctionOfFormThis.ts, 75, 3)) +>defend : Symbol(ArrowElite.defend, Decl(typeGuardFunctionOfFormThis.ts, 67, 37)) +} +else if (guard.isMedic()) { +>guard.isMedic : Symbol(ArrowGuard.isMedic, Decl(typeGuardFunctionOfFormThis.ts, 61, 5)) +>guard : Symbol(guard, Decl(typeGuardFunctionOfFormThis.ts, 75, 3)) +>isMedic : Symbol(ArrowGuard.isMedic, Decl(typeGuardFunctionOfFormThis.ts, 61, 5)) + + guard.heal(); +>guard.heal : Symbol(ArrowMedic.heal, Decl(typeGuardFunctionOfFormThis.ts, 71, 37)) +>guard : Symbol(guard, Decl(typeGuardFunctionOfFormThis.ts, 75, 3)) +>heal : Symbol(ArrowMedic.heal, Decl(typeGuardFunctionOfFormThis.ts, 71, 37)) +} + +interface Supplies { +>Supplies : Symbol(Supplies, Decl(typeGuardFunctionOfFormThis.ts, 81, 1)) + + spoiled: boolean; +>spoiled : Symbol(spoiled, Decl(typeGuardFunctionOfFormThis.ts, 83, 20)) +} + +interface Sundries { +>Sundries : Symbol(Sundries, Decl(typeGuardFunctionOfFormThis.ts, 85, 1)) + + broken: boolean; +>broken : Symbol(broken, Decl(typeGuardFunctionOfFormThis.ts, 87, 20)) +} + +interface Crate { +>Crate : Symbol(Crate, Decl(typeGuardFunctionOfFormThis.ts, 89, 1)) +>T : Symbol(T, Decl(typeGuardFunctionOfFormThis.ts, 91, 16)) + + contents: T; +>contents : Symbol(contents, Decl(typeGuardFunctionOfFormThis.ts, 91, 20)) +>T : Symbol(T, Decl(typeGuardFunctionOfFormThis.ts, 91, 16)) + + volume: number; +>volume : Symbol(volume, Decl(typeGuardFunctionOfFormThis.ts, 92, 16)) + + isSupplies(): this is Crate; +>isSupplies : Symbol(isSupplies, Decl(typeGuardFunctionOfFormThis.ts, 93, 19)) +>Crate : Symbol(Crate, Decl(typeGuardFunctionOfFormThis.ts, 89, 1)) +>Supplies : Symbol(Supplies, Decl(typeGuardFunctionOfFormThis.ts, 81, 1)) + + isSundries(): this is Crate; +>isSundries : Symbol(isSundries, Decl(typeGuardFunctionOfFormThis.ts, 94, 42)) +>Crate : Symbol(Crate, Decl(typeGuardFunctionOfFormThis.ts, 89, 1)) +>Sundries : Symbol(Sundries, Decl(typeGuardFunctionOfFormThis.ts, 85, 1)) +} + +let crate: Crate<{}>; +>crate : Symbol(crate, Decl(typeGuardFunctionOfFormThis.ts, 98, 3)) +>Crate : Symbol(Crate, Decl(typeGuardFunctionOfFormThis.ts, 89, 1)) + +if (crate.isSundries()) { +>crate.isSundries : Symbol(Crate.isSundries, Decl(typeGuardFunctionOfFormThis.ts, 94, 42)) +>crate : Symbol(crate, Decl(typeGuardFunctionOfFormThis.ts, 98, 3)) +>isSundries : Symbol(Crate.isSundries, Decl(typeGuardFunctionOfFormThis.ts, 94, 42)) + + crate.contents.broken = true; +>crate.contents.broken : Symbol(Sundries.broken, Decl(typeGuardFunctionOfFormThis.ts, 87, 20)) +>crate.contents : Symbol(Crate.contents, Decl(typeGuardFunctionOfFormThis.ts, 91, 20)) +>crate : Symbol(crate, Decl(typeGuardFunctionOfFormThis.ts, 98, 3)) +>contents : Symbol(Crate.contents, Decl(typeGuardFunctionOfFormThis.ts, 91, 20)) +>broken : Symbol(Sundries.broken, Decl(typeGuardFunctionOfFormThis.ts, 87, 20)) +} +else if (crate.isSupplies()) { +>crate.isSupplies : Symbol(Crate.isSupplies, Decl(typeGuardFunctionOfFormThis.ts, 93, 19)) +>crate : Symbol(crate, Decl(typeGuardFunctionOfFormThis.ts, 98, 3)) +>isSupplies : Symbol(Crate.isSupplies, Decl(typeGuardFunctionOfFormThis.ts, 93, 19)) + + crate.contents.spoiled = true; +>crate.contents.spoiled : Symbol(Supplies.spoiled, Decl(typeGuardFunctionOfFormThis.ts, 83, 20)) +>crate.contents : Symbol(Crate.contents, Decl(typeGuardFunctionOfFormThis.ts, 91, 20)) +>crate : Symbol(crate, Decl(typeGuardFunctionOfFormThis.ts, 98, 3)) +>contents : Symbol(Crate.contents, Decl(typeGuardFunctionOfFormThis.ts, 91, 20)) +>spoiled : Symbol(Supplies.spoiled, Decl(typeGuardFunctionOfFormThis.ts, 83, 20)) +} + +// Matching guards should be assignable + +a.isFollower = b.isFollower; +>a.isFollower : Symbol(RoyalGuard.isFollower, Decl(typeGuardFunctionOfFormThis.ts, 3, 5)) +>a : Symbol(a, Decl(typeGuardFunctionOfFormThis.ts, 17, 3)) +>isFollower : Symbol(RoyalGuard.isFollower, Decl(typeGuardFunctionOfFormThis.ts, 3, 5)) +>b.isFollower : Symbol(RoyalGuard.isFollower, Decl(typeGuardFunctionOfFormThis.ts, 3, 5)) +>b : Symbol(b, Decl(typeGuardFunctionOfFormThis.ts, 27, 3)) +>isFollower : Symbol(RoyalGuard.isFollower, Decl(typeGuardFunctionOfFormThis.ts, 3, 5)) + +a.isLeader = b.isLeader; +>a.isLeader : Symbol(RoyalGuard.isLeader, Decl(typeGuardFunctionOfFormThis.ts, 0, 18)) +>a : Symbol(a, Decl(typeGuardFunctionOfFormThis.ts, 17, 3)) +>isLeader : Symbol(RoyalGuard.isLeader, Decl(typeGuardFunctionOfFormThis.ts, 0, 18)) +>b.isLeader : Symbol(RoyalGuard.isLeader, Decl(typeGuardFunctionOfFormThis.ts, 0, 18)) +>b : Symbol(b, Decl(typeGuardFunctionOfFormThis.ts, 27, 3)) +>isLeader : Symbol(RoyalGuard.isLeader, Decl(typeGuardFunctionOfFormThis.ts, 0, 18)) + +class MimicGuard { +>MimicGuard : Symbol(MimicGuard, Decl(typeGuardFunctionOfFormThis.ts, 110, 24)) + + isLeader(): this is MimicLeader { return this instanceof MimicLeader; }; +>isLeader : Symbol(isLeader, Decl(typeGuardFunctionOfFormThis.ts, 112, 18)) +>MimicLeader : Symbol(MimicLeader, Decl(typeGuardFunctionOfFormThis.ts, 115, 1)) +>this : Symbol(MimicGuard, Decl(typeGuardFunctionOfFormThis.ts, 110, 24)) +>MimicLeader : Symbol(MimicLeader, Decl(typeGuardFunctionOfFormThis.ts, 115, 1)) + + isFollower(): this is MimicFollower { return this instanceof MimicFollower; }; +>isFollower : Symbol(isFollower, Decl(typeGuardFunctionOfFormThis.ts, 113, 76)) +>MimicFollower : Symbol(MimicFollower, Decl(typeGuardFunctionOfFormThis.ts, 119, 1)) +>this : Symbol(MimicGuard, Decl(typeGuardFunctionOfFormThis.ts, 110, 24)) +>MimicFollower : Symbol(MimicFollower, Decl(typeGuardFunctionOfFormThis.ts, 119, 1)) +} + +class MimicLeader extends MimicGuard { +>MimicLeader : Symbol(MimicLeader, Decl(typeGuardFunctionOfFormThis.ts, 115, 1)) +>MimicGuard : Symbol(MimicGuard, Decl(typeGuardFunctionOfFormThis.ts, 110, 24)) + + lead(): void {} +>lead : Symbol(lead, Decl(typeGuardFunctionOfFormThis.ts, 117, 38)) +} + +class MimicFollower extends MimicGuard { +>MimicFollower : Symbol(MimicFollower, Decl(typeGuardFunctionOfFormThis.ts, 119, 1)) +>MimicGuard : Symbol(MimicGuard, Decl(typeGuardFunctionOfFormThis.ts, 110, 24)) + + follow(): void {} +>follow : Symbol(follow, Decl(typeGuardFunctionOfFormThis.ts, 121, 40)) +} + +let mimic = new MimicGuard(); +>mimic : Symbol(mimic, Decl(typeGuardFunctionOfFormThis.ts, 125, 3)) +>MimicGuard : Symbol(MimicGuard, Decl(typeGuardFunctionOfFormThis.ts, 110, 24)) + +a.isLeader = mimic.isLeader; +>a.isLeader : Symbol(RoyalGuard.isLeader, Decl(typeGuardFunctionOfFormThis.ts, 0, 18)) +>a : Symbol(a, Decl(typeGuardFunctionOfFormThis.ts, 17, 3)) +>isLeader : Symbol(RoyalGuard.isLeader, Decl(typeGuardFunctionOfFormThis.ts, 0, 18)) +>mimic.isLeader : Symbol(MimicGuard.isLeader, Decl(typeGuardFunctionOfFormThis.ts, 112, 18)) +>mimic : Symbol(mimic, Decl(typeGuardFunctionOfFormThis.ts, 125, 3)) +>isLeader : Symbol(MimicGuard.isLeader, Decl(typeGuardFunctionOfFormThis.ts, 112, 18)) + +a.isFollower = mimic.isFollower; +>a.isFollower : Symbol(RoyalGuard.isFollower, Decl(typeGuardFunctionOfFormThis.ts, 3, 5)) +>a : Symbol(a, Decl(typeGuardFunctionOfFormThis.ts, 17, 3)) +>isFollower : Symbol(RoyalGuard.isFollower, Decl(typeGuardFunctionOfFormThis.ts, 3, 5)) +>mimic.isFollower : Symbol(MimicGuard.isFollower, Decl(typeGuardFunctionOfFormThis.ts, 113, 76)) +>mimic : Symbol(mimic, Decl(typeGuardFunctionOfFormThis.ts, 125, 3)) +>isFollower : Symbol(MimicGuard.isFollower, Decl(typeGuardFunctionOfFormThis.ts, 113, 76)) + +if (mimic.isFollower()) { +>mimic.isFollower : Symbol(MimicGuard.isFollower, Decl(typeGuardFunctionOfFormThis.ts, 113, 76)) +>mimic : Symbol(mimic, Decl(typeGuardFunctionOfFormThis.ts, 125, 3)) +>isFollower : Symbol(MimicGuard.isFollower, Decl(typeGuardFunctionOfFormThis.ts, 113, 76)) + + mimic.follow(); +>mimic.follow : Symbol(MimicFollower.follow, Decl(typeGuardFunctionOfFormThis.ts, 121, 40)) +>mimic : Symbol(mimic, Decl(typeGuardFunctionOfFormThis.ts, 125, 3)) +>follow : Symbol(MimicFollower.follow, Decl(typeGuardFunctionOfFormThis.ts, 121, 40)) + + mimic.isFollower = a.isFollower; +>mimic.isFollower : Symbol(MimicGuard.isFollower, Decl(typeGuardFunctionOfFormThis.ts, 113, 76)) +>mimic : Symbol(mimic, Decl(typeGuardFunctionOfFormThis.ts, 125, 3)) +>isFollower : Symbol(MimicGuard.isFollower, Decl(typeGuardFunctionOfFormThis.ts, 113, 76)) +>a.isFollower : Symbol(RoyalGuard.isFollower, Decl(typeGuardFunctionOfFormThis.ts, 3, 5)) +>a : Symbol(a, Decl(typeGuardFunctionOfFormThis.ts, 17, 3)) +>isFollower : Symbol(RoyalGuard.isFollower, Decl(typeGuardFunctionOfFormThis.ts, 3, 5)) +} diff --git a/tests/baselines/reference/typeGuardFunctionOfFormThis.types b/tests/baselines/reference/typeGuardFunctionOfFormThis.types new file mode 100644 index 0000000000000..fbe862ab98ba9 --- /dev/null +++ b/tests/baselines/reference/typeGuardFunctionOfFormThis.types @@ -0,0 +1,427 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThis.ts === +class RoyalGuard { +>RoyalGuard : RoyalGuard + + isLeader(): this is LeadGuard { +>isLeader : () => this is LeadGuard +>LeadGuard : LeadGuard + + return this instanceof LeadGuard; +>this instanceof LeadGuard : boolean +>this : this +>LeadGuard : typeof LeadGuard + } + isFollower(): this is FollowerGuard { +>isFollower : () => this is FollowerGuard +>FollowerGuard : FollowerGuard + + return this instanceof FollowerGuard; +>this instanceof FollowerGuard : boolean +>this : this +>FollowerGuard : typeof FollowerGuard + } +} + +class LeadGuard extends RoyalGuard { +>LeadGuard : LeadGuard +>RoyalGuard : RoyalGuard + + lead(): void {}; +>lead : () => void +} + +class FollowerGuard extends RoyalGuard { +>FollowerGuard : FollowerGuard +>RoyalGuard : RoyalGuard + + follow(): void {}; +>follow : () => void +} + +let a: RoyalGuard = new FollowerGuard(); +>a : RoyalGuard +>RoyalGuard : RoyalGuard +>new FollowerGuard() : FollowerGuard +>FollowerGuard : typeof FollowerGuard + +if (a.isLeader()) { +>a.isLeader() : boolean +>a.isLeader : () => this is LeadGuard +>a : RoyalGuard +>isLeader : () => this is LeadGuard + + a.lead(); +>a.lead() : void +>a.lead : () => void +>a : LeadGuard +>lead : () => void +} +else if (a.isFollower()) { +>a.isFollower() : boolean +>a.isFollower : () => this is FollowerGuard +>a : RoyalGuard +>isFollower : () => this is FollowerGuard + + a.follow(); +>a.follow() : void +>a.follow : () => void +>a : FollowerGuard +>follow : () => void +} + +interface GuardInterface extends RoyalGuard {} +>GuardInterface : GuardInterface +>RoyalGuard : RoyalGuard + +let b: GuardInterface; +>b : GuardInterface +>GuardInterface : GuardInterface + +if (b.isLeader()) { +>b.isLeader() : boolean +>b.isLeader : () => this is LeadGuard +>b : GuardInterface +>isLeader : () => this is LeadGuard + + b.lead(); +>b.lead() : void +>b.lead : () => void +>b : LeadGuard +>lead : () => void +} +else if (b.isFollower()) { +>b.isFollower() : boolean +>b.isFollower : () => this is FollowerGuard +>b : GuardInterface +>isFollower : () => this is FollowerGuard + + b.follow(); +>b.follow() : void +>b.follow : () => void +>b : FollowerGuard +>follow : () => void +} + +if (((a.isLeader)())) { +>((a.isLeader)()) : boolean +>(a.isLeader)() : boolean +>(a.isLeader) : () => this is LeadGuard +>a.isLeader : () => this is LeadGuard +>a : RoyalGuard +>isLeader : () => this is LeadGuard + + a.lead(); +>a.lead() : void +>a.lead : () => void +>a : LeadGuard +>lead : () => void +} +else if (((a).isFollower())) { +>((a).isFollower()) : boolean +>(a).isFollower() : boolean +>(a).isFollower : () => this is FollowerGuard +>(a) : RoyalGuard +>a : RoyalGuard +>isFollower : () => this is FollowerGuard + + a.follow(); +>a.follow() : void +>a.follow : () => void +>a : FollowerGuard +>follow : () => void +} + +if (((a["isLeader"])())) { +>((a["isLeader"])()) : boolean +>(a["isLeader"])() : boolean +>(a["isLeader"]) : () => this is LeadGuard +>a["isLeader"] : () => this is LeadGuard +>a : RoyalGuard +>"isLeader" : string + + a.lead(); +>a.lead() : void +>a.lead : () => void +>a : LeadGuard +>lead : () => void +} +else if (((a)["isFollower"]())) { +>((a)["isFollower"]()) : boolean +>(a)["isFollower"]() : boolean +>(a)["isFollower"] : () => this is FollowerGuard +>(a) : RoyalGuard +>a : RoyalGuard +>"isFollower" : string + + a.follow(); +>a.follow() : void +>a.follow : () => void +>a : FollowerGuard +>follow : () => void +} + +var holder2 = {a}; +>holder2 : { a: RoyalGuard; } +>{a} : { a: RoyalGuard; } +>a : RoyalGuard + +if (holder2.a.isLeader()) { +>holder2.a.isLeader() : boolean +>holder2.a.isLeader : () => this is LeadGuard +>holder2.a : RoyalGuard +>holder2 : { a: RoyalGuard; } +>a : RoyalGuard +>isLeader : () => this is LeadGuard + + holder2.a; +>holder2.a : RoyalGuard +>holder2 : { a: RoyalGuard; } +>a : RoyalGuard +} +else { + holder2.a; +>holder2.a : RoyalGuard +>holder2 : { a: RoyalGuard; } +>a : RoyalGuard +} + +class ArrowGuard { +>ArrowGuard : ArrowGuard + + isElite = (): this is ArrowElite => { +>isElite : () => this is ArrowElite +>(): this is ArrowElite => { return this instanceof ArrowElite; } : () => this is ArrowElite +>ArrowElite : ArrowElite + + return this instanceof ArrowElite; +>this instanceof ArrowElite : boolean +>this : this +>ArrowElite : typeof ArrowElite + } + isMedic = (): this is ArrowMedic => { +>isMedic : () => this is ArrowMedic +>(): this is ArrowMedic => { return this instanceof ArrowMedic; } : () => this is ArrowMedic +>ArrowMedic : ArrowMedic + + return this instanceof ArrowMedic; +>this instanceof ArrowMedic : boolean +>this : this +>ArrowMedic : typeof ArrowMedic + } +} + +class ArrowElite extends ArrowGuard { +>ArrowElite : ArrowElite +>ArrowGuard : ArrowGuard + + defend(): void {} +>defend : () => void +} + +class ArrowMedic extends ArrowGuard { +>ArrowMedic : ArrowMedic +>ArrowGuard : ArrowGuard + + heal(): void {} +>heal : () => void +} + +let guard = new ArrowGuard(); +>guard : ArrowGuard +>new ArrowGuard() : ArrowGuard +>ArrowGuard : typeof ArrowGuard + +if (guard.isElite()) { +>guard.isElite() : boolean +>guard.isElite : () => this is ArrowElite +>guard : ArrowGuard +>isElite : () => this is ArrowElite + + guard.defend(); +>guard.defend() : void +>guard.defend : () => void +>guard : ArrowElite +>defend : () => void +} +else if (guard.isMedic()) { +>guard.isMedic() : boolean +>guard.isMedic : () => this is ArrowMedic +>guard : ArrowGuard +>isMedic : () => this is ArrowMedic + + guard.heal(); +>guard.heal() : void +>guard.heal : () => void +>guard : ArrowMedic +>heal : () => void +} + +interface Supplies { +>Supplies : Supplies + + spoiled: boolean; +>spoiled : boolean +} + +interface Sundries { +>Sundries : Sundries + + broken: boolean; +>broken : boolean +} + +interface Crate { +>Crate : Crate +>T : T + + contents: T; +>contents : T +>T : T + + volume: number; +>volume : number + + isSupplies(): this is Crate; +>isSupplies : () => this is Crate +>Crate : Crate +>Supplies : Supplies + + isSundries(): this is Crate; +>isSundries : () => this is Crate +>Crate : Crate +>Sundries : Sundries +} + +let crate: Crate<{}>; +>crate : Crate<{}> +>Crate : Crate + +if (crate.isSundries()) { +>crate.isSundries() : boolean +>crate.isSundries : () => this is Crate +>crate : Crate<{}> +>isSundries : () => this is Crate + + crate.contents.broken = true; +>crate.contents.broken = true : boolean +>crate.contents.broken : boolean +>crate.contents : Sundries +>crate : Crate +>contents : Sundries +>broken : boolean +>true : boolean +} +else if (crate.isSupplies()) { +>crate.isSupplies() : boolean +>crate.isSupplies : () => this is Crate +>crate : Crate<{}> +>isSupplies : () => this is Crate + + crate.contents.spoiled = true; +>crate.contents.spoiled = true : boolean +>crate.contents.spoiled : boolean +>crate.contents : Supplies +>crate : Crate +>contents : Supplies +>spoiled : boolean +>true : boolean +} + +// Matching guards should be assignable + +a.isFollower = b.isFollower; +>a.isFollower = b.isFollower : () => this is FollowerGuard +>a.isFollower : () => this is FollowerGuard +>a : RoyalGuard +>isFollower : () => this is FollowerGuard +>b.isFollower : () => this is FollowerGuard +>b : GuardInterface +>isFollower : () => this is FollowerGuard + +a.isLeader = b.isLeader; +>a.isLeader = b.isLeader : () => this is LeadGuard +>a.isLeader : () => this is LeadGuard +>a : RoyalGuard +>isLeader : () => this is LeadGuard +>b.isLeader : () => this is LeadGuard +>b : GuardInterface +>isLeader : () => this is LeadGuard + +class MimicGuard { +>MimicGuard : MimicGuard + + isLeader(): this is MimicLeader { return this instanceof MimicLeader; }; +>isLeader : () => this is MimicLeader +>MimicLeader : MimicLeader +>this instanceof MimicLeader : boolean +>this : this +>MimicLeader : typeof MimicLeader + + isFollower(): this is MimicFollower { return this instanceof MimicFollower; }; +>isFollower : () => this is MimicFollower +>MimicFollower : MimicFollower +>this instanceof MimicFollower : boolean +>this : this +>MimicFollower : typeof MimicFollower +} + +class MimicLeader extends MimicGuard { +>MimicLeader : MimicLeader +>MimicGuard : MimicGuard + + lead(): void {} +>lead : () => void +} + +class MimicFollower extends MimicGuard { +>MimicFollower : MimicFollower +>MimicGuard : MimicGuard + + follow(): void {} +>follow : () => void +} + +let mimic = new MimicGuard(); +>mimic : MimicGuard +>new MimicGuard() : MimicGuard +>MimicGuard : typeof MimicGuard + +a.isLeader = mimic.isLeader; +>a.isLeader = mimic.isLeader : () => this is MimicLeader +>a.isLeader : () => this is LeadGuard +>a : RoyalGuard +>isLeader : () => this is LeadGuard +>mimic.isLeader : () => this is MimicLeader +>mimic : MimicGuard +>isLeader : () => this is MimicLeader + +a.isFollower = mimic.isFollower; +>a.isFollower = mimic.isFollower : () => this is MimicFollower +>a.isFollower : () => this is FollowerGuard +>a : RoyalGuard +>isFollower : () => this is FollowerGuard +>mimic.isFollower : () => this is MimicFollower +>mimic : MimicGuard +>isFollower : () => this is MimicFollower + +if (mimic.isFollower()) { +>mimic.isFollower() : boolean +>mimic.isFollower : () => this is MimicFollower +>mimic : MimicGuard +>isFollower : () => this is MimicFollower + + mimic.follow(); +>mimic.follow() : void +>mimic.follow : () => void +>mimic : MimicFollower +>follow : () => void + + mimic.isFollower = a.isFollower; +>mimic.isFollower = a.isFollower : () => this is FollowerGuard +>mimic.isFollower : () => this is MimicFollower +>mimic : MimicFollower +>isFollower : () => this is MimicFollower +>a.isFollower : () => this is FollowerGuard +>a : RoyalGuard +>isFollower : () => this is FollowerGuard +} diff --git a/tests/baselines/reference/typeGuardFunctionOfFormThisErrors.errors.txt b/tests/baselines/reference/typeGuardFunctionOfFormThisErrors.errors.txt new file mode 100644 index 0000000000000..9d2e9fdd48eb2 --- /dev/null +++ b/tests/baselines/reference/typeGuardFunctionOfFormThisErrors.errors.txt @@ -0,0 +1,76 @@ +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors.ts(23,1): error TS2322: Type '() => this is LeadGuard' is not assignable to type '() => this is FollowerGuard'. + Type 'LeadGuard' is not assignable to type 'FollowerGuard'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors.ts(24,1): error TS2322: Type '() => this is FollowerGuard' is not assignable to type '() => this is LeadGuard'. + Type 'FollowerGuard' is not assignable to type 'LeadGuard'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors.ts(26,1): error TS2322: Type '() => this is LeadGuard' is not assignable to type '() => this is FollowerGuard'. + Type 'LeadGuard' is not assignable to type 'FollowerGuard'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors.ts(27,1): error TS2322: Type '() => this is FollowerGuard' is not assignable to type '() => this is LeadGuard'. + Type 'FollowerGuard' is not assignable to type 'LeadGuard'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors.ts(29,32): error TS2526: A 'this' type is available only in a non-static member of a class or interface. + + +==== tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors.ts (5 errors) ==== + class RoyalGuard { + isLeader(): this is LeadGuard { + return this instanceof LeadGuard; + } + isFollower(): this is FollowerGuard { + return this instanceof FollowerGuard; + } + } + + class LeadGuard extends RoyalGuard { + lead(): void {}; + } + + class FollowerGuard extends RoyalGuard { + follow(): void {}; + } + + interface GuardInterface extends RoyalGuard {} + let a: RoyalGuard = new FollowerGuard(); + let b: GuardInterface = new LeadGuard(); + + // Mismatched guards shouldn't be assignable + b.isFollower = b.isLeader; + ~~~~~~~~~~~~ +!!! error TS2322: Type '() => this is LeadGuard' is not assignable to type '() => this is FollowerGuard'. +!!! error TS2322: Type 'LeadGuard' is not assignable to type 'FollowerGuard'. + b.isLeader = b.isFollower; + ~~~~~~~~~~ +!!! error TS2322: Type '() => this is FollowerGuard' is not assignable to type '() => this is LeadGuard'. +!!! error TS2322: Type 'FollowerGuard' is not assignable to type 'LeadGuard'. + + a.isFollower = a.isLeader; + ~~~~~~~~~~~~ +!!! error TS2322: Type '() => this is LeadGuard' is not assignable to type '() => this is FollowerGuard'. +!!! error TS2322: Type 'LeadGuard' is not assignable to type 'FollowerGuard'. + a.isLeader = a.isFollower; + ~~~~~~~~~~ +!!! error TS2322: Type '() => this is FollowerGuard' is not assignable to type '() => this is LeadGuard'. +!!! error TS2322: Type 'FollowerGuard' is not assignable to type 'LeadGuard'. + + function invalidGuard(c: any): this is number { + ~~~~ +!!! error TS2526: A 'this' type is available only in a non-static member of a class or interface. + return false; + } + + let c: number | number[]; + if (invalidGuard(c)) { + c; + } + else { + c; + } + + let holder = {invalidGuard}; + + if (holder.invalidGuard(c)) { + c; + holder; + } + else { + c; + holder; + } \ No newline at end of file diff --git a/tests/baselines/reference/typeGuardFunctionOfFormThisErrors.js b/tests/baselines/reference/typeGuardFunctionOfFormThisErrors.js new file mode 100644 index 0000000000000..f11716daebdea --- /dev/null +++ b/tests/baselines/reference/typeGuardFunctionOfFormThisErrors.js @@ -0,0 +1,113 @@ +//// [typeGuardFunctionOfFormThisErrors.ts] +class RoyalGuard { + isLeader(): this is LeadGuard { + return this instanceof LeadGuard; + } + isFollower(): this is FollowerGuard { + return this instanceof FollowerGuard; + } +} + +class LeadGuard extends RoyalGuard { + lead(): void {}; +} + +class FollowerGuard extends RoyalGuard { + follow(): void {}; +} + +interface GuardInterface extends RoyalGuard {} +let a: RoyalGuard = new FollowerGuard(); +let b: GuardInterface = new LeadGuard(); + +// Mismatched guards shouldn't be assignable +b.isFollower = b.isLeader; +b.isLeader = b.isFollower; + +a.isFollower = a.isLeader; +a.isLeader = a.isFollower; + +function invalidGuard(c: any): this is number { + return false; +} + +let c: number | number[]; +if (invalidGuard(c)) { + c; +} +else { + c; +} + +let holder = {invalidGuard}; + +if (holder.invalidGuard(c)) { + c; + holder; +} +else { + c; + holder; +} + +//// [typeGuardFunctionOfFormThisErrors.js] +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var RoyalGuard = (function () { + function RoyalGuard() { + } + RoyalGuard.prototype.isLeader = function () { + return this instanceof LeadGuard; + }; + RoyalGuard.prototype.isFollower = function () { + return this instanceof FollowerGuard; + }; + return RoyalGuard; +})(); +var LeadGuard = (function (_super) { + __extends(LeadGuard, _super); + function LeadGuard() { + _super.apply(this, arguments); + } + LeadGuard.prototype.lead = function () { }; + ; + return LeadGuard; +})(RoyalGuard); +var FollowerGuard = (function (_super) { + __extends(FollowerGuard, _super); + function FollowerGuard() { + _super.apply(this, arguments); + } + FollowerGuard.prototype.follow = function () { }; + ; + return FollowerGuard; +})(RoyalGuard); +var a = new FollowerGuard(); +var b = new LeadGuard(); +// Mismatched guards shouldn't be assignable +b.isFollower = b.isLeader; +b.isLeader = b.isFollower; +a.isFollower = a.isLeader; +a.isLeader = a.isFollower; +function invalidGuard(c) { + return false; +} +var c; +if (invalidGuard(c)) { + c; +} +else { + c; +} +var holder = { invalidGuard: invalidGuard }; +if (holder.invalidGuard(c)) { + c; + holder; +} +else { + c; + holder; +} diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThis.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThis.ts new file mode 100644 index 0000000000000..bc7bbce381647 --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThis.ts @@ -0,0 +1,134 @@ +class RoyalGuard { + isLeader(): this is LeadGuard { + return this instanceof LeadGuard; + } + isFollower(): this is FollowerGuard { + return this instanceof FollowerGuard; + } +} + +class LeadGuard extends RoyalGuard { + lead(): void {}; +} + +class FollowerGuard extends RoyalGuard { + follow(): void {}; +} + +let a: RoyalGuard = new FollowerGuard(); +if (a.isLeader()) { + a.lead(); +} +else if (a.isFollower()) { + a.follow(); +} + +interface GuardInterface extends RoyalGuard {} + +let b: GuardInterface; +if (b.isLeader()) { + b.lead(); +} +else if (b.isFollower()) { + b.follow(); +} + +if (((a.isLeader)())) { + a.lead(); +} +else if (((a).isFollower())) { + a.follow(); +} + +if (((a["isLeader"])())) { + a.lead(); +} +else if (((a)["isFollower"]())) { + a.follow(); +} + +var holder2 = {a}; + +if (holder2.a.isLeader()) { + holder2.a; +} +else { + holder2.a; +} + +class ArrowGuard { + isElite = (): this is ArrowElite => { + return this instanceof ArrowElite; + } + isMedic = (): this is ArrowMedic => { + return this instanceof ArrowMedic; + } +} + +class ArrowElite extends ArrowGuard { + defend(): void {} +} + +class ArrowMedic extends ArrowGuard { + heal(): void {} +} + +let guard = new ArrowGuard(); +if (guard.isElite()) { + guard.defend(); +} +else if (guard.isMedic()) { + guard.heal(); +} + +interface Supplies { + spoiled: boolean; +} + +interface Sundries { + broken: boolean; +} + +interface Crate { + contents: T; + volume: number; + isSupplies(): this is Crate; + isSundries(): this is Crate; +} + +let crate: Crate<{}>; + +if (crate.isSundries()) { + crate.contents.broken = true; +} +else if (crate.isSupplies()) { + crate.contents.spoiled = true; +} + +// Matching guards should be assignable + +a.isFollower = b.isFollower; +a.isLeader = b.isLeader; + +class MimicGuard { + isLeader(): this is MimicLeader { return this instanceof MimicLeader; }; + isFollower(): this is MimicFollower { return this instanceof MimicFollower; }; +} + +class MimicLeader extends MimicGuard { + lead(): void {} +} + +class MimicFollower extends MimicGuard { + follow(): void {} +} + +let mimic = new MimicGuard(); + +a.isLeader = mimic.isLeader; +a.isFollower = mimic.isFollower; + +if (mimic.isFollower()) { + mimic.follow(); + mimic.isFollower = a.isFollower; +} \ No newline at end of file diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors.ts new file mode 100644 index 0000000000000..2e03781952d2d --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors.ts @@ -0,0 +1,50 @@ +class RoyalGuard { + isLeader(): this is LeadGuard { + return this instanceof LeadGuard; + } + isFollower(): this is FollowerGuard { + return this instanceof FollowerGuard; + } +} + +class LeadGuard extends RoyalGuard { + lead(): void {}; +} + +class FollowerGuard extends RoyalGuard { + follow(): void {}; +} + +interface GuardInterface extends RoyalGuard {} +let a: RoyalGuard = new FollowerGuard(); +let b: GuardInterface = new LeadGuard(); + +// Mismatched guards shouldn't be assignable +b.isFollower = b.isLeader; +b.isLeader = b.isFollower; + +a.isFollower = a.isLeader; +a.isLeader = a.isFollower; + +function invalidGuard(c: any): this is number { + return false; +} + +let c: number | number[]; +if (invalidGuard(c)) { + c; +} +else { + c; +} + +let holder = {invalidGuard}; + +if (holder.invalidGuard(c)) { + c; + holder; +} +else { + c; + holder; +} \ No newline at end of file From d7208e81d61ec3c5154c766f594f8fa224f9627f Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 3 Dec 2015 13:46:37 -0800 Subject: [PATCH 03/15] feedback from pr --- src/compiler/checker.ts | 37 +++++++----- src/compiler/diagnosticMessages.json | 2 +- .../reference/typeGuardFunctionOfFormThis.js | 58 ++++++++++++++++++- .../typeGuardFunctionOfFormThis.symbols | 1 + .../typeGuardFunctionOfFormThis.types | 1 + ...peGuardFunctionOfFormThisErrors.errors.txt | 17 +++++- .../typeGuardFunctionOfFormThisErrors.js | 39 +++++++++++++ .../typeGuards/typeGuardFunctionOfFormThis.ts | 3 +- .../typeGuardFunctionOfFormThisErrors.ts | 10 ++++ 9 files changed, 148 insertions(+), 20 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a5a904ab3a91f..a447b67b3362d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4732,12 +4732,11 @@ namespace ts { } function cloneTypePredicate(predicate: TypePredicate, mapper: TypeMapper): ThisTypePredicate | IdentifierTypePredicate { - if (predicate.kind === TypePredicateKind.Identifier) { - const identifierPredicate = predicate as IdentifierTypePredicate; + if (isIdentifierTypePredicate(predicate)) { return { kind: TypePredicateKind.Identifier, - parameterName: identifierPredicate.parameterName, - parameterIndex: identifierPredicate.parameterIndex, + parameterName: predicate.parameterName, + parameterIndex: predicate.parameterIndex, type: instantiateType(predicate.type, mapper) } as IdentifierTypePredicate; } @@ -4751,18 +4750,14 @@ namespace ts { function instantiateSignature(signature: Signature, mapper: TypeMapper, eraseTypeParameters?: boolean): Signature { let freshTypeParameters: TypeParameter[]; - let freshTypePredicate: ThisTypePredicate | IdentifierTypePredicate; if (signature.typeParameters && !eraseTypeParameters) { freshTypeParameters = instantiateList(signature.typeParameters, mapper, instantiateTypeParameter); mapper = combineTypeMappers(createTypeMapper(signature.typeParameters, freshTypeParameters), mapper); } - if (signature.typePredicate) { - freshTypePredicate = cloneTypePredicate(signature.typePredicate, mapper); - } const result = createSignature(signature.declaration, freshTypeParameters, instantiateList(signature.parameters, mapper, instantiateSymbol), instantiateType(signature.resolvedReturnType, mapper), - freshTypePredicate, + signature.typePredicate && cloneTypePredicate(signature.typePredicate, mapper), signature.minArgumentCount, signature.hasRestParameter, signature.hasStringLiterals); result.target = signature; result.mapper = mapper; @@ -6752,7 +6747,7 @@ namespace ts { const predicate = signature.typePredicate; if (isIdentifierTypePredicate(predicate)) { if (expr.arguments[predicate.parameterIndex] && - getSymbolAtLocation(expr.arguments[predicate.parameterIndex]) === symbol) { + getSymbolAtTypePredicatePosition(expr.arguments[predicate.parameterIndex]) === symbol) { return getNarrowedType(type, predicate.type, assumeTrue); } } @@ -6762,18 +6757,28 @@ namespace ts { if (expression.kind === SyntaxKind.ElementAccessExpression || expression.kind === SyntaxKind.PropertyAccessExpression) { const accessExpression = expression as ElementAccessExpression | PropertyAccessExpression; const possibleIdentifier = skipParenthesizedNodes(accessExpression.expression); - if (possibleIdentifier.kind === SyntaxKind.Identifier && getSymbolAtLocation(possibleIdentifier) === symbol) { + if (possibleIdentifier.kind === SyntaxKind.Identifier && getSymbolAtTypePredicatePosition(possibleIdentifier) === symbol) { return getNarrowedType(type, predicate.type, assumeTrue); } } } return type; + } - function skipParenthesizedNodes(expression: Expression): Expression { - while (expression.kind === SyntaxKind.ParenthesizedExpression) { - expression = (expression as ParenthesizedExpression).expression; - } - return expression; + function skipParenthesizedNodes(expression: Expression): Expression { + while (expression.kind === SyntaxKind.ParenthesizedExpression) { + expression = (expression as ParenthesizedExpression).expression; + } + return expression; + } + + function getSymbolAtTypePredicatePosition(expr: Expression): Symbol { + expr = skipParenthesizedNodes(expr); + switch (expr.kind) { + case SyntaxKind.Identifier: + case SyntaxKind.PropertyAccessExpression: + case SyntaxKind.QualifiedName: + return getSymbolOfEntityNameOrPropertyAccessExpression(expr as Node as (EntityName | PropertyAccessExpression)); } } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index f48a37968ca22..081bfc2385df9 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1632,7 +1632,7 @@ "category": "Error", "code": 2517 }, - "A this based type guard is not assignable to a parameter based type guard": { + "A 'this'-based type guard is not assignable to a parameter-based type guard": { "category": "Error", "code": 2518 }, diff --git a/tests/baselines/reference/typeGuardFunctionOfFormThis.js b/tests/baselines/reference/typeGuardFunctionOfFormThis.js index 16ac876be8c08..bc91defc2442c 100644 --- a/tests/baselines/reference/typeGuardFunctionOfFormThis.js +++ b/tests/baselines/reference/typeGuardFunctionOfFormThis.js @@ -132,7 +132,8 @@ a.isFollower = mimic.isFollower; if (mimic.isFollower()) { mimic.follow(); mimic.isFollower = a.isFollower; -} +} + //// [typeGuardFunctionOfFormThis.js] var __extends = (this && this.__extends) || function (d, b) { @@ -279,3 +280,58 @@ if (mimic.isFollower()) { mimic.follow(); mimic.isFollower = a.isFollower; } + + +//// [typeGuardFunctionOfFormThis.d.ts] +declare class RoyalGuard { + isLeader(): this is LeadGuard; + isFollower(): this is FollowerGuard; +} +declare class LeadGuard extends RoyalGuard { + lead(): void; +} +declare class FollowerGuard extends RoyalGuard { + follow(): void; +} +declare let a: RoyalGuard; +interface GuardInterface extends RoyalGuard { +} +declare let b: GuardInterface; +declare var holder2: { + a: RoyalGuard; +}; +declare class ArrowGuard { + isElite: () => this is ArrowElite; + isMedic: () => this is ArrowMedic; +} +declare class ArrowElite extends ArrowGuard { + defend(): void; +} +declare class ArrowMedic extends ArrowGuard { + heal(): void; +} +declare let guard: ArrowGuard; +interface Supplies { + spoiled: boolean; +} +interface Sundries { + broken: boolean; +} +interface Crate { + contents: T; + volume: number; + isSupplies(): this is Crate; + isSundries(): this is Crate; +} +declare let crate: Crate<{}>; +declare class MimicGuard { + isLeader(): this is MimicLeader; + isFollower(): this is MimicFollower; +} +declare class MimicLeader extends MimicGuard { + lead(): void; +} +declare class MimicFollower extends MimicGuard { + follow(): void; +} +declare let mimic: MimicGuard; diff --git a/tests/baselines/reference/typeGuardFunctionOfFormThis.symbols b/tests/baselines/reference/typeGuardFunctionOfFormThis.symbols index 9bda32c455700..84c07a17f0e94 100644 --- a/tests/baselines/reference/typeGuardFunctionOfFormThis.symbols +++ b/tests/baselines/reference/typeGuardFunctionOfFormThis.symbols @@ -369,3 +369,4 @@ if (mimic.isFollower()) { >a : Symbol(a, Decl(typeGuardFunctionOfFormThis.ts, 17, 3)) >isFollower : Symbol(RoyalGuard.isFollower, Decl(typeGuardFunctionOfFormThis.ts, 3, 5)) } + diff --git a/tests/baselines/reference/typeGuardFunctionOfFormThis.types b/tests/baselines/reference/typeGuardFunctionOfFormThis.types index fbe862ab98ba9..fa5becc7790b1 100644 --- a/tests/baselines/reference/typeGuardFunctionOfFormThis.types +++ b/tests/baselines/reference/typeGuardFunctionOfFormThis.types @@ -425,3 +425,4 @@ if (mimic.isFollower()) { >a : RoyalGuard >isFollower : () => this is FollowerGuard } + diff --git a/tests/baselines/reference/typeGuardFunctionOfFormThisErrors.errors.txt b/tests/baselines/reference/typeGuardFunctionOfFormThisErrors.errors.txt index 9d2e9fdd48eb2..605282f19bb27 100644 --- a/tests/baselines/reference/typeGuardFunctionOfFormThisErrors.errors.txt +++ b/tests/baselines/reference/typeGuardFunctionOfFormThisErrors.errors.txt @@ -7,9 +7,11 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors.ts(27,1): error TS2322: Type '() => this is FollowerGuard' is not assignable to type '() => this is LeadGuard'. Type 'FollowerGuard' is not assignable to type 'LeadGuard'. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors.ts(29,32): error TS2526: A 'this' type is available only in a non-static member of a class or interface. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors.ts(55,7): error TS2339: Property 'follow' does not exist on type 'RoyalGuard'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors.ts(58,7): error TS2339: Property 'lead' does not exist on type 'RoyalGuard'. -==== tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors.ts (5 errors) ==== +==== tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors.ts (7 errors) ==== class RoyalGuard { isLeader(): this is LeadGuard { return this instanceof LeadGuard; @@ -73,4 +75,17 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors else { c; holder; + } + + let detached = a.isFollower; + + if (detached()) { + a.follow(); + ~~~~~~ +!!! error TS2339: Property 'follow' does not exist on type 'RoyalGuard'. + } + else { + a.lead(); + ~~~~ +!!! error TS2339: Property 'lead' does not exist on type 'RoyalGuard'. } \ No newline at end of file diff --git a/tests/baselines/reference/typeGuardFunctionOfFormThisErrors.js b/tests/baselines/reference/typeGuardFunctionOfFormThisErrors.js index f11716daebdea..0495e3cd77380 100644 --- a/tests/baselines/reference/typeGuardFunctionOfFormThisErrors.js +++ b/tests/baselines/reference/typeGuardFunctionOfFormThisErrors.js @@ -48,6 +48,15 @@ if (holder.invalidGuard(c)) { else { c; holder; +} + +let detached = a.isFollower; + +if (detached()) { + a.follow(); +} +else { + a.lead(); } //// [typeGuardFunctionOfFormThisErrors.js] @@ -111,3 +120,33 @@ else { c; holder; } +var detached = a.isFollower; +if (detached()) { + a.follow(); +} +else { + a.lead(); +} + + +//// [typeGuardFunctionOfFormThisErrors.d.ts] +declare class RoyalGuard { + isLeader(): this is LeadGuard; + isFollower(): this is FollowerGuard; +} +declare class LeadGuard extends RoyalGuard { + lead(): void; +} +declare class FollowerGuard extends RoyalGuard { + follow(): void; +} +interface GuardInterface extends RoyalGuard { +} +declare let a: RoyalGuard; +declare let b: GuardInterface; +declare function invalidGuard(c: any): this is number; +declare let c: number | number[]; +declare let holder: { + invalidGuard: (c: any) => this is number; +}; +declare let detached: () => this is FollowerGuard; diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThis.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThis.ts index bc7bbce381647..c053ec55d079f 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThis.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThis.ts @@ -1,3 +1,4 @@ +// @declaration: true class RoyalGuard { isLeader(): this is LeadGuard { return this instanceof LeadGuard; @@ -131,4 +132,4 @@ a.isFollower = mimic.isFollower; if (mimic.isFollower()) { mimic.follow(); mimic.isFollower = a.isFollower; -} \ No newline at end of file +} diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors.ts index 2e03781952d2d..1c449e624f15c 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors.ts @@ -1,3 +1,4 @@ +// @declaration: true class RoyalGuard { isLeader(): this is LeadGuard { return this instanceof LeadGuard; @@ -47,4 +48,13 @@ if (holder.invalidGuard(c)) { else { c; holder; +} + +let detached = a.isFollower; + +if (detached()) { + a.follow(); +} +else { + a.lead(); } \ No newline at end of file From cfe2cc36085dfaeeba230ea9f538c9ba04431e4b Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 3 Dec 2015 14:10:22 -0800 Subject: [PATCH 04/15] utilize type guard on type guard logic more --- src/compiler/checker.ts | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a447b67b3362d..ebfc29d955007 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2057,9 +2057,10 @@ namespace ts { writeSpace(writer); let returnType: Type; - if (signature.typePredicate) { - if (signature.typePredicate.kind === TypePredicateKind.Identifier) { - writer.writeParameter((signature.typePredicate as IdentifierTypePredicate).parameterName); + const predicate = signature.typePredicate; + if (predicate) { + if (isIdentifierTypePredicate(predicate)) { + writer.writeParameter(predicate.parameterName); } else { writeKeyword(writer, SyntaxKind.ThisKeyword); @@ -2067,7 +2068,7 @@ namespace ts { writeSpace(writer); writeKeyword(writer, SyntaxKind.IsKeyword); writeSpace(writer); - returnType = signature.typePredicate.type; + returnType = predicate.type; } else { returnType = getReturnTypeOfSignature(signature); @@ -5572,17 +5573,19 @@ namespace ts { } if (source.typePredicate && target.typePredicate) { + const sourcePredicate = source.typePredicate as IdentifierTypePredicate; + const targetPredicate = target.typePredicate as IdentifierTypePredicate; if (source.typePredicate.kind !== target.typePredicate.kind) { if (reportErrors) { reportError(Diagnostics.A_this_based_type_guard_is_not_assignable_to_a_parameter_based_type_guard); } return Ternary.False; } - if (source.typePredicate.kind === TypePredicateKind.This) { - if (!isTypeIdenticalTo(source.typePredicate.type, target.typePredicate.type)) { + if (!(isIdentifierTypePredicate(sourcePredicate) && isIdentifierTypePredicate(targetPredicate))) { + if (!isTypeIdenticalTo(sourcePredicate.type, targetPredicate.type)) { if (reportErrors) { - const sourceTypeText = typeToString(source.typePredicate.type); - const targetTypeText = typeToString(target.typePredicate.type); + const sourceTypeText = typeToString(sourcePredicate.type); + const targetTypeText = typeToString(targetPredicate.type); reportError(Diagnostics.Type_0_is_not_assignable_to_type_1, sourceTypeText, targetTypeText); @@ -5591,8 +5594,6 @@ namespace ts { } } else { - const sourcePredicate = source.typePredicate as IdentifierTypePredicate; - const targetPredicate = target.typePredicate as IdentifierTypePredicate; const hasDifferentParameterIndex = sourcePredicate.parameterIndex !== targetPredicate.parameterIndex; let hasDifferentTypes: boolean; if (hasDifferentParameterIndex || From d51dd84f6c8a7e422b29c15dccbac7fef4e0d2f2 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 3 Dec 2015 14:18:44 -0800 Subject: [PATCH 05/15] remove unneeded cast --- src/compiler/checker.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ebfc29d955007..9c9474eed4db6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5573,8 +5573,8 @@ namespace ts { } if (source.typePredicate && target.typePredicate) { - const sourcePredicate = source.typePredicate as IdentifierTypePredicate; - const targetPredicate = target.typePredicate as IdentifierTypePredicate; + const sourcePredicate = source.typePredicate; + const targetPredicate = target.typePredicate; if (source.typePredicate.kind !== target.typePredicate.kind) { if (reportErrors) { reportError(Diagnostics.A_this_based_type_guard_is_not_assignable_to_a_parameter_based_type_guard); From b9f310d4f2eef0f0f494e2c742707fea8fce9c01 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 3 Dec 2015 18:21:36 -0800 Subject: [PATCH 06/15] first pass at this type predicate members --- src/compiler/checker.ts | 251 +++++++++++------- src/compiler/diagnosticMessages.json | 4 + src/compiler/types.ts | 6 + src/compiler/utilities.ts | 2 +- .../typeGuardFunctionErrors.errors.txt | 4 +- .../reference/typeGuardOfFormThisMember.js | 130 +++++++++ .../typeGuardOfFormThisMember.symbols | 126 +++++++++ .../reference/typeGuardOfFormThisMember.types | 136 ++++++++++ .../typeGuards/typeGuardOfFormThisMember.ts | 43 +++ 9 files changed, 610 insertions(+), 92 deletions(-) create mode 100644 tests/baselines/reference/typeGuardOfFormThisMember.js create mode 100644 tests/baselines/reference/typeGuardOfFormThisMember.symbols create mode 100644 tests/baselines/reference/typeGuardOfFormThisMember.types create mode 100644 tests/cases/conformance/expressions/typeGuards/typeGuardOfFormThisMember.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9c9474eed4db6..a1d13faff5a82 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1670,10 +1670,16 @@ namespace ts { function writeType(type: Type, flags: TypeFormatFlags) { // Write undefined/null type as any if (type.flags & TypeFlags.Intrinsic) { - // Special handling for unknown / resolving types, they should show up as any and not unknown or __resolving - writer.writeKeyword(!(globalFlags & TypeFormatFlags.WriteOwnNameForAnyLike) && isTypeAny(type) - ? "any" - : (type).intrinsicName); + if (type.flags & TypeFlags.PredicateType) { + buildTypePredicateDisplay(writer, (type as PredicateType).predicate); + buildTypeDisplay((type as PredicateType).predicate.type, writer, enclosingDeclaration, flags, symbolStack); + } + else { + // Special handling for unknown / resolving types, they should show up as any and not unknown or __resolving + writer.writeKeyword(!(globalFlags & TypeFormatFlags.WriteOwnNameForAnyLike) && isTypeAny(type) + ? "any" + : (type).intrinsicName); + } } else if (type.flags & TypeFlags.ThisType) { if (inObjectTypeLiteral) { @@ -2046,6 +2052,18 @@ namespace ts { writePunctuation(writer, SyntaxKind.CloseParenToken); } + function buildTypePredicateDisplay(writer: SymbolWriter, predicate: TypePredicate) { + if (isIdentifierTypePredicate(predicate)) { + writer.writeParameter(predicate.parameterName); + } + else { + writeKeyword(writer, SyntaxKind.ThisKeyword); + } + writeSpace(writer); + writeKeyword(writer, SyntaxKind.IsKeyword); + writeSpace(writer); + } + function buildReturnTypeDisplay(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) { if (flags & TypeFormatFlags.WriteArrowStyleSignature) { writeSpace(writer); @@ -2059,15 +2077,7 @@ namespace ts { let returnType: Type; const predicate = signature.typePredicate; if (predicate) { - if (isIdentifierTypePredicate(predicate)) { - writer.writeParameter(predicate.parameterName); - } - else { - writeKeyword(writer, SyntaxKind.ThisKeyword); - } - writeSpace(writer); - writeKeyword(writer, SyntaxKind.IsKeyword); - writeSpace(writer); + buildTypePredicateDisplay(writer, predicate); returnType = predicate.type; } else { @@ -3850,6 +3860,24 @@ namespace ts { return false; } + function createTypePredicateFromTypePredicateNode(node: TypePredicateNode): IdentifierTypePredicate | ThisTypePredicate { + if (node.parameterName.kind === SyntaxKind.Identifier) { + const parameterName = node.parameterName as Identifier; + return { + kind: TypePredicateKind.Identifier, + parameterName: parameterName ? parameterName.text : undefined, + parameterIndex: parameterName ? getTypePredicateParameterIndex((node.parent as SignatureDeclaration).parameters, parameterName) : undefined, + type: getTypeFromTypeNode(node.type) + } as IdentifierTypePredicate; + } + else { + return { + kind: TypePredicateKind.This, + type: getTypeFromTypeNode(node.type) + } as ThisTypePredicate; + } + } + function getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature { const links = getNodeLinks(declaration); if (!links.resolvedSignature) { @@ -3897,22 +3925,7 @@ namespace ts { else if (declaration.type) { returnType = getTypeFromTypeNode(declaration.type); if (declaration.type.kind === SyntaxKind.TypePredicate) { - const typePredicateNode = declaration.type as TypePredicateNode; - if (typePredicateNode.parameterName.kind === SyntaxKind.Identifier) { - const parameterName = typePredicateNode.parameterName as Identifier; - typePredicate = { - kind: TypePredicateKind.Identifier, - parameterName: parameterName ? parameterName.text : undefined, - parameterIndex: parameterName ? getTypePredicateParameterIndex(declaration.parameters, parameterName) : undefined, - type: getTypeFromTypeNode(typePredicateNode.type) - }; - } - else { - typePredicate = { - kind: TypePredicateKind.This, - type: getTypeFromTypeNode(typePredicateNode.type) - } as ThisTypePredicate; - } + typePredicate = createTypePredicateFromTypePredicateNode(declaration.type as TypePredicateNode); } } else { @@ -4588,6 +4601,26 @@ namespace ts { return links.resolvedType; } + function getPredicateType(node: TypePredicateNode): Type { + if (!(node.parent.kind === SyntaxKind.PropertyDeclaration || node.parent.kind === SyntaxKind.GetAccessor)) { + return booleanType; + } + else { + const type = createType(TypeFlags.Boolean | TypeFlags.PredicateType) as PredicateType; + type.symbol = getSymbolOfNode(node); + type.predicate = createTypePredicateFromTypePredicateNode(node) as ThisTypePredicate; + return type; + } + } + + function getTypeFromPredicateTypeNode(node: TypePredicateNode): Type { + const links = getNodeLinks(node); + if (!links.resolvedType) { + links.resolvedType = getPredicateType(node); + } + return links.resolvedType; + } + function getTypeFromTypeNode(node: TypeNode): Type { switch (node.kind) { case SyntaxKind.AnyKeyword: @@ -4609,7 +4642,7 @@ namespace ts { case SyntaxKind.TypeReference: return getTypeFromTypeReference(node); case SyntaxKind.TypePredicate: - return booleanType; + return getTypeFromPredicateTypeNode(node); case SyntaxKind.ExpressionWithTypeArguments: return getTypeFromTypeReference(node); case SyntaxKind.TypeQuery: @@ -4998,6 +5031,7 @@ namespace ts { if (relation === assignableRelation) { if (isTypeAny(source)) return Ternary.True; if (source === numberType && target.flags & TypeFlags.Enum) return Ternary.True; + if (source.flags & TypeFlags.Boolean && target.flags & TypeFlags.Boolean && (source.flags & TypeFlags.PredicateType || target.flags & TypeFlags.PredicateType)) return Ternary.True; } if (source.flags & TypeFlags.FreshObjectLiteral) { @@ -6747,20 +6781,37 @@ namespace ts { } const predicate = signature.typePredicate; if (isIdentifierTypePredicate(predicate)) { - if (expr.arguments[predicate.parameterIndex] && - getSymbolAtTypePredicatePosition(expr.arguments[predicate.parameterIndex]) === symbol) { + const callExpression = expr as CallExpression; + if (callExpression.arguments[predicate.parameterIndex] && + getSymbolAtTypePredicatePosition(callExpression.arguments[predicate.parameterIndex]) === symbol) { return getNarrowedType(type, predicate.type, assumeTrue); } } else { const expression = skipParenthesizedNodes(expr.expression); + return narrowTypeByThisTypePredicate(type, predicate, expression, assumeTrue); + } + return type; + } - if (expression.kind === SyntaxKind.ElementAccessExpression || expression.kind === SyntaxKind.PropertyAccessExpression) { - const accessExpression = expression as ElementAccessExpression | PropertyAccessExpression; - const possibleIdentifier = skipParenthesizedNodes(accessExpression.expression); - if (possibleIdentifier.kind === SyntaxKind.Identifier && getSymbolAtTypePredicatePosition(possibleIdentifier) === symbol) { - return getNarrowedType(type, predicate.type, assumeTrue); - } + function narrowTypeByTypePredicateMember(type: Type, expr: ElementAccessExpression | PropertyAccessExpression, assumeTrue: boolean): Type { + if (type.flags & TypeFlags.Any) { + return type; + } + const memberType = getTypeOfExpression(expr); + if (!(memberType.flags & TypeFlags.PredicateType)) { + return type; + } + + return narrowTypeByThisTypePredicate(type, (memberType as PredicateType).predicate, expr, assumeTrue); + } + + function narrowTypeByThisTypePredicate(type: Type, predicate: ThisTypePredicate, expression: Expression, assumeTrue: boolean): Type { + if (expression.kind === SyntaxKind.ElementAccessExpression || expression.kind === SyntaxKind.PropertyAccessExpression) { + const accessExpression = expression as ElementAccessExpression | PropertyAccessExpression; + const possibleIdentifier = skipParenthesizedNodes(accessExpression.expression); + if (possibleIdentifier.kind === SyntaxKind.Identifier && getSymbolAtTypePredicatePosition(possibleIdentifier) === symbol) { + return getNarrowedType(type, predicate.type, assumeTrue); } } return type; @@ -6811,6 +6862,9 @@ namespace ts { return narrowType(type, (expr).operand, !assumeTrue); } break; + case SyntaxKind.ElementAccessExpression: + case SyntaxKind.PropertyAccessExpression: + return narrowTypeByTypePredicateMember(type, expr as (ElementAccessExpression | PropertyAccessExpression), assumeTrue); } return type; } @@ -11018,6 +11072,18 @@ namespace ts { return false; } + function isInLegalThisTypePredicatePosition(node: Node): boolean { + if (isInLegalTypePredicatePosition(node)) { + return true; + } + switch (node.parent.kind) { + case SyntaxKind.PropertyDeclaration: + case SyntaxKind.GetAccessor: + return node === (node.parent).type; + } + return false; + } + function checkSignatureDeclaration(node: SignatureDeclaration) { // Grammar checking if (node.kind === SyntaxKind.IndexSignature) { @@ -11038,63 +11104,67 @@ namespace ts { if (node.type.kind === SyntaxKind.TypePredicate) { const typePredicate = getSignatureFromDeclaration(node).typePredicate; const typePredicateNode = node.type; - if (isInLegalTypePredicatePosition(typePredicateNode)) { - if (isIdentifierTypePredicate(typePredicate)) { - 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); - } + if (isIdentifierTypePredicate(typePredicate)) { + if (!isInLegalTypePredicatePosition(typePredicateNode)) { + error(typePredicateNode, + Diagnostics.A_type_predicate_is_only_allowed_in_return_type_position_for_functions_and_methods); + return; + } + if (typePredicate.parameterIndex >= 0) { + if (node.parameters[typePredicate.parameterIndex].dotDotDotToken) { + error(typePredicateNode.parameterName, + Diagnostics.A_type_predicate_cannot_reference_a_rest_parameter); } - 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); - } + 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 (!hasReportedError) { - error(typePredicateNode.parameterName, - Diagnostics.Cannot_find_parameter_0, - typePredicate.parameterName); + 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); } } - } - else { - // Reuse this type diagnostics on the this type node to determine if a this type predicate is valid - getTypeFromThisTypeNode(typePredicateNode.parameterName as ThisTypeNode); + 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); + if (!isInLegalThisTypePredicatePosition(typePredicateNode)) { + error(typePredicateNode, + Diagnostics.A_this_based_type_predicate_is_only_allowed_in_class_or_interface_members_get_accessors_or_return_type_positions_for_functions_and_methods); + return; + } + // Reuse this type diagnostics on the this type node to determine if a this type predicate is valid + getTypeFromThisTypeNode(typePredicateNode.parameterName as ThisTypeNode); } } else { @@ -14222,9 +14292,12 @@ namespace ts { } function checkTypePredicate(node: TypePredicateNode) { - if (!isInLegalTypePredicatePosition(node)) { + if (node.parameterName.kind === SyntaxKind.Identifier && !isInLegalTypePredicatePosition(node)) { error(node, Diagnostics.A_type_predicate_is_only_allowed_in_return_type_position_for_functions_and_methods); } + else if (node.parameterName.kind === SyntaxKind.ThisType && (!isInLegalThisTypePredicatePosition(node) || getTypeFromThisTypeNode(node.parameterName as ThisTypeNode) === unknownType)) { + error(node, Diagnostics.A_this_based_type_predicate_is_only_allowed_in_class_or_interface_members_get_accessors_or_return_type_positions_for_functions_and_methods); + } } function checkSourceElement(node: Node): void { diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 081bfc2385df9..0ca8a93d071f9 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1636,6 +1636,10 @@ "category": "Error", "code": 2518 }, + "A 'this'-based type predicate is only allowed in class or interface 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 diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 4bde87d0ff5b2..d8e6291ac02bf 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2108,6 +2108,7 @@ namespace ts { ESSymbol = 0x01000000, // Type of symbol primitive introduced in ES6 ThisType = 0x02000000, // This type ObjectLiteralPatternWithComputedProperties = 0x04000000, // Object literal type implied by binding pattern has computed properties + PredicateType = 0x08000000, /* @internal */ Intrinsic = Any | String | Number | Boolean | ESSymbol | Void | Undefined | Null, @@ -2140,6 +2141,11 @@ namespace ts { intrinsicName: string; // Name of intrinsic type } + // Predicate types (TypeFlags.Predicate) + export interface PredicateType extends Type { + predicate: ThisTypePredicate; + } + // String literal types (TypeFlags.StringLiteral) export interface StringLiteralType extends Type { text: string; // Text of string literal diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 5a35bcbdee138..863fbcb23eff4 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -693,7 +693,7 @@ namespace ts { } export function isIdentifierTypePredicate(predicate: TypePredicate): predicate is IdentifierTypePredicate { - return predicate.kind === TypePredicateKind.Identifier; + return predicate && predicate.kind === TypePredicateKind.Identifier; } export function getContainingFunction(node: Node): FunctionLikeDeclaration { diff --git a/tests/baselines/reference/typeGuardFunctionErrors.errors.txt b/tests/baselines/reference/typeGuardFunctionErrors.errors.txt index 92d9d21f0cc85..9ad0ac6aa4a91 100644 --- a/tests/baselines/reference/typeGuardFunctionErrors.errors.txt +++ b/tests/baselines/reference/typeGuardFunctionErrors.errors.txt @@ -25,7 +25,7 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(91,1): 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(104,25): error TS2519: A 'this'-based type predicate is only allowed in class or interface members, get accessors, or return type positions for functions and methods 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 @@ -194,7 +194,7 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(137,39 class D { constructor(p1: A): p1 is C { ~~~~~~~ -!!! error TS1228: A type predicate is only allowed in return type position for functions and methods. +!!! error TS2519: A 'this'-based type predicate is only allowed in class or interface members, get accessors, or return type positions for functions and methods return true; ~~~~ !!! error TS2322: Type 'boolean' is not assignable to type 'D'. diff --git a/tests/baselines/reference/typeGuardOfFormThisMember.js b/tests/baselines/reference/typeGuardOfFormThisMember.js new file mode 100644 index 0000000000000..2a53be25ab1fa --- /dev/null +++ b/tests/baselines/reference/typeGuardOfFormThisMember.js @@ -0,0 +1,130 @@ +//// [typeGuardOfFormThisMember.ts] +// There's a 'File' class in the stdlib, wrap with a namespace to avoid collision +namespace Test { + export class FileSystemObject { + get isFile(): this is File { + return this instanceof File; + } + set isFile(param) { + // noop + } + get isDirectory(): this is Directory { + return this instanceof Directory; + } + isNetworked: this is (Networked & this); + constructor(public path: string) {} + } + + export class File extends FileSystemObject { + constructor(path: string, public content: string) { super(path); } + } + export class Directory extends FileSystemObject { + children: FileSystemObject[]; + } + export interface Networked { + host: string; + } + + let file: FileSystemObject = new File("foo/bar.txt", "foo"); + file.isNetworked = false; + file.isNetworked = file.isDirectory; + file.isFile = file.isNetworked; + let x = file.isFile; + if (file.isFile) { + file.content; + } + else if (file.isDirectory) { + file.children; + } + else if (file.isNetworked) { + file.host; + } +} + +//// [typeGuardOfFormThisMember.js] +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +// There's a 'File' class in the stdlib, wrap with a namespace to avoid collision +var Test; +(function (Test) { + var FileSystemObject = (function () { + function FileSystemObject(path) { + this.path = path; + } + Object.defineProperty(FileSystemObject.prototype, "isFile", { + get: function () { + return this instanceof File; + }, + set: function (param) { + // noop + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(FileSystemObject.prototype, "isDirectory", { + get: function () { + return this instanceof Directory; + }, + enumerable: true, + configurable: true + }); + return FileSystemObject; + })(); + Test.FileSystemObject = FileSystemObject; + var File = (function (_super) { + __extends(File, _super); + function File(path, content) { + _super.call(this, path); + this.content = content; + } + return File; + })(FileSystemObject); + Test.File = File; + var Directory = (function (_super) { + __extends(Directory, _super); + function Directory() { + _super.apply(this, arguments); + } + return Directory; + })(FileSystemObject); + Test.Directory = Directory; + var file = new File("foo/bar.txt", "foo"); + file.isNetworked = false; + file.isNetworked = file.isDirectory; + file.isFile = file.isNetworked; + var x = file.isFile; + if (file.isFile) { + file.content; + } + else if (file.isDirectory) { + file.children; + } + else if (file.isNetworked) { + file.host; + } +})(Test || (Test = {})); + + +//// [typeGuardOfFormThisMember.d.ts] +declare namespace Test { + class FileSystemObject { + path: string; + isFile: this is File; + isDirectory: this is Directory; + isNetworked: this is (Networked & this); + constructor(path: string); + } + class File extends FileSystemObject { + content: string; + constructor(path: string, content: string); + } + class Directory extends FileSystemObject { + children: FileSystemObject[]; + } + interface Networked { + host: string; + } +} diff --git a/tests/baselines/reference/typeGuardOfFormThisMember.symbols b/tests/baselines/reference/typeGuardOfFormThisMember.symbols new file mode 100644 index 0000000000000..fe78ed165070b --- /dev/null +++ b/tests/baselines/reference/typeGuardOfFormThisMember.symbols @@ -0,0 +1,126 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardOfFormThisMember.ts === +// There's a 'File' class in the stdlib, wrap with a namespace to avoid collision +namespace Test { +>Test : Symbol(Test, Decl(typeGuardOfFormThisMember.ts, 0, 0)) + + export class FileSystemObject { +>FileSystemObject : Symbol(FileSystemObject, Decl(typeGuardOfFormThisMember.ts, 1, 16)) + + get isFile(): this is File { +>isFile : Symbol(isFile, Decl(typeGuardOfFormThisMember.ts, 2, 32), Decl(typeGuardOfFormThisMember.ts, 5, 3)) +>File : Symbol(File, Decl(typeGuardOfFormThisMember.ts, 14, 2)) + + return this instanceof File; +>this : Symbol(FileSystemObject, Decl(typeGuardOfFormThisMember.ts, 1, 16)) +>File : Symbol(File, Decl(typeGuardOfFormThisMember.ts, 14, 2)) + } + set isFile(param) { +>isFile : Symbol(isFile, Decl(typeGuardOfFormThisMember.ts, 2, 32), Decl(typeGuardOfFormThisMember.ts, 5, 3)) +>param : Symbol(param, Decl(typeGuardOfFormThisMember.ts, 6, 13)) + + // noop + } + get isDirectory(): this is Directory { +>isDirectory : Symbol(isDirectory, Decl(typeGuardOfFormThisMember.ts, 8, 3)) +>Directory : Symbol(Directory, Decl(typeGuardOfFormThisMember.ts, 18, 2)) + + return this instanceof Directory; +>this : Symbol(FileSystemObject, Decl(typeGuardOfFormThisMember.ts, 1, 16)) +>Directory : Symbol(Directory, Decl(typeGuardOfFormThisMember.ts, 18, 2)) + } + isNetworked: this is (Networked & this); +>isNetworked : Symbol(isNetworked, Decl(typeGuardOfFormThisMember.ts, 11, 3)) +>Networked : Symbol(Networked, Decl(typeGuardOfFormThisMember.ts, 21, 2)) + + constructor(public path: string) {} +>path : Symbol(path, Decl(typeGuardOfFormThisMember.ts, 13, 14)) + } + + export class File extends FileSystemObject { +>File : Symbol(File, Decl(typeGuardOfFormThisMember.ts, 14, 2)) +>FileSystemObject : Symbol(FileSystemObject, Decl(typeGuardOfFormThisMember.ts, 1, 16)) + + constructor(path: string, public content: string) { super(path); } +>path : Symbol(path, Decl(typeGuardOfFormThisMember.ts, 17, 14)) +>content : Symbol(content, Decl(typeGuardOfFormThisMember.ts, 17, 27)) +>super : Symbol(FileSystemObject, Decl(typeGuardOfFormThisMember.ts, 1, 16)) +>path : Symbol(path, Decl(typeGuardOfFormThisMember.ts, 17, 14)) + } + export class Directory extends FileSystemObject { +>Directory : Symbol(Directory, Decl(typeGuardOfFormThisMember.ts, 18, 2)) +>FileSystemObject : Symbol(FileSystemObject, Decl(typeGuardOfFormThisMember.ts, 1, 16)) + + children: FileSystemObject[]; +>children : Symbol(children, Decl(typeGuardOfFormThisMember.ts, 19, 50)) +>FileSystemObject : Symbol(FileSystemObject, Decl(typeGuardOfFormThisMember.ts, 1, 16)) + } + export interface Networked { +>Networked : Symbol(Networked, Decl(typeGuardOfFormThisMember.ts, 21, 2)) + + host: string; +>host : Symbol(host, Decl(typeGuardOfFormThisMember.ts, 22, 29)) + } + + let file: FileSystemObject = new File("foo/bar.txt", "foo"); +>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4)) +>FileSystemObject : Symbol(FileSystemObject, Decl(typeGuardOfFormThisMember.ts, 1, 16)) +>File : Symbol(File, Decl(typeGuardOfFormThisMember.ts, 14, 2)) + + file.isNetworked = false; +>file.isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 11, 3)) +>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4)) +>isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 11, 3)) + + file.isNetworked = file.isDirectory; +>file.isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 11, 3)) +>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4)) +>isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 11, 3)) +>file.isDirectory : Symbol(FileSystemObject.isDirectory, Decl(typeGuardOfFormThisMember.ts, 8, 3)) +>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4)) +>isDirectory : Symbol(FileSystemObject.isDirectory, Decl(typeGuardOfFormThisMember.ts, 8, 3)) + + file.isFile = file.isNetworked; +>file.isFile : Symbol(FileSystemObject.isFile, Decl(typeGuardOfFormThisMember.ts, 2, 32), Decl(typeGuardOfFormThisMember.ts, 5, 3)) +>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4)) +>isFile : Symbol(FileSystemObject.isFile, Decl(typeGuardOfFormThisMember.ts, 2, 32), Decl(typeGuardOfFormThisMember.ts, 5, 3)) +>file.isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 11, 3)) +>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4)) +>isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 11, 3)) + + let x = file.isFile; +>x : Symbol(x, Decl(typeGuardOfFormThisMember.ts, 30, 4)) +>file.isFile : Symbol(FileSystemObject.isFile, Decl(typeGuardOfFormThisMember.ts, 2, 32), Decl(typeGuardOfFormThisMember.ts, 5, 3)) +>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4)) +>isFile : Symbol(FileSystemObject.isFile, Decl(typeGuardOfFormThisMember.ts, 2, 32), Decl(typeGuardOfFormThisMember.ts, 5, 3)) + + if (file.isFile) { +>file.isFile : Symbol(FileSystemObject.isFile, Decl(typeGuardOfFormThisMember.ts, 2, 32), Decl(typeGuardOfFormThisMember.ts, 5, 3)) +>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4)) +>isFile : Symbol(FileSystemObject.isFile, Decl(typeGuardOfFormThisMember.ts, 2, 32), Decl(typeGuardOfFormThisMember.ts, 5, 3)) + + file.content; +>file.content : Symbol(File.content, Decl(typeGuardOfFormThisMember.ts, 17, 27)) +>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4)) +>content : Symbol(File.content, Decl(typeGuardOfFormThisMember.ts, 17, 27)) + } + else if (file.isDirectory) { +>file.isDirectory : Symbol(FileSystemObject.isDirectory, Decl(typeGuardOfFormThisMember.ts, 8, 3)) +>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4)) +>isDirectory : Symbol(FileSystemObject.isDirectory, Decl(typeGuardOfFormThisMember.ts, 8, 3)) + + file.children; +>file.children : Symbol(Directory.children, Decl(typeGuardOfFormThisMember.ts, 19, 50)) +>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4)) +>children : Symbol(Directory.children, Decl(typeGuardOfFormThisMember.ts, 19, 50)) + } + else if (file.isNetworked) { +>file.isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 11, 3)) +>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4)) +>isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 11, 3)) + + file.host; +>file.host : Symbol(Networked.host, Decl(typeGuardOfFormThisMember.ts, 22, 29)) +>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4)) +>host : Symbol(Networked.host, Decl(typeGuardOfFormThisMember.ts, 22, 29)) + } +} diff --git a/tests/baselines/reference/typeGuardOfFormThisMember.types b/tests/baselines/reference/typeGuardOfFormThisMember.types new file mode 100644 index 0000000000000..c379186fac731 --- /dev/null +++ b/tests/baselines/reference/typeGuardOfFormThisMember.types @@ -0,0 +1,136 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardOfFormThisMember.ts === +// There's a 'File' class in the stdlib, wrap with a namespace to avoid collision +namespace Test { +>Test : typeof Test + + export class FileSystemObject { +>FileSystemObject : FileSystemObject + + get isFile(): this is File { +>isFile : this is File +>File : File + + return this instanceof File; +>this instanceof File : boolean +>this : this +>File : typeof File + } + set isFile(param) { +>isFile : this is File +>param : this is File + + // noop + } + get isDirectory(): this is Directory { +>isDirectory : this is Directory +>Directory : Directory + + return this instanceof Directory; +>this instanceof Directory : boolean +>this : this +>Directory : typeof Directory + } + isNetworked: this is (Networked & this); +>isNetworked : this is Networked & this +>Networked : Networked + + constructor(public path: string) {} +>path : string + } + + export class File extends FileSystemObject { +>File : File +>FileSystemObject : FileSystemObject + + constructor(path: string, public content: string) { super(path); } +>path : string +>content : string +>super(path) : void +>super : typeof FileSystemObject +>path : string + } + export class Directory extends FileSystemObject { +>Directory : Directory +>FileSystemObject : FileSystemObject + + children: FileSystemObject[]; +>children : FileSystemObject[] +>FileSystemObject : FileSystemObject + } + export interface Networked { +>Networked : Networked + + host: string; +>host : string + } + + let file: FileSystemObject = new File("foo/bar.txt", "foo"); +>file : FileSystemObject +>FileSystemObject : FileSystemObject +>new File("foo/bar.txt", "foo") : File +>File : typeof File +>"foo/bar.txt" : string +>"foo" : string + + file.isNetworked = false; +>file.isNetworked = false : boolean +>file.isNetworked : this is Networked & this +>file : FileSystemObject +>isNetworked : this is Networked & this +>false : boolean + + file.isNetworked = file.isDirectory; +>file.isNetworked = file.isDirectory : this is Directory +>file.isNetworked : this is Networked & this +>file : FileSystemObject +>isNetworked : this is Networked & this +>file.isDirectory : this is Directory +>file : FileSystemObject +>isDirectory : this is Directory + + file.isFile = file.isNetworked; +>file.isFile = file.isNetworked : this is Networked & this +>file.isFile : this is File +>file : FileSystemObject +>isFile : this is File +>file.isNetworked : this is Networked & this +>file : FileSystemObject +>isNetworked : this is Networked & this + + let x = file.isFile; +>x : this is File +>file.isFile : this is File +>file : FileSystemObject +>isFile : this is File + + if (file.isFile) { +>file.isFile : this is File +>file : FileSystemObject +>isFile : this is File + + file.content; +>file.content : string +>file : File +>content : string + } + else if (file.isDirectory) { +>file.isDirectory : this is Directory +>file : FileSystemObject +>isDirectory : this is Directory + + file.children; +>file.children : FileSystemObject[] +>file : Directory +>children : FileSystemObject[] + } + else if (file.isNetworked) { +>file.isNetworked : this is Networked & this +>file : FileSystemObject +>isNetworked : this is Networked & this + + file.host; +>file.host : string +>file : Networked & this +>host : string + } +} diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormThisMember.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormThisMember.ts new file mode 100644 index 0000000000000..1c714ff7d5a55 --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormThisMember.ts @@ -0,0 +1,43 @@ +// @target: es5 +// @declaration: true +// There's a 'File' class in the stdlib, wrap with a namespace to avoid collision +namespace Test { + export class FileSystemObject { + get isFile(): this is File { + return this instanceof File; + } + set isFile(param) { + // noop + } + get isDirectory(): this is Directory { + return this instanceof Directory; + } + isNetworked: this is (Networked & this); + constructor(public path: string) {} + } + + export class File extends FileSystemObject { + constructor(path: string, public content: string) { super(path); } + } + export class Directory extends FileSystemObject { + children: FileSystemObject[]; + } + export interface Networked { + host: string; + } + + let file: FileSystemObject = new File("foo/bar.txt", "foo"); + file.isNetworked = false; + file.isNetworked = file.isDirectory; + file.isFile = file.isNetworked; + let x = file.isFile; + if (file.isFile) { + file.content; + } + else if (file.isDirectory) { + file.children; + } + else if (file.isNetworked) { + file.host; + } +} \ No newline at end of file From be6e341d2afcaf0f00b13212db896991a2f339b7 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Mon, 7 Dec 2015 18:16:05 -0800 Subject: [PATCH 07/15] Fix narrowing, interfaces. Expose issue with generic instantiation --- src/compiler/binder.ts | 13 +++- src/compiler/checker.ts | 40 ++++++------ src/compiler/types.ts | 2 +- .../typeGuards/typeGuardFunctionOfFormThis.ts | 6 ++ .../typeGuards/typeGuardOfFormThisMember.ts | 62 ++++++++++++++++++- 5 files changed, 101 insertions(+), 22 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 7561783d1398b..837260e5e1ba4 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1189,7 +1189,8 @@ namespace ts { case SyntaxKind.ThisType: seenThisKeyword = true; return; - + case SyntaxKind.TypePredicate: + return checkTypePredicate(node as TypePredicateNode); case SyntaxKind.TypeParameter: return declareSymbolAndAddToSymbolTable(node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes); case SyntaxKind.Parameter: @@ -1275,6 +1276,16 @@ namespace ts { } } + function checkTypePredicate(node: TypePredicateNode) { + if (node.parameterName && node.parameterName.kind === SyntaxKind.Identifier) { + checkStrictModeIdentifier(node.parameterName as Identifier); + } + if (node.parameterName && node.parameterName.kind === SyntaxKind.ThisType) { + seenThisKeyword = true; + } + bind(node.type); + } + function bindSourceFileIfExternalModule() { setExportContextFlag(file); if (isExternalModule(file)) { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a1d13faff5a82..02ddd7b26cff6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2634,7 +2634,13 @@ namespace ts { // During a normal type check we'll never get to here with a property assignment (the check of the containing // object literal uses a different path). We exclude widening only so that language services and type verification // tools see the actual type. - return declaration.kind !== SyntaxKind.PropertyAssignment ? getWidenedType(type) : type; + if (declaration.kind === SyntaxKind.PropertyAssignment) { + return type; + } + if (type.flags & TypeFlags.PredicateType && (declaration.kind === SyntaxKind.PropertyDeclaration || declaration.kind === SyntaxKind.PropertySignature)) { + return type; + } + return getWidenedType(type); } // Rest parameters default to type any[], other parameters default to type any @@ -4602,7 +4608,7 @@ namespace ts { } function getPredicateType(node: TypePredicateNode): Type { - if (!(node.parent.kind === SyntaxKind.PropertyDeclaration || node.parent.kind === SyntaxKind.GetAccessor)) { + if (!(node.parent.kind === SyntaxKind.PropertyDeclaration || node.parent.kind === SyntaxKind.PropertySignature || node.parent.kind === SyntaxKind.GetAccessor)) { return booleanType; } else { @@ -6008,6 +6014,9 @@ namespace ts { function getWidenedType(type: Type): Type { if (type.flags & TypeFlags.RequiresWidening) { + if (type.flags & TypeFlags.PredicateType) { + return booleanType; + } if (type.flags & (TypeFlags.Undefined | TypeFlags.Null)) { return anyType; } @@ -11078,8 +11087,9 @@ namespace ts { } switch (node.parent.kind) { case SyntaxKind.PropertyDeclaration: + case SyntaxKind.PropertySignature: case SyntaxKind.GetAccessor: - return node === (node.parent).type; + return node === (node.parent as (PropertyDeclaration | GetAccessorDeclaration | PropertySignature)).type; } return false; } @@ -11104,12 +11114,8 @@ namespace ts { if (node.type.kind === SyntaxKind.TypePredicate) { const typePredicate = getSignatureFromDeclaration(node).typePredicate; const typePredicateNode = node.type; + checkSourceElement(typePredicateNode); if (isIdentifierTypePredicate(typePredicate)) { - if (!isInLegalTypePredicatePosition(typePredicateNode)) { - error(typePredicateNode, - Diagnostics.A_type_predicate_is_only_allowed_in_return_type_position_for_functions_and_methods); - return; - } if (typePredicate.parameterIndex >= 0) { if (node.parameters[typePredicate.parameterIndex].dotDotDotToken) { error(typePredicateNode.parameterName, @@ -11157,15 +11163,6 @@ namespace ts { } } } - else { - if (!isInLegalThisTypePredicatePosition(typePredicateNode)) { - error(typePredicateNode, - Diagnostics.A_this_based_type_predicate_is_only_allowed_in_class_or_interface_members_get_accessors_or_return_type_positions_for_functions_and_methods); - return; - } - // Reuse this type diagnostics on the this type node to determine if a this type predicate is valid - getTypeFromThisTypeNode(typePredicateNode.parameterName as ThisTypeNode); - } } else { checkSourceElement(node.type); @@ -14295,8 +14292,13 @@ namespace ts { if (node.parameterName.kind === SyntaxKind.Identifier && !isInLegalTypePredicatePosition(node)) { error(node, Diagnostics.A_type_predicate_is_only_allowed_in_return_type_position_for_functions_and_methods); } - else if (node.parameterName.kind === SyntaxKind.ThisType && (!isInLegalThisTypePredicatePosition(node) || getTypeFromThisTypeNode(node.parameterName as ThisTypeNode) === unknownType)) { - error(node, Diagnostics.A_this_based_type_predicate_is_only_allowed_in_class_or_interface_members_get_accessors_or_return_type_positions_for_functions_and_methods); + else if (node.parameterName.kind === SyntaxKind.ThisType) { + if (!isInLegalThisTypePredicatePosition(node)) { + error(node, Diagnostics.A_this_based_type_predicate_is_only_allowed_in_class_or_interface_members_get_accessors_or_return_type_positions_for_functions_and_methods); + } + else { + getTypeFromThisTypeNode(node.parameterName as ThisTypeNode); + } } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d8e6291ac02bf..54eb4f449dd13 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2120,7 +2120,7 @@ namespace ts { UnionOrIntersection = Union | Intersection, StructuredType = ObjectType | Union | Intersection, /* @internal */ - RequiresWidening = ContainsUndefinedOrNull | ContainsObjectLiteral, + RequiresWidening = ContainsUndefinedOrNull | ContainsObjectLiteral | PredicateType, /* @internal */ PropagatingFlags = ContainsUndefinedOrNull | ContainsObjectLiteral | ContainsAnyFunctionType } diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThis.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThis.ts index c053ec55d079f..aadbf3cd9c584 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThis.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThis.ts @@ -133,3 +133,9 @@ if (mimic.isFollower()) { mimic.follow(); mimic.isFollower = a.isFollower; } + + +interface MimicGuardInterface { + isLeader(): this is LeadGuard; + isFollower(): this is FollowerGuard; +} diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormThisMember.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormThisMember.ts index 1c714ff7d5a55..182d475b8157a 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormThisMember.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormThisMember.ts @@ -40,4 +40,64 @@ namespace Test { else if (file.isNetworked) { file.host; } -} \ No newline at end of file + + interface GenericLeadGuard extends GenericGuard { + lead(): void; + } + + interface GenericFollowerGuard extends GenericGuard { + follow(): void; + } + + interface GenericGuard { + target: T; + isLeader: this is (GenericLeadGuard); + isFollower: this is GenericFollowerGuard; + } + + let guard: GenericGuard; + if (guard.isLeader) { + guard.lead(); + } + else if (guard.isFollower) { + guard.follow(); + } + + interface SpecificGuard { + isMoreSpecific: this is MoreSpecificGuard; + } + + interface MoreSpecificGuard extends SpecificGuard { + do(): void; + } + + let general: SpecificGuard; + if (general.isMoreSpecific) { + general.do(); + } + + + class doThing { + constructor(private x: T) {} + isThing(x: any): x is doThing { + return true; + } + + } + + let z: doThing<{}> = new doThing({x: 10}); + let z1 = new doThing({x: 10}); + if (z1.isThing(z)) { + z; + } +} + +function f(g: (x: number) => void) { + + +} + +f(function(x) { + + +}) \ No newline at end of file From b6eb4ae10f290a648bc127f8e9965939e594a8c9 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 8 Dec 2015 15:00:47 -0800 Subject: [PATCH 08/15] fix generic instantiation, nested this guards now work --- src/compiler/checker.ts | 13 +- .../typeGuardFunctionErrors.errors.txt | 9 +- .../reference/typeGuardFunctionOfFormThis.js | 10 ++ .../typeGuardFunctionOfFormThis.symbols | 13 ++ .../typeGuardFunctionOfFormThis.types | 13 ++ .../reference/typeGuardOfFormThisMember.js | 57 ++++++- .../typeGuardOfFormThisMember.symbols | 113 ++++++++++++++ .../reference/typeGuardOfFormThisMember.types | 140 ++++++++++++++++-- .../typeGuards/typeGuardOfFormThisMember.ts | 29 +--- 9 files changed, 353 insertions(+), 44 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 02ddd7b26cff6..68dea4eb4eb82 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4612,11 +4612,15 @@ namespace ts { return booleanType; } else { + return createPredicateType(getSymbolOfNode(node), createTypePredicateFromTypePredicateNode(node) as ThisTypePredicate); + } + } + + function createPredicateType(symbol: Symbol, predicate: ThisTypePredicate) { const type = createType(TypeFlags.Boolean | TypeFlags.PredicateType) as PredicateType; - type.symbol = getSymbolOfNode(node); - type.predicate = createTypePredicateFromTypePredicateNode(node) as ThisTypePredicate; + type.symbol = symbol; + type.predicate = predicate; return type; - } } function getTypeFromPredicateTypeNode(node: TypePredicateNode): Type { @@ -4867,6 +4871,9 @@ namespace ts { if (type.flags & TypeFlags.Intersection) { return getIntersectionType(instantiateList((type).types, mapper, instantiateType)); } + if (type.flags & TypeFlags.PredicateType) { + return createPredicateType(type.symbol, {kind: TypePredicateKind.This, type: instantiateType((type as PredicateType).predicate.type, mapper)} as ThisTypePredicate); + } } return type; } diff --git a/tests/baselines/reference/typeGuardFunctionErrors.errors.txt b/tests/baselines/reference/typeGuardFunctionErrors.errors.txt index 9ad0ac6aa4a91..2d40520a8ce57 100644 --- a/tests/baselines/reference/typeGuardFunctionErrors.errors.txt +++ b/tests/baselines/reference/typeGuardFunctionErrors.errors.txt @@ -25,7 +25,7 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(91,1): 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 TS2519: A 'this'-based type predicate is only allowed in class or interface members, get accessors, or return type positions 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(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 @@ -33,6 +33,7 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(107,20 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 TS1225: Cannot find parameter 'p1'. 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(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. @@ -40,7 +41,7 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(133,34 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 (34 errors) ==== class A { propA: number; @@ -194,7 +195,7 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(137,39 class D { constructor(p1: A): p1 is C { ~~~~~~~ -!!! error TS2519: A 'this'-based type predicate is only allowed in class or interface members, get accessors, or return type positions for functions and methods +!!! 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'. @@ -224,6 +225,8 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(137,39 interface I2 { [index: number]: p1 is C; + ~~ +!!! error TS1225: Cannot find parameter 'p1'. ~~~~~~~ !!! error TS1228: A type predicate is only allowed in return type position for functions and methods. } diff --git a/tests/baselines/reference/typeGuardFunctionOfFormThis.js b/tests/baselines/reference/typeGuardFunctionOfFormThis.js index bc91defc2442c..c1c670e8d7d2c 100644 --- a/tests/baselines/reference/typeGuardFunctionOfFormThis.js +++ b/tests/baselines/reference/typeGuardFunctionOfFormThis.js @@ -133,6 +133,12 @@ if (mimic.isFollower()) { mimic.follow(); mimic.isFollower = a.isFollower; } + + +interface MimicGuardInterface { + isLeader(): this is LeadGuard; + isFollower(): this is FollowerGuard; +} //// [typeGuardFunctionOfFormThis.js] @@ -335,3 +341,7 @@ declare class MimicFollower extends MimicGuard { follow(): void; } declare let mimic: MimicGuard; +interface MimicGuardInterface { + isLeader(): this is LeadGuard; + isFollower(): this is FollowerGuard; +} diff --git a/tests/baselines/reference/typeGuardFunctionOfFormThis.symbols b/tests/baselines/reference/typeGuardFunctionOfFormThis.symbols index 84c07a17f0e94..2968063dc517b 100644 --- a/tests/baselines/reference/typeGuardFunctionOfFormThis.symbols +++ b/tests/baselines/reference/typeGuardFunctionOfFormThis.symbols @@ -370,3 +370,16 @@ if (mimic.isFollower()) { >isFollower : Symbol(RoyalGuard.isFollower, Decl(typeGuardFunctionOfFormThis.ts, 3, 5)) } + +interface MimicGuardInterface { +>MimicGuardInterface : Symbol(MimicGuardInterface, Decl(typeGuardFunctionOfFormThis.ts, 133, 1)) + + isLeader(): this is LeadGuard; +>isLeader : Symbol(isLeader, Decl(typeGuardFunctionOfFormThis.ts, 136, 31)) +>LeadGuard : Symbol(LeadGuard, Decl(typeGuardFunctionOfFormThis.ts, 7, 1)) + + isFollower(): this is FollowerGuard; +>isFollower : Symbol(isFollower, Decl(typeGuardFunctionOfFormThis.ts, 137, 34)) +>FollowerGuard : Symbol(FollowerGuard, Decl(typeGuardFunctionOfFormThis.ts, 11, 1)) +} + diff --git a/tests/baselines/reference/typeGuardFunctionOfFormThis.types b/tests/baselines/reference/typeGuardFunctionOfFormThis.types index fa5becc7790b1..6b21fea34576a 100644 --- a/tests/baselines/reference/typeGuardFunctionOfFormThis.types +++ b/tests/baselines/reference/typeGuardFunctionOfFormThis.types @@ -426,3 +426,16 @@ if (mimic.isFollower()) { >isFollower : () => this is FollowerGuard } + +interface MimicGuardInterface { +>MimicGuardInterface : MimicGuardInterface + + isLeader(): this is LeadGuard; +>isLeader : () => this is LeadGuard +>LeadGuard : LeadGuard + + isFollower(): this is FollowerGuard; +>isFollower : () => this is FollowerGuard +>FollowerGuard : FollowerGuard +} + diff --git a/tests/baselines/reference/typeGuardOfFormThisMember.js b/tests/baselines/reference/typeGuardOfFormThisMember.js index 2a53be25ab1fa..cbf70c4a64086 100644 --- a/tests/baselines/reference/typeGuardOfFormThisMember.js +++ b/tests/baselines/reference/typeGuardOfFormThisMember.js @@ -32,6 +32,10 @@ namespace Test { let x = file.isFile; if (file.isFile) { file.content; + if (file.isNetworked) { + file.host; + file.content; + } } else if (file.isDirectory) { file.children; @@ -39,7 +43,43 @@ namespace Test { else if (file.isNetworked) { file.host; } -} + + interface GenericLeadGuard extends GenericGuard { + lead(): void; + } + + interface GenericFollowerGuard extends GenericGuard { + follow(): void; + } + + interface GenericGuard { + target: T; + isLeader: this is (GenericLeadGuard); + isFollower: this is GenericFollowerGuard; + } + + let guard: GenericGuard; + if (guard.isLeader) { + guard.lead(); + } + else if (guard.isFollower) { + guard.follow(); + } + + interface SpecificGuard { + isMoreSpecific: this is MoreSpecificGuard; + } + + interface MoreSpecificGuard extends SpecificGuard { + do(): void; + } + + let general: SpecificGuard; + if (general.isMoreSpecific) { + general.do(); + } +} + //// [typeGuardOfFormThisMember.js] var __extends = (this && this.__extends) || function (d, b) { @@ -98,6 +138,10 @@ var Test; var x = file.isFile; if (file.isFile) { file.content; + if (file.isNetworked) { + file.host; + file.content; + } } else if (file.isDirectory) { file.children; @@ -105,6 +149,17 @@ var Test; else if (file.isNetworked) { file.host; } + var guard; + if (guard.isLeader) { + guard.lead(); + } + else if (guard.isFollower) { + guard.follow(); + } + var general; + if (general.isMoreSpecific) { + general.do(); + } })(Test || (Test = {})); diff --git a/tests/baselines/reference/typeGuardOfFormThisMember.symbols b/tests/baselines/reference/typeGuardOfFormThisMember.symbols index fe78ed165070b..84573ddf0c703 100644 --- a/tests/baselines/reference/typeGuardOfFormThisMember.symbols +++ b/tests/baselines/reference/typeGuardOfFormThisMember.symbols @@ -102,6 +102,22 @@ namespace Test { >file.content : Symbol(File.content, Decl(typeGuardOfFormThisMember.ts, 17, 27)) >file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4)) >content : Symbol(File.content, Decl(typeGuardOfFormThisMember.ts, 17, 27)) + + if (file.isNetworked) { +>file.isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 11, 3)) +>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4)) +>isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 11, 3)) + + file.host; +>file.host : Symbol(Networked.host, Decl(typeGuardOfFormThisMember.ts, 22, 29)) +>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4)) +>host : Symbol(Networked.host, Decl(typeGuardOfFormThisMember.ts, 22, 29)) + + file.content; +>file.content : Symbol(File.content, Decl(typeGuardOfFormThisMember.ts, 17, 27)) +>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4)) +>content : Symbol(File.content, Decl(typeGuardOfFormThisMember.ts, 17, 27)) + } } else if (file.isDirectory) { >file.isDirectory : Symbol(FileSystemObject.isDirectory, Decl(typeGuardOfFormThisMember.ts, 8, 3)) @@ -123,4 +139,101 @@ namespace Test { >file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4)) >host : Symbol(Networked.host, Decl(typeGuardOfFormThisMember.ts, 22, 29)) } + + interface GenericLeadGuard extends GenericGuard { +>GenericLeadGuard : Symbol(GenericLeadGuard, Decl(typeGuardOfFormThisMember.ts, 43, 2)) +>T : Symbol(T, Decl(typeGuardOfFormThisMember.ts, 45, 28)) +>GenericGuard : Symbol(GenericGuard, Decl(typeGuardOfFormThisMember.ts, 51, 2)) +>T : Symbol(T, Decl(typeGuardOfFormThisMember.ts, 45, 28)) + + lead(): void; +>lead : Symbol(lead, Decl(typeGuardOfFormThisMember.ts, 45, 56)) + } + + interface GenericFollowerGuard extends GenericGuard { +>GenericFollowerGuard : Symbol(GenericFollowerGuard, Decl(typeGuardOfFormThisMember.ts, 47, 2)) +>T : Symbol(T, Decl(typeGuardOfFormThisMember.ts, 49, 32)) +>GenericGuard : Symbol(GenericGuard, Decl(typeGuardOfFormThisMember.ts, 51, 2)) +>T : Symbol(T, Decl(typeGuardOfFormThisMember.ts, 49, 32)) + + follow(): void; +>follow : Symbol(follow, Decl(typeGuardOfFormThisMember.ts, 49, 60)) + } + + interface GenericGuard { +>GenericGuard : Symbol(GenericGuard, Decl(typeGuardOfFormThisMember.ts, 51, 2)) +>T : Symbol(T, Decl(typeGuardOfFormThisMember.ts, 53, 24)) + + target: T; +>target : Symbol(target, Decl(typeGuardOfFormThisMember.ts, 53, 28)) +>T : Symbol(T, Decl(typeGuardOfFormThisMember.ts, 53, 24)) + + isLeader: this is (GenericLeadGuard); +>isLeader : Symbol(isLeader, Decl(typeGuardOfFormThisMember.ts, 54, 12)) +>GenericLeadGuard : Symbol(GenericLeadGuard, Decl(typeGuardOfFormThisMember.ts, 43, 2)) +>T : Symbol(T, Decl(typeGuardOfFormThisMember.ts, 53, 24)) + + isFollower: this is GenericFollowerGuard; +>isFollower : Symbol(isFollower, Decl(typeGuardOfFormThisMember.ts, 55, 42)) +>GenericFollowerGuard : Symbol(GenericFollowerGuard, Decl(typeGuardOfFormThisMember.ts, 47, 2)) +>T : Symbol(T, Decl(typeGuardOfFormThisMember.ts, 53, 24)) + } + + let guard: GenericGuard; +>guard : Symbol(guard, Decl(typeGuardOfFormThisMember.ts, 59, 4)) +>GenericGuard : Symbol(GenericGuard, Decl(typeGuardOfFormThisMember.ts, 51, 2)) +>File : Symbol(File, Decl(typeGuardOfFormThisMember.ts, 14, 2)) + + if (guard.isLeader) { +>guard.isLeader : Symbol(GenericGuard.isLeader, Decl(typeGuardOfFormThisMember.ts, 54, 12)) +>guard : Symbol(guard, Decl(typeGuardOfFormThisMember.ts, 59, 4)) +>isLeader : Symbol(GenericGuard.isLeader, Decl(typeGuardOfFormThisMember.ts, 54, 12)) + + guard.lead(); +>guard.lead : Symbol(GenericLeadGuard.lead, Decl(typeGuardOfFormThisMember.ts, 45, 56)) +>guard : Symbol(guard, Decl(typeGuardOfFormThisMember.ts, 59, 4)) +>lead : Symbol(GenericLeadGuard.lead, Decl(typeGuardOfFormThisMember.ts, 45, 56)) + } + else if (guard.isFollower) { +>guard.isFollower : Symbol(GenericGuard.isFollower, Decl(typeGuardOfFormThisMember.ts, 55, 42)) +>guard : Symbol(guard, Decl(typeGuardOfFormThisMember.ts, 59, 4)) +>isFollower : Symbol(GenericGuard.isFollower, Decl(typeGuardOfFormThisMember.ts, 55, 42)) + + guard.follow(); +>guard.follow : Symbol(GenericFollowerGuard.follow, Decl(typeGuardOfFormThisMember.ts, 49, 60)) +>guard : Symbol(guard, Decl(typeGuardOfFormThisMember.ts, 59, 4)) +>follow : Symbol(GenericFollowerGuard.follow, Decl(typeGuardOfFormThisMember.ts, 49, 60)) + } + + interface SpecificGuard { +>SpecificGuard : Symbol(SpecificGuard, Decl(typeGuardOfFormThisMember.ts, 65, 2)) + + isMoreSpecific: this is MoreSpecificGuard; +>isMoreSpecific : Symbol(isMoreSpecific, Decl(typeGuardOfFormThisMember.ts, 67, 26)) +>MoreSpecificGuard : Symbol(MoreSpecificGuard, Decl(typeGuardOfFormThisMember.ts, 69, 2)) + } + + interface MoreSpecificGuard extends SpecificGuard { +>MoreSpecificGuard : Symbol(MoreSpecificGuard, Decl(typeGuardOfFormThisMember.ts, 69, 2)) +>SpecificGuard : Symbol(SpecificGuard, Decl(typeGuardOfFormThisMember.ts, 65, 2)) + + do(): void; +>do : Symbol(do, Decl(typeGuardOfFormThisMember.ts, 71, 52)) + } + + let general: SpecificGuard; +>general : Symbol(general, Decl(typeGuardOfFormThisMember.ts, 75, 4)) +>SpecificGuard : Symbol(SpecificGuard, Decl(typeGuardOfFormThisMember.ts, 65, 2)) + + if (general.isMoreSpecific) { +>general.isMoreSpecific : Symbol(SpecificGuard.isMoreSpecific, Decl(typeGuardOfFormThisMember.ts, 67, 26)) +>general : Symbol(general, Decl(typeGuardOfFormThisMember.ts, 75, 4)) +>isMoreSpecific : Symbol(SpecificGuard.isMoreSpecific, Decl(typeGuardOfFormThisMember.ts, 67, 26)) + + general.do(); +>general.do : Symbol(MoreSpecificGuard.do, Decl(typeGuardOfFormThisMember.ts, 71, 52)) +>general : Symbol(general, Decl(typeGuardOfFormThisMember.ts, 75, 4)) +>do : Symbol(MoreSpecificGuard.do, Decl(typeGuardOfFormThisMember.ts, 71, 52)) + } } + diff --git a/tests/baselines/reference/typeGuardOfFormThisMember.types b/tests/baselines/reference/typeGuardOfFormThisMember.types index c379186fac731..3ecb174653b0f 100644 --- a/tests/baselines/reference/typeGuardOfFormThisMember.types +++ b/tests/baselines/reference/typeGuardOfFormThisMember.types @@ -17,7 +17,7 @@ namespace Test { } set isFile(param) { >isFile : this is File ->param : this is File +>param : boolean // noop } @@ -74,31 +74,31 @@ namespace Test { file.isNetworked = false; >file.isNetworked = false : boolean ->file.isNetworked : this is Networked & this +>file.isNetworked : this is Networked & FileSystemObject >file : FileSystemObject ->isNetworked : this is Networked & this +>isNetworked : this is Networked & FileSystemObject >false : boolean file.isNetworked = file.isDirectory; >file.isNetworked = file.isDirectory : this is Directory ->file.isNetworked : this is Networked & this +>file.isNetworked : this is Networked & FileSystemObject >file : FileSystemObject ->isNetworked : this is Networked & this +>isNetworked : this is Networked & FileSystemObject >file.isDirectory : this is Directory >file : FileSystemObject >isDirectory : this is Directory file.isFile = file.isNetworked; ->file.isFile = file.isNetworked : this is Networked & this +>file.isFile = file.isNetworked : this is Networked & FileSystemObject >file.isFile : this is File >file : FileSystemObject >isFile : this is File ->file.isNetworked : this is Networked & this +>file.isNetworked : this is Networked & FileSystemObject >file : FileSystemObject ->isNetworked : this is Networked & this +>isNetworked : this is Networked & FileSystemObject let x = file.isFile; ->x : this is File +>x : boolean >file.isFile : this is File >file : FileSystemObject >isFile : this is File @@ -112,6 +112,22 @@ namespace Test { >file.content : string >file : File >content : string + + if (file.isNetworked) { +>file.isNetworked : this is Networked & File +>file : File +>isNetworked : this is Networked & File + + file.host; +>file.host : string +>file : Networked & File +>host : string + + file.content; +>file.content : string +>file : Networked & File +>content : string + } } else if (file.isDirectory) { >file.isDirectory : this is Directory @@ -124,13 +140,113 @@ namespace Test { >children : FileSystemObject[] } else if (file.isNetworked) { ->file.isNetworked : this is Networked & this +>file.isNetworked : this is Networked & FileSystemObject >file : FileSystemObject ->isNetworked : this is Networked & this +>isNetworked : this is Networked & FileSystemObject file.host; >file.host : string ->file : Networked & this +>file : Networked & FileSystemObject >host : string } + + interface GenericLeadGuard extends GenericGuard { +>GenericLeadGuard : GenericLeadGuard +>T : T +>GenericGuard : GenericGuard +>T : T + + lead(): void; +>lead : () => void + } + + interface GenericFollowerGuard extends GenericGuard { +>GenericFollowerGuard : GenericFollowerGuard +>T : T +>GenericGuard : GenericGuard +>T : T + + follow(): void; +>follow : () => void + } + + interface GenericGuard { +>GenericGuard : GenericGuard +>T : T + + target: T; +>target : T +>T : T + + isLeader: this is (GenericLeadGuard); +>isLeader : this is GenericLeadGuard +>GenericLeadGuard : GenericLeadGuard +>T : T + + isFollower: this is GenericFollowerGuard; +>isFollower : this is GenericFollowerGuard +>GenericFollowerGuard : GenericFollowerGuard +>T : T + } + + let guard: GenericGuard; +>guard : GenericGuard +>GenericGuard : GenericGuard +>File : File + + if (guard.isLeader) { +>guard.isLeader : this is GenericLeadGuard +>guard : GenericGuard +>isLeader : this is GenericLeadGuard + + guard.lead(); +>guard.lead() : void +>guard.lead : () => void +>guard : GenericLeadGuard +>lead : () => void + } + else if (guard.isFollower) { +>guard.isFollower : this is GenericFollowerGuard +>guard : GenericGuard +>isFollower : this is GenericFollowerGuard + + guard.follow(); +>guard.follow() : void +>guard.follow : () => void +>guard : GenericFollowerGuard +>follow : () => void + } + + interface SpecificGuard { +>SpecificGuard : SpecificGuard + + isMoreSpecific: this is MoreSpecificGuard; +>isMoreSpecific : this is MoreSpecificGuard +>MoreSpecificGuard : MoreSpecificGuard + } + + interface MoreSpecificGuard extends SpecificGuard { +>MoreSpecificGuard : MoreSpecificGuard +>SpecificGuard : SpecificGuard + + do(): void; +>do : () => void + } + + let general: SpecificGuard; +>general : SpecificGuard +>SpecificGuard : SpecificGuard + + if (general.isMoreSpecific) { +>general.isMoreSpecific : this is MoreSpecificGuard +>general : SpecificGuard +>isMoreSpecific : this is MoreSpecificGuard + + general.do(); +>general.do() : void +>general.do : () => void +>general : MoreSpecificGuard +>do : () => void + } } + diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormThisMember.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormThisMember.ts index 182d475b8157a..707f0f6eea575 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormThisMember.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormThisMember.ts @@ -33,6 +33,10 @@ namespace Test { let x = file.isFile; if (file.isFile) { file.content; + if (file.isNetworked) { + file.host; + file.content; + } } else if (file.isDirectory) { file.children; @@ -75,29 +79,4 @@ namespace Test { if (general.isMoreSpecific) { general.do(); } - - - class doThing { - constructor(private x: T) {} - isThing(x: any): x is doThing { - return true; - } - - } - - let z: doThing<{}> = new doThing({x: 10}); - let z1 = new doThing({x: 10}); - if (z1.isThing(z)) { - z; - } } - -function f(g: (x: number) => void) { - - -} - -f(function(x) { - - -}) \ No newline at end of file From 459430165d7f802bb430b7739a51c785f14828ad Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 8 Dec 2015 17:08:57 -0800 Subject: [PATCH 09/15] grand unified theory of predicate types --- src/compiler/checker.ts | 169 +++++------- src/compiler/types.ts | 3 +- .../arrayBufferIsViewNarrowsType.types | 2 +- tests/baselines/reference/isArray.types | 2 +- .../stringLiteralCheckedInIf02.types | 2 +- .../stringLiteralTypesAsTags01.errors.txt | 11 +- .../reference/typeGuardFunction.types | 14 +- .../typeGuardFunctionErrors.errors.txt | 16 +- .../reference/typeGuardFunctionGenerics.types | 4 +- .../typeGuardFunctionOfFormThis.types | 36 +-- ...peGuardFunctionOfFormThisErrors.errors.txt | 28 +- .../reference/typeGuardOfFormIsType.types | 16 +- .../typeGuardOfFormIsTypeOnInterfaces.types | 16 +- .../reference/typeGuardOfFormThisMember.js | 10 +- .../typeGuardOfFormThisMember.symbols | 241 +++++++++--------- .../reference/typeGuardOfFormThisMember.types | 24 +- ...typeGuardOfFormThisMemberErrors.errors.txt | 51 ++++ .../typeGuardOfFormThisMemberErrors.js | 112 ++++++++ .../unionAndIntersectionInference1.types | 4 +- .../typeGuards/typeGuardOfFormThisMember.ts | 5 +- .../typeGuardOfFormThisMemberErrors.ts | 34 +++ 21 files changed, 473 insertions(+), 327 deletions(-) create mode 100644 tests/baselines/reference/typeGuardOfFormThisMemberErrors.errors.txt create mode 100644 tests/baselines/reference/typeGuardOfFormThisMemberErrors.js create mode 100644 tests/cases/conformance/expressions/typeGuards/typeGuardOfFormThisMemberErrors.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 68dea4eb4eb82..e496a0b07bcb9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -123,8 +123,8 @@ namespace ts { const noConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); - const anySignature = createSignature(undefined, undefined, emptyArray, anyType, undefined, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false); - const unknownSignature = createSignature(undefined, undefined, emptyArray, unknownType, undefined, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false); + const anySignature = createSignature(undefined, undefined, emptyArray, anyType, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false); + const unknownSignature = createSignature(undefined, undefined, emptyArray, unknownType, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false); const globals: SymbolTable = {}; @@ -2074,15 +2074,7 @@ namespace ts { } writeSpace(writer); - let returnType: Type; - const predicate = signature.typePredicate; - if (predicate) { - buildTypePredicateDisplay(writer, predicate); - returnType = predicate.type; - } - else { - returnType = getReturnTypeOfSignature(signature); - } + const returnType = getReturnTypeOfSignature(signature); buildTypeDisplay(returnType, writer, enclosingDeclaration, flags, symbolStack); } @@ -3378,13 +3370,12 @@ namespace ts { } function createSignature(declaration: SignatureDeclaration, typeParameters: TypeParameter[], parameters: Symbol[], - resolvedReturnType: Type, typePredicate: IdentifierTypePredicate | ThisTypePredicate, minArgumentCount: number, hasRestParameter: boolean, hasStringLiterals: boolean): Signature { + resolvedReturnType: Type, minArgumentCount: number, hasRestParameter: boolean, hasStringLiterals: boolean): Signature { const sig = new Signature(checker); sig.declaration = declaration; sig.typeParameters = typeParameters; sig.parameters = parameters; sig.resolvedReturnType = resolvedReturnType; - sig.typePredicate = typePredicate; sig.minArgumentCount = minArgumentCount; sig.hasRestParameter = hasRestParameter; sig.hasStringLiterals = hasStringLiterals; @@ -3392,13 +3383,13 @@ namespace ts { } function cloneSignature(sig: Signature): Signature { - return createSignature(sig.declaration, sig.typeParameters, sig.parameters, sig.resolvedReturnType, sig.typePredicate, + return createSignature(sig.declaration, sig.typeParameters, sig.parameters, sig.resolvedReturnType, sig.minArgumentCount, sig.hasRestParameter, sig.hasStringLiterals); } function getDefaultConstructSignatures(classType: InterfaceType): Signature[] { if (!hasClassBaseType(classType)) { - return [createSignature(undefined, classType.localTypeParameters, emptyArray, classType, undefined, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false)]; + return [createSignature(undefined, classType.localTypeParameters, emptyArray, classType, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false)]; } const baseConstructorType = getBaseConstructorTypeOfClass(classType); const baseSignatures = getSignaturesOfType(baseConstructorType, SignatureKind.Construct); @@ -3924,15 +3915,11 @@ namespace ts { } let returnType: Type; - let typePredicate: IdentifierTypePredicate | ThisTypePredicate; if (classType) { returnType = classType; } else if (declaration.type) { returnType = getTypeFromTypeNode(declaration.type); - if (declaration.type.kind === SyntaxKind.TypePredicate) { - typePredicate = createTypePredicateFromTypePredicateNode(declaration.type as TypePredicateNode); - } } else { // TypeScript 1.0 spec (April 2014): @@ -3947,8 +3934,7 @@ namespace ts { } } - links.resolvedSignature = createSignature(declaration, typeParameters, parameters, returnType, typePredicate, - minArgumentCount, hasRestParameter(declaration), hasStringLiterals); + links.resolvedSignature = createSignature(declaration, typeParameters, parameters, returnType, minArgumentCount, hasRestParameter(declaration), hasStringLiterals); } return links.resolvedSignature; } @@ -4608,15 +4594,10 @@ namespace ts { } function getPredicateType(node: TypePredicateNode): Type { - if (!(node.parent.kind === SyntaxKind.PropertyDeclaration || node.parent.kind === SyntaxKind.PropertySignature || node.parent.kind === SyntaxKind.GetAccessor)) { - return booleanType; - } - else { - return createPredicateType(getSymbolOfNode(node), createTypePredicateFromTypePredicateNode(node) as ThisTypePredicate); - } + return createPredicateType(getSymbolOfNode(node), createTypePredicateFromTypePredicateNode(node)); } - function createPredicateType(symbol: Symbol, predicate: ThisTypePredicate) { + function createPredicateType(symbol: Symbol, predicate: ThisTypePredicate | IdentifierTypePredicate) { const type = createType(TypeFlags.Boolean | TypeFlags.PredicateType) as PredicateType; type.symbol = symbol; type.predicate = predicate; @@ -4801,7 +4782,6 @@ namespace ts { const result = createSignature(signature.declaration, freshTypeParameters, instantiateList(signature.parameters, mapper, instantiateSymbol), instantiateType(signature.resolvedReturnType, mapper), - signature.typePredicate && cloneTypePredicate(signature.typePredicate, mapper), signature.minArgumentCount, signature.hasRestParameter, signature.hasStringLiterals); result.target = signature; result.mapper = mapper; @@ -4872,7 +4852,8 @@ namespace ts { return getIntersectionType(instantiateList((type).types, mapper, instantiateType)); } if (type.flags & TypeFlags.PredicateType) { - return createPredicateType(type.symbol, {kind: TypePredicateKind.This, type: instantiateType((type as PredicateType).predicate.type, mapper)} as ThisTypePredicate); + const predicate = (type as PredicateType).predicate; + return createPredicateType(type.symbol, cloneTypePredicate(predicate, mapper)); } } return type; @@ -5044,7 +5025,36 @@ namespace ts { if (relation === assignableRelation) { if (isTypeAny(source)) return Ternary.True; if (source === numberType && target.flags & TypeFlags.Enum) return Ternary.True; - if (source.flags & TypeFlags.Boolean && target.flags & TypeFlags.Boolean && (source.flags & TypeFlags.PredicateType || target.flags & TypeFlags.PredicateType)) return Ternary.True; + } + if (source.flags & TypeFlags.Boolean && target.flags & TypeFlags.Boolean) { + if (source.flags & TypeFlags.PredicateType && target.flags & TypeFlags.PredicateType) { + const sourcePredicate = source as PredicateType; + const targetPredicate = target as PredicateType; + if (sourcePredicate.predicate.kind !== targetPredicate.predicate.kind) { + if (reportErrors) { + reportError(Diagnostics.A_this_based_type_guard_is_not_assignable_to_a_parameter_based_type_guard); + reportError(Diagnostics.Type_predicate_0_is_not_assignable_to_1, typeToString(source), typeToString(target)); + } + return Ternary.False; + } + if (sourcePredicate.predicate.kind === TypePredicateKind.Identifier) { + const sourceIdentifierPredicate = sourcePredicate.predicate as IdentifierTypePredicate; + const targetIdentifierPredicate = targetPredicate.predicate as IdentifierTypePredicate; + if (sourceIdentifierPredicate.parameterIndex !== targetIdentifierPredicate.parameterIndex) { + if (reportErrors) { + reportError(Diagnostics.Parameter_0_is_not_in_the_same_position_as_parameter_1, sourceIdentifierPredicate.parameterName, targetIdentifierPredicate.parameterName); + reportError(Diagnostics.Type_predicate_0_is_not_assignable_to_1, typeToString(source), typeToString(target)); + } + return Ternary.False; + } + } + const related = isRelatedTo(sourcePredicate.predicate.type, targetPredicate.predicate.type, reportErrors, headMessage); + if (related === Ternary.False && reportErrors) { + reportError(Diagnostics.Type_predicate_0_is_not_assignable_to_1, typeToString(source), typeToString(target)); + } + return related; + } + return Ternary.True; } if (source.flags & TypeFlags.FreshObjectLiteral) { @@ -5619,65 +5629,6 @@ namespace ts { result &= related; } - if (source.typePredicate && target.typePredicate) { - const sourcePredicate = source.typePredicate; - const targetPredicate = target.typePredicate; - if (source.typePredicate.kind !== target.typePredicate.kind) { - if (reportErrors) { - reportError(Diagnostics.A_this_based_type_guard_is_not_assignable_to_a_parameter_based_type_guard); - } - return Ternary.False; - } - if (!(isIdentifierTypePredicate(sourcePredicate) && isIdentifierTypePredicate(targetPredicate))) { - if (!isTypeIdenticalTo(sourcePredicate.type, targetPredicate.type)) { - if (reportErrors) { - const sourceTypeText = typeToString(sourcePredicate.type); - const targetTypeText = typeToString(targetPredicate.type); - reportError(Diagnostics.Type_0_is_not_assignable_to_type_1, - sourceTypeText, - targetTypeText); - } - return Ternary.False; - } - } - else { - const hasDifferentParameterIndex = sourcePredicate.parameterIndex !== targetPredicate.parameterIndex; - let hasDifferentTypes: boolean; - if (hasDifferentParameterIndex || - (hasDifferentTypes = !isTypeIdenticalTo(sourcePredicate.type, targetPredicate.type))) { - - if (reportErrors) { - const sourceParamText = sourcePredicate.parameterName; - const targetParamText = targetPredicate.parameterName; - const sourceTypeText = typeToString(sourcePredicate.type); - const targetTypeText = typeToString(targetPredicate.type); - - if (hasDifferentParameterIndex) { - reportError(Diagnostics.Parameter_0_is_not_in_the_same_position_as_parameter_1, - sourceParamText, - targetParamText); - } - else if (hasDifferentTypes) { - reportError(Diagnostics.Type_0_is_not_assignable_to_type_1, - sourceTypeText, - targetTypeText); - } - - reportError(Diagnostics.Type_predicate_0_is_not_assignable_to_1, - `${sourceParamText} is ${sourceTypeText}`, - `${targetParamText} is ${targetTypeText}`); - } - return Ternary.False; - } - } - } - else if (!source.typePredicate && target.typePredicate) { - if (reportErrors) { - reportError(Diagnostics.Signature_0_must_have_a_type_predicate, signatureToString(source)); - } - return Ternary.False; - } - const targetReturnType = getReturnTypeOfSignature(target); if (targetReturnType === voidType) return result; const sourceReturnType = getReturnTypeOfSignature(source); @@ -6239,6 +6190,11 @@ namespace ts { inferFromTypes(sourceTypes[i], targetTypes[i]); } } + else if (source.flags & TypeFlags.PredicateType && target.flags & TypeFlags.PredicateType) { + if ((source as PredicateType).predicate.kind === (target as PredicateType).predicate.kind) { + inferFromTypes((source as PredicateType).predicate.type, (target as PredicateType).predicate.type); + } + } else if (source.flags & TypeFlags.Tuple && target.flags & TypeFlags.Tuple && (source).elementTypes.length === (target).elementTypes.length) { // If source and target are tuples of the same size, infer from element types const sourceTypes = (source).elementTypes; @@ -6331,21 +6287,7 @@ namespace ts { function inferFromSignature(source: Signature, target: Signature) { forEachMatchingParameterType(source, target, inferFromTypes); - if (source.typePredicate && target.typePredicate) { - if (target.typePredicate.kind === source.typePredicate.kind) { - if ((target.typePredicate.kind === TypePredicateKind.Identifier - && (target.typePredicate as IdentifierTypePredicate).parameterIndex === (source.typePredicate as IdentifierTypePredicate).parameterIndex) - || target.typePredicate.kind === TypePredicateKind.This) { - // Return types from type predicates are treated as booleans. In order to infer types - // from type predicates we would need to infer using the type within the type predicate - // (i.e. 'Foo' from 'x is Foo'). - inferFromTypes(source.typePredicate.type, target.typePredicate.type); - } - } - } - else { - inferFromTypes(getReturnTypeOfSignature(source), getReturnTypeOfSignature(target)); - } + inferFromTypes(getReturnTypeOfSignature(source), getReturnTypeOfSignature(target)); } function inferFromIndexTypes(source: Type, target: Type, sourceKind: IndexKind, targetKind: IndexKind) { @@ -6791,11 +6733,12 @@ namespace ts { return type; } const signature = getResolvedSignature(expr); + const predicateType = getReturnTypeOfSignature(signature); - if (!signature.typePredicate) { + if (!predicateType || !(predicateType.flags & TypeFlags.PredicateType)) { return type; } - const predicate = signature.typePredicate; + const predicate = (predicateType as PredicateType).predicate; if (isIdentifierTypePredicate(predicate)) { const callExpression = expr as CallExpression; if (callExpression.arguments[predicate.parameterIndex] && @@ -6819,7 +6762,7 @@ namespace ts { return type; } - return narrowTypeByThisTypePredicate(type, (memberType as PredicateType).predicate, expr, assumeTrue); + return narrowTypeByThisTypePredicate(type, (memberType as PredicateType).predicate as ThisTypePredicate, expr, assumeTrue); } function narrowTypeByThisTypePredicate(type: Type, predicate: ThisTypePredicate, expression: Expression, assumeTrue: boolean): Type { @@ -11119,8 +11062,12 @@ namespace ts { if (node.type) { if (node.type.kind === SyntaxKind.TypePredicate) { - const typePredicate = getSignatureFromDeclaration(node).typePredicate; - const typePredicateNode = node.type; + const returnType = getReturnTypeOfSignature(getSignatureFromDeclaration(node)); + if (!returnType || !(returnType.flags & TypeFlags.PredicateType)) { + return; + } + const typePredicate = (returnType as PredicateType).predicate; + const typePredicateNode = node.type as TypePredicateNode; checkSourceElement(typePredicateNode); if (isIdentifierTypePredicate(typePredicate)) { if (typePredicate.parameterIndex >= 0) { @@ -13126,7 +13073,7 @@ namespace ts { error(node.expression, Diagnostics.Return_type_of_constructor_signature_must_be_assignable_to_the_instance_type_of_the_class); } } - else if (func.type || isGetAccessorWithAnnotatatedSetAccessor(func) || signature.typePredicate) { + else if (func.type || isGetAccessorWithAnnotatatedSetAccessor(func) || returnType.flags & TypeFlags.PredicateType) { if (isAsyncFunctionLike(func)) { const promisedType = getPromisedType(returnType); const awaitedType = checkAwaitedType(exprType, node.expression, Diagnostics.Return_expression_in_async_function_does_not_have_a_valid_callable_then_member); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 54eb4f449dd13..9358ec2433352 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2143,7 +2143,7 @@ namespace ts { // Predicate types (TypeFlags.Predicate) export interface PredicateType extends Type { - predicate: ThisTypePredicate; + predicate: ThisTypePredicate | IdentifierTypePredicate; } // String literal types (TypeFlags.StringLiteral) @@ -2262,7 +2262,6 @@ namespace ts { declaration: SignatureDeclaration; // Originating declaration typeParameters: TypeParameter[]; // Type parameters (undefined if non-generic) parameters: Symbol[]; // Parameters - typePredicate?: ThisTypePredicate | IdentifierTypePredicate; // Type predicate /* @internal */ resolvedReturnType: Type; // Resolved return type /* @internal */ diff --git a/tests/baselines/reference/arrayBufferIsViewNarrowsType.types b/tests/baselines/reference/arrayBufferIsViewNarrowsType.types index 129b7d601d863..b9d4f3db81bd3 100644 --- a/tests/baselines/reference/arrayBufferIsViewNarrowsType.types +++ b/tests/baselines/reference/arrayBufferIsViewNarrowsType.types @@ -4,7 +4,7 @@ var obj: Object; >Object : Object if (ArrayBuffer.isView(obj)) { ->ArrayBuffer.isView(obj) : boolean +>ArrayBuffer.isView(obj) : arg is ArrayBufferView >ArrayBuffer.isView : (arg: any) => arg is ArrayBufferView >ArrayBuffer : ArrayBufferConstructor >isView : (arg: any) => arg is ArrayBufferView diff --git a/tests/baselines/reference/isArray.types b/tests/baselines/reference/isArray.types index bc452b12bef01..de54e9d064cdd 100644 --- a/tests/baselines/reference/isArray.types +++ b/tests/baselines/reference/isArray.types @@ -4,7 +4,7 @@ var maybeArray: number | number[]; if (Array.isArray(maybeArray)) { ->Array.isArray(maybeArray) : boolean +>Array.isArray(maybeArray) : arg is any[] >Array.isArray : (arg: any) => arg is any[] >Array : ArrayConstructor >isArray : (arg: any) => arg is any[] diff --git a/tests/baselines/reference/stringLiteralCheckedInIf02.types b/tests/baselines/reference/stringLiteralCheckedInIf02.types index 79f4c6a223a71..f91eea030976d 100644 --- a/tests/baselines/reference/stringLiteralCheckedInIf02.types +++ b/tests/baselines/reference/stringLiteralCheckedInIf02.types @@ -31,7 +31,7 @@ function f(foo: T) { >T : ("a" | "b")[] | "a" | "b" if (isS(foo)) { ->isS(foo) : boolean +>isS(foo) : t is "a" | "b" >isS : (t: ("a" | "b")[] | "a" | "b") => t is "a" | "b" >foo : ("a" | "b")[] | "a" | "b" diff --git a/tests/baselines/reference/stringLiteralTypesAsTags01.errors.txt b/tests/baselines/reference/stringLiteralTypesAsTags01.errors.txt index 6e4f8943fb6af..de3b7be2612a0 100644 --- a/tests/baselines/reference/stringLiteralTypesAsTags01.errors.txt +++ b/tests/baselines/reference/stringLiteralTypesAsTags01.errors.txt @@ -1,10 +1,7 @@ -tests/cases/conformance/types/stringLiteral/stringLiteralTypesAsTags01.ts(18,10): error TS2382: Specialized overload signature is not assignable to any non-specialized signature. -tests/cases/conformance/types/stringLiteral/stringLiteralTypesAsTags01.ts(19,10): error TS2382: Specialized overload signature is not assignable to any non-specialized signature. -tests/cases/conformance/types/stringLiteral/stringLiteralTypesAsTags01.ts(20,10): error TS2394: Overload signature is not compatible with function implementation. tests/cases/conformance/types/stringLiteral/stringLiteralTypesAsTags01.ts(22,21): error TS2304: Cannot find name 'is'. -==== tests/cases/conformance/types/stringLiteral/stringLiteralTypesAsTags01.ts (4 errors) ==== +==== tests/cases/conformance/types/stringLiteral/stringLiteralTypesAsTags01.ts (1 errors) ==== type Kind = "A" | "B" @@ -23,14 +20,8 @@ tests/cases/conformance/types/stringLiteral/stringLiteralTypesAsTags01.ts(22,21) } function hasKind(entity: Entity, kind: "A"): entity is A; - ~~~~~~~ -!!! error TS2382: Specialized overload signature is not assignable to any non-specialized signature. function hasKind(entity: Entity, kind: "B"): entity is B; - ~~~~~~~ -!!! error TS2382: Specialized overload signature is not assignable to any non-specialized signature. function hasKind(entity: Entity, kind: Kind): entity is Entity; - ~~~~~~~ -!!! error TS2394: Overload signature is not compatible with function implementation. function hasKind(entity: Entity, kind: Kind): boolean { return kind === is; ~~ diff --git a/tests/baselines/reference/typeGuardFunction.types b/tests/baselines/reference/typeGuardFunction.types index 50a5fcaf32411..9bab1e7ca2ca3 100644 --- a/tests/baselines/reference/typeGuardFunction.types +++ b/tests/baselines/reference/typeGuardFunction.types @@ -54,7 +54,7 @@ var b: B; // Basic if (isC(a)) { ->isC(a) : boolean +>isC(a) : p1 is C >isC : (p1: any) => p1 is C >a : A @@ -70,7 +70,7 @@ var subType: C; >C : C if(isA(subType)) { ->isA(subType) : boolean +>isA(subType) : p1 is A >isA : (p1: any) => p1 is A >subType : C @@ -87,7 +87,7 @@ var union: A | B; >B : B if(isA(union)) { ->isA(union) : boolean +>isA(union) : p1 is A >isA : (p1: any) => p1 is A >union : A | B @@ -118,7 +118,7 @@ declare function isC_multipleParams(p1, p2): p1 is C; >C : C if (isC_multipleParams(a, 0)) { ->isC_multipleParams(a, 0) : boolean +>isC_multipleParams(a, 0) : p1 is C >isC_multipleParams : (p1: any, p2: any) => p1 is C >a : A >0 : number @@ -197,7 +197,7 @@ declare function acceptingBoolean(a: boolean); acceptingBoolean(isA(a)); >acceptingBoolean(isA(a)) : any >acceptingBoolean : (a: boolean) => any ->isA(a) : boolean +>isA(a) : p1 is A >isA : (p1: any) => p1 is A >a : A @@ -223,8 +223,8 @@ let union2: C | B; let union3: boolean | B = isA(union2) || union2; >union3 : boolean | B >B : B ->isA(union2) || union2 : boolean | B ->isA(union2) : boolean +>isA(union2) || union2 : p1 is A | B +>isA(union2) : p1 is A >isA : (p1: any) => p1 is A >union2 : C | B >union2 : B diff --git a/tests/baselines/reference/typeGuardFunctionErrors.errors.txt b/tests/baselines/reference/typeGuardFunctionErrors.errors.txt index 2d40520a8ce57..445738e97839a 100644 --- a/tests/baselines/reference/typeGuardFunctionErrors.errors.txt +++ b/tests/baselines/reference/typeGuardFunctionErrors.errors.txt @@ -1,4 +1,4 @@ -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(15,12): error TS2322: Type 'string' is not assignable to type 'x is A'. 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. @@ -16,8 +16,7 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(70,7): tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(75,46): error TS2345: Argument of type '(p1: any) => p1 is C' is not assignable to parameter of type '(p1: any) => p1 is B'. Type predicate 'p1 is C' is not assignable to 'p1 is B'. Type 'C' is not assignable to type 'B'. -tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(79,1): error TS2322: Type '(p1: any, p2: any) => boolean' is not assignable to type '(p1: any, p2: any) => p1 is A'. - Signature '(p1: any, p2: any): boolean' must have a type predicate. + Property 'propB' is missing in type 'C'. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(85,1): error TS2322: Type '(p1: any, p2: any) => p2 is A' is not assignable to type '(p1: any, p2: any) => p1 is A'. Type predicate 'p2 is A' is not assignable to 'p1 is A'. Parameter 'p2' is not in the same position as parameter 'p1'. @@ -25,7 +24,6 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(91,1): 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(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 @@ -41,7 +39,7 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(133,34 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 (34 errors) ==== +==== tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts (32 errors) ==== class A { propA: number; @@ -58,7 +56,7 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(137,39 function hasANonBooleanReturnStatement(x): x is A { return ''; ~~ -!!! error TS2322: Type 'string' is not assignable to type 'boolean'. +!!! error TS2322: Type 'string' is not assignable to type 'x is A'. } function hasTypeGuardTypeInsideTypeGuardType(x): x is x is A { @@ -150,13 +148,11 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(137,39 !!! error TS2345: Argument of type '(p1: any) => p1 is C' is not assignable to parameter of type '(p1: any) => p1 is B'. !!! error TS2345: Type predicate 'p1 is C' is not assignable to 'p1 is B'. !!! error TS2345: Type 'C' is not assignable to type 'B'. +!!! error TS2345: Property 'propB' is missing in type 'C'. // Boolean not assignable to type guard var assign1: (p1, p2) => p1 is A; assign1 = function(p1, p2): boolean { - ~~~~~~~ -!!! error TS2322: Type '(p1: any, p2: any) => boolean' is not assignable to type '(p1: any, p2: any) => p1 is A'. -!!! error TS2322: Signature '(p1: any, p2: any): boolean' must have a type predicate. return true; }; @@ -194,8 +190,6 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(137,39 // 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'. diff --git a/tests/baselines/reference/typeGuardFunctionGenerics.types b/tests/baselines/reference/typeGuardFunctionGenerics.types index c4655e71f0cb5..1f2ad2b69beab 100644 --- a/tests/baselines/reference/typeGuardFunctionGenerics.types +++ b/tests/baselines/reference/typeGuardFunctionGenerics.types @@ -100,7 +100,7 @@ let test1: boolean = funA(isB); >isB : (p1: any) => p1 is B if (funB(retC, a)) { ->funB(retC, a) : boolean +>funB(retC, a) : p2 is C >funB : (p1: (p1: any) => T, p2: any) => p2 is T >retC : (x: any) => C >a : A @@ -118,7 +118,7 @@ let test2: B = funC(isB); >isB : (p1: any) => p1 is B if (funD(isC, a)) { ->funD(isC, a) : boolean +>funD(isC, a) : p2 is C >funD : (p1: (p1: any) => p1 is T, p2: any) => p2 is T >isC : (p1: any) => p1 is C >a : A diff --git a/tests/baselines/reference/typeGuardFunctionOfFormThis.types b/tests/baselines/reference/typeGuardFunctionOfFormThis.types index 6b21fea34576a..e91c77dd07aaa 100644 --- a/tests/baselines/reference/typeGuardFunctionOfFormThis.types +++ b/tests/baselines/reference/typeGuardFunctionOfFormThis.types @@ -45,7 +45,7 @@ let a: RoyalGuard = new FollowerGuard(); >FollowerGuard : typeof FollowerGuard if (a.isLeader()) { ->a.isLeader() : boolean +>a.isLeader() : this is LeadGuard >a.isLeader : () => this is LeadGuard >a : RoyalGuard >isLeader : () => this is LeadGuard @@ -57,7 +57,7 @@ if (a.isLeader()) { >lead : () => void } else if (a.isFollower()) { ->a.isFollower() : boolean +>a.isFollower() : this is FollowerGuard >a.isFollower : () => this is FollowerGuard >a : RoyalGuard >isFollower : () => this is FollowerGuard @@ -78,7 +78,7 @@ let b: GuardInterface; >GuardInterface : GuardInterface if (b.isLeader()) { ->b.isLeader() : boolean +>b.isLeader() : this is LeadGuard >b.isLeader : () => this is LeadGuard >b : GuardInterface >isLeader : () => this is LeadGuard @@ -90,7 +90,7 @@ if (b.isLeader()) { >lead : () => void } else if (b.isFollower()) { ->b.isFollower() : boolean +>b.isFollower() : this is FollowerGuard >b.isFollower : () => this is FollowerGuard >b : GuardInterface >isFollower : () => this is FollowerGuard @@ -103,8 +103,8 @@ else if (b.isFollower()) { } if (((a.isLeader)())) { ->((a.isLeader)()) : boolean ->(a.isLeader)() : boolean +>((a.isLeader)()) : this is LeadGuard +>(a.isLeader)() : this is LeadGuard >(a.isLeader) : () => this is LeadGuard >a.isLeader : () => this is LeadGuard >a : RoyalGuard @@ -117,8 +117,8 @@ if (((a.isLeader)())) { >lead : () => void } else if (((a).isFollower())) { ->((a).isFollower()) : boolean ->(a).isFollower() : boolean +>((a).isFollower()) : this is FollowerGuard +>(a).isFollower() : this is FollowerGuard >(a).isFollower : () => this is FollowerGuard >(a) : RoyalGuard >a : RoyalGuard @@ -132,8 +132,8 @@ else if (((a).isFollower())) { } if (((a["isLeader"])())) { ->((a["isLeader"])()) : boolean ->(a["isLeader"])() : boolean +>((a["isLeader"])()) : this is LeadGuard +>(a["isLeader"])() : this is LeadGuard >(a["isLeader"]) : () => this is LeadGuard >a["isLeader"] : () => this is LeadGuard >a : RoyalGuard @@ -146,8 +146,8 @@ if (((a["isLeader"])())) { >lead : () => void } else if (((a)["isFollower"]())) { ->((a)["isFollower"]()) : boolean ->(a)["isFollower"]() : boolean +>((a)["isFollower"]()) : this is FollowerGuard +>(a)["isFollower"]() : this is FollowerGuard >(a)["isFollower"] : () => this is FollowerGuard >(a) : RoyalGuard >a : RoyalGuard @@ -166,7 +166,7 @@ var holder2 = {a}; >a : RoyalGuard if (holder2.a.isLeader()) { ->holder2.a.isLeader() : boolean +>holder2.a.isLeader() : this is LeadGuard >holder2.a.isLeader : () => this is LeadGuard >holder2.a : RoyalGuard >holder2 : { a: RoyalGuard; } @@ -232,7 +232,7 @@ let guard = new ArrowGuard(); >ArrowGuard : typeof ArrowGuard if (guard.isElite()) { ->guard.isElite() : boolean +>guard.isElite() : this is ArrowElite >guard.isElite : () => this is ArrowElite >guard : ArrowGuard >isElite : () => this is ArrowElite @@ -244,7 +244,7 @@ if (guard.isElite()) { >defend : () => void } else if (guard.isMedic()) { ->guard.isMedic() : boolean +>guard.isMedic() : this is ArrowMedic >guard.isMedic : () => this is ArrowMedic >guard : ArrowGuard >isMedic : () => this is ArrowMedic @@ -297,7 +297,7 @@ let crate: Crate<{}>; >Crate : Crate if (crate.isSundries()) { ->crate.isSundries() : boolean +>crate.isSundries() : this is Crate >crate.isSundries : () => this is Crate >crate : Crate<{}> >isSundries : () => this is Crate @@ -312,7 +312,7 @@ if (crate.isSundries()) { >true : boolean } else if (crate.isSupplies()) { ->crate.isSupplies() : boolean +>crate.isSupplies() : this is Crate >crate.isSupplies : () => this is Crate >crate : Crate<{}> >isSupplies : () => this is Crate @@ -405,7 +405,7 @@ a.isFollower = mimic.isFollower; >isFollower : () => this is MimicFollower if (mimic.isFollower()) { ->mimic.isFollower() : boolean +>mimic.isFollower() : this is MimicFollower >mimic.isFollower : () => this is MimicFollower >mimic : MimicGuard >isFollower : () => this is MimicFollower diff --git a/tests/baselines/reference/typeGuardFunctionOfFormThisErrors.errors.txt b/tests/baselines/reference/typeGuardFunctionOfFormThisErrors.errors.txt index 605282f19bb27..9f9f5d6533cab 100644 --- a/tests/baselines/reference/typeGuardFunctionOfFormThisErrors.errors.txt +++ b/tests/baselines/reference/typeGuardFunctionOfFormThisErrors.errors.txt @@ -1,11 +1,17 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors.ts(23,1): error TS2322: Type '() => this is LeadGuard' is not assignable to type '() => this is FollowerGuard'. - Type 'LeadGuard' is not assignable to type 'FollowerGuard'. + Type predicate 'this is LeadGuard' is not assignable to 'this is FollowerGuard'. + Type 'LeadGuard' is not assignable to type 'FollowerGuard'. + Property 'follow' is missing in type 'LeadGuard'. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors.ts(24,1): error TS2322: Type '() => this is FollowerGuard' is not assignable to type '() => this is LeadGuard'. - Type 'FollowerGuard' is not assignable to type 'LeadGuard'. + Type predicate 'this is FollowerGuard' is not assignable to 'this is LeadGuard'. + Type 'FollowerGuard' is not assignable to type 'LeadGuard'. + Property 'lead' is missing in type 'FollowerGuard'. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors.ts(26,1): error TS2322: Type '() => this is LeadGuard' is not assignable to type '() => this is FollowerGuard'. - Type 'LeadGuard' is not assignable to type 'FollowerGuard'. + Type predicate 'this is LeadGuard' is not assignable to 'this is FollowerGuard'. + Type 'LeadGuard' is not assignable to type 'FollowerGuard'. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors.ts(27,1): error TS2322: Type '() => this is FollowerGuard' is not assignable to type '() => this is LeadGuard'. - Type 'FollowerGuard' is not assignable to type 'LeadGuard'. + Type predicate 'this is FollowerGuard' is not assignable to 'this is LeadGuard'. + Type 'FollowerGuard' is not assignable to type 'LeadGuard'. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors.ts(29,32): error TS2526: A 'this' type is available only in a non-static member of a class or interface. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors.ts(55,7): error TS2339: Property 'follow' does not exist on type 'RoyalGuard'. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors.ts(58,7): error TS2339: Property 'lead' does not exist on type 'RoyalGuard'. @@ -37,20 +43,26 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors b.isFollower = b.isLeader; ~~~~~~~~~~~~ !!! error TS2322: Type '() => this is LeadGuard' is not assignable to type '() => this is FollowerGuard'. -!!! error TS2322: Type 'LeadGuard' is not assignable to type 'FollowerGuard'. +!!! error TS2322: Type predicate 'this is LeadGuard' is not assignable to 'this is FollowerGuard'. +!!! error TS2322: Type 'LeadGuard' is not assignable to type 'FollowerGuard'. +!!! error TS2322: Property 'follow' is missing in type 'LeadGuard'. b.isLeader = b.isFollower; ~~~~~~~~~~ !!! error TS2322: Type '() => this is FollowerGuard' is not assignable to type '() => this is LeadGuard'. -!!! error TS2322: Type 'FollowerGuard' is not assignable to type 'LeadGuard'. +!!! error TS2322: Type predicate 'this is FollowerGuard' is not assignable to 'this is LeadGuard'. +!!! error TS2322: Type 'FollowerGuard' is not assignable to type 'LeadGuard'. +!!! error TS2322: Property 'lead' is missing in type 'FollowerGuard'. a.isFollower = a.isLeader; ~~~~~~~~~~~~ !!! error TS2322: Type '() => this is LeadGuard' is not assignable to type '() => this is FollowerGuard'. -!!! error TS2322: Type 'LeadGuard' is not assignable to type 'FollowerGuard'. +!!! error TS2322: Type predicate 'this is LeadGuard' is not assignable to 'this is FollowerGuard'. +!!! error TS2322: Type 'LeadGuard' is not assignable to type 'FollowerGuard'. a.isLeader = a.isFollower; ~~~~~~~~~~ !!! error TS2322: Type '() => this is FollowerGuard' is not assignable to type '() => this is LeadGuard'. -!!! error TS2322: Type 'FollowerGuard' is not assignable to type 'LeadGuard'. +!!! error TS2322: Type predicate 'this is FollowerGuard' is not assignable to 'this is LeadGuard'. +!!! error TS2322: Type 'FollowerGuard' is not assignable to type 'LeadGuard'. function invalidGuard(c: any): this is number { ~~~~ diff --git a/tests/baselines/reference/typeGuardOfFormIsType.types b/tests/baselines/reference/typeGuardOfFormIsType.types index e2059be7b637e..aa8f8cc7d606b 100644 --- a/tests/baselines/reference/typeGuardOfFormIsType.types +++ b/tests/baselines/reference/typeGuardOfFormIsType.types @@ -67,7 +67,7 @@ str = isC1(c1Orc2) && c1Orc2.p1; // C1 >str = isC1(c1Orc2) && c1Orc2.p1 : string >str : string >isC1(c1Orc2) && c1Orc2.p1 : string ->isC1(c1Orc2) : boolean +>isC1(c1Orc2) : x is C1 >isC1 : (x: any) => x is C1 >c1Orc2 : C1 | C2 >c1Orc2.p1 : string @@ -78,7 +78,7 @@ num = isC2(c1Orc2) && c1Orc2.p2; // C2 >num = isC2(c1Orc2) && c1Orc2.p2 : number >num : number >isC2(c1Orc2) && c1Orc2.p2 : number ->isC2(c1Orc2) : boolean +>isC2(c1Orc2) : x is C2 >isC2 : (x: any) => x is C2 >c1Orc2 : C1 | C2 >c1Orc2.p2 : number @@ -89,7 +89,7 @@ str = isD1(c1Orc2) && c1Orc2.p1; // D1 >str = isD1(c1Orc2) && c1Orc2.p1 : string >str : string >isD1(c1Orc2) && c1Orc2.p1 : string ->isD1(c1Orc2) : boolean +>isD1(c1Orc2) : x is D1 >isD1 : (x: any) => x is D1 >c1Orc2 : C1 | C2 >c1Orc2.p1 : string @@ -100,7 +100,7 @@ num = isD1(c1Orc2) && c1Orc2.p3; // D1 >num = isD1(c1Orc2) && c1Orc2.p3 : number >num : number >isD1(c1Orc2) && c1Orc2.p3 : number ->isD1(c1Orc2) : boolean +>isD1(c1Orc2) : x is D1 >isD1 : (x: any) => x is D1 >c1Orc2 : C1 | C2 >c1Orc2.p3 : number @@ -116,7 +116,7 @@ num = isC2(c2Ord1) && c2Ord1.p2; // C2 >num = isC2(c2Ord1) && c2Ord1.p2 : number >num : number >isC2(c2Ord1) && c2Ord1.p2 : number ->isC2(c2Ord1) : boolean +>isC2(c2Ord1) : x is C2 >isC2 : (x: any) => x is C2 >c2Ord1 : C2 | D1 >c2Ord1.p2 : number @@ -127,7 +127,7 @@ num = isD1(c2Ord1) && c2Ord1.p3; // D1 >num = isD1(c2Ord1) && c2Ord1.p3 : number >num : number >isD1(c2Ord1) && c2Ord1.p3 : number ->isD1(c2Ord1) : boolean +>isD1(c2Ord1) : x is D1 >isD1 : (x: any) => x is D1 >c2Ord1 : C2 | D1 >c2Ord1.p3 : number @@ -138,7 +138,7 @@ str = isD1(c2Ord1) && c2Ord1.p1; // D1 >str = isD1(c2Ord1) && c2Ord1.p1 : string >str : string >isD1(c2Ord1) && c2Ord1.p1 : string ->isD1(c2Ord1) : boolean +>isD1(c2Ord1) : x is D1 >isD1 : (x: any) => x is D1 >c2Ord1 : C2 | D1 >c2Ord1.p1 : string @@ -150,7 +150,7 @@ var r2: C2 | D1 = isC1(c2Ord1) && c2Ord1; // C2 | D1 >C2 : C2 >D1 : D1 >isC1(c2Ord1) && c2Ord1 : D1 ->isC1(c2Ord1) : boolean +>isC1(c2Ord1) : x is C1 >isC1 : (x: any) => x is C1 >c2Ord1 : C2 | D1 >c2Ord1 : D1 diff --git a/tests/baselines/reference/typeGuardOfFormIsTypeOnInterfaces.types b/tests/baselines/reference/typeGuardOfFormIsTypeOnInterfaces.types index ea169e954138c..4e28e6a4d3801 100644 --- a/tests/baselines/reference/typeGuardOfFormIsTypeOnInterfaces.types +++ b/tests/baselines/reference/typeGuardOfFormIsTypeOnInterfaces.types @@ -98,7 +98,7 @@ str = isC1(c1Orc2) && c1Orc2.p1; // C1 >str = isC1(c1Orc2) && c1Orc2.p1 : string >str : string >isC1(c1Orc2) && c1Orc2.p1 : string ->isC1(c1Orc2) : boolean +>isC1(c1Orc2) : x is C1 >isC1 : (x: any) => x is C1 >c1Orc2 : C1 | C2 >c1Orc2.p1 : string @@ -109,7 +109,7 @@ num = isC2(c1Orc2) && c1Orc2.p2; // C2 >num = isC2(c1Orc2) && c1Orc2.p2 : number >num : number >isC2(c1Orc2) && c1Orc2.p2 : number ->isC2(c1Orc2) : boolean +>isC2(c1Orc2) : x is C2 >isC2 : (x: any) => x is C2 >c1Orc2 : C1 | C2 >c1Orc2.p2 : number @@ -120,7 +120,7 @@ str = isD1(c1Orc2) && c1Orc2.p1; // D1 >str = isD1(c1Orc2) && c1Orc2.p1 : string >str : string >isD1(c1Orc2) && c1Orc2.p1 : string ->isD1(c1Orc2) : boolean +>isD1(c1Orc2) : x is D1 >isD1 : (x: any) => x is D1 >c1Orc2 : C1 | C2 >c1Orc2.p1 : string @@ -131,7 +131,7 @@ num = isD1(c1Orc2) && c1Orc2.p3; // D1 >num = isD1(c1Orc2) && c1Orc2.p3 : number >num : number >isD1(c1Orc2) && c1Orc2.p3 : number ->isD1(c1Orc2) : boolean +>isD1(c1Orc2) : x is D1 >isD1 : (x: any) => x is D1 >c1Orc2 : C1 | C2 >c1Orc2.p3 : number @@ -147,7 +147,7 @@ num = isC2(c2Ord1) && c2Ord1.p2; // C2 >num = isC2(c2Ord1) && c2Ord1.p2 : number >num : number >isC2(c2Ord1) && c2Ord1.p2 : number ->isC2(c2Ord1) : boolean +>isC2(c2Ord1) : x is C2 >isC2 : (x: any) => x is C2 >c2Ord1 : C2 | D1 >c2Ord1.p2 : number @@ -158,7 +158,7 @@ num = isD1(c2Ord1) && c2Ord1.p3; // D1 >num = isD1(c2Ord1) && c2Ord1.p3 : number >num : number >isD1(c2Ord1) && c2Ord1.p3 : number ->isD1(c2Ord1) : boolean +>isD1(c2Ord1) : x is D1 >isD1 : (x: any) => x is D1 >c2Ord1 : C2 | D1 >c2Ord1.p3 : number @@ -169,7 +169,7 @@ str = isD1(c2Ord1) && c2Ord1.p1; // D1 >str = isD1(c2Ord1) && c2Ord1.p1 : string >str : string >isD1(c2Ord1) && c2Ord1.p1 : string ->isD1(c2Ord1) : boolean +>isD1(c2Ord1) : x is D1 >isD1 : (x: any) => x is D1 >c2Ord1 : C2 | D1 >c2Ord1.p1 : string @@ -181,7 +181,7 @@ var r2: C2 | D1 = isC1(c2Ord1) && c2Ord1; // C2 | D1 >C2 : C2 >D1 : D1 >isC1(c2Ord1) && c2Ord1 : D1 ->isC1(c2Ord1) : boolean +>isC1(c2Ord1) : x is C1 >isC1 : (x: any) => x is C1 >c2Ord1 : C2 | D1 >c2Ord1 : D1 diff --git a/tests/baselines/reference/typeGuardOfFormThisMember.js b/tests/baselines/reference/typeGuardOfFormThisMember.js index cbf70c4a64086..8cb5b89aa90c5 100644 --- a/tests/baselines/reference/typeGuardOfFormThisMember.js +++ b/tests/baselines/reference/typeGuardOfFormThisMember.js @@ -2,6 +2,7 @@ // There's a 'File' class in the stdlib, wrap with a namespace to avoid collision namespace Test { export class FileSystemObject { + isFSO: this is FileSystemObject; get isFile(): this is File { return this instanceof File; } @@ -27,8 +28,8 @@ namespace Test { let file: FileSystemObject = new File("foo/bar.txt", "foo"); file.isNetworked = false; - file.isNetworked = file.isDirectory; - file.isFile = file.isNetworked; + file.isFSO = file.isFile; + file.isFile = true; let x = file.isFile; if (file.isFile) { file.content; @@ -133,8 +134,8 @@ var Test; Test.Directory = Directory; var file = new File("foo/bar.txt", "foo"); file.isNetworked = false; - file.isNetworked = file.isDirectory; - file.isFile = file.isNetworked; + file.isFSO = file.isFile; + file.isFile = true; var x = file.isFile; if (file.isFile) { file.content; @@ -167,6 +168,7 @@ var Test; declare namespace Test { class FileSystemObject { path: string; + isFSO: this is FileSystemObject; isFile: this is File; isDirectory: this is Directory; isNetworked: this is (Networked & this); diff --git a/tests/baselines/reference/typeGuardOfFormThisMember.symbols b/tests/baselines/reference/typeGuardOfFormThisMember.symbols index 84573ddf0c703..50e15b68ca40d 100644 --- a/tests/baselines/reference/typeGuardOfFormThisMember.symbols +++ b/tests/baselines/reference/typeGuardOfFormThisMember.symbols @@ -6,234 +6,235 @@ namespace Test { export class FileSystemObject { >FileSystemObject : Symbol(FileSystemObject, Decl(typeGuardOfFormThisMember.ts, 1, 16)) + isFSO: this is FileSystemObject; +>isFSO : Symbol(isFSO, Decl(typeGuardOfFormThisMember.ts, 2, 32)) +>FileSystemObject : Symbol(FileSystemObject, Decl(typeGuardOfFormThisMember.ts, 1, 16)) + get isFile(): this is File { ->isFile : Symbol(isFile, Decl(typeGuardOfFormThisMember.ts, 2, 32), Decl(typeGuardOfFormThisMember.ts, 5, 3)) ->File : Symbol(File, Decl(typeGuardOfFormThisMember.ts, 14, 2)) +>isFile : Symbol(isFile, Decl(typeGuardOfFormThisMember.ts, 3, 34), Decl(typeGuardOfFormThisMember.ts, 6, 3)) +>File : Symbol(File, Decl(typeGuardOfFormThisMember.ts, 15, 2)) return this instanceof File; >this : Symbol(FileSystemObject, Decl(typeGuardOfFormThisMember.ts, 1, 16)) ->File : Symbol(File, Decl(typeGuardOfFormThisMember.ts, 14, 2)) +>File : Symbol(File, Decl(typeGuardOfFormThisMember.ts, 15, 2)) } set isFile(param) { ->isFile : Symbol(isFile, Decl(typeGuardOfFormThisMember.ts, 2, 32), Decl(typeGuardOfFormThisMember.ts, 5, 3)) ->param : Symbol(param, Decl(typeGuardOfFormThisMember.ts, 6, 13)) +>isFile : Symbol(isFile, Decl(typeGuardOfFormThisMember.ts, 3, 34), Decl(typeGuardOfFormThisMember.ts, 6, 3)) +>param : Symbol(param, Decl(typeGuardOfFormThisMember.ts, 7, 13)) // noop } get isDirectory(): this is Directory { ->isDirectory : Symbol(isDirectory, Decl(typeGuardOfFormThisMember.ts, 8, 3)) ->Directory : Symbol(Directory, Decl(typeGuardOfFormThisMember.ts, 18, 2)) +>isDirectory : Symbol(isDirectory, Decl(typeGuardOfFormThisMember.ts, 9, 3)) +>Directory : Symbol(Directory, Decl(typeGuardOfFormThisMember.ts, 19, 2)) return this instanceof Directory; >this : Symbol(FileSystemObject, Decl(typeGuardOfFormThisMember.ts, 1, 16)) ->Directory : Symbol(Directory, Decl(typeGuardOfFormThisMember.ts, 18, 2)) +>Directory : Symbol(Directory, Decl(typeGuardOfFormThisMember.ts, 19, 2)) } isNetworked: this is (Networked & this); ->isNetworked : Symbol(isNetworked, Decl(typeGuardOfFormThisMember.ts, 11, 3)) ->Networked : Symbol(Networked, Decl(typeGuardOfFormThisMember.ts, 21, 2)) +>isNetworked : Symbol(isNetworked, Decl(typeGuardOfFormThisMember.ts, 12, 3)) +>Networked : Symbol(Networked, Decl(typeGuardOfFormThisMember.ts, 22, 2)) constructor(public path: string) {} ->path : Symbol(path, Decl(typeGuardOfFormThisMember.ts, 13, 14)) +>path : Symbol(path, Decl(typeGuardOfFormThisMember.ts, 14, 14)) } export class File extends FileSystemObject { ->File : Symbol(File, Decl(typeGuardOfFormThisMember.ts, 14, 2)) +>File : Symbol(File, Decl(typeGuardOfFormThisMember.ts, 15, 2)) >FileSystemObject : Symbol(FileSystemObject, Decl(typeGuardOfFormThisMember.ts, 1, 16)) constructor(path: string, public content: string) { super(path); } ->path : Symbol(path, Decl(typeGuardOfFormThisMember.ts, 17, 14)) ->content : Symbol(content, Decl(typeGuardOfFormThisMember.ts, 17, 27)) +>path : Symbol(path, Decl(typeGuardOfFormThisMember.ts, 18, 14)) +>content : Symbol(content, Decl(typeGuardOfFormThisMember.ts, 18, 27)) >super : Symbol(FileSystemObject, Decl(typeGuardOfFormThisMember.ts, 1, 16)) ->path : Symbol(path, Decl(typeGuardOfFormThisMember.ts, 17, 14)) +>path : Symbol(path, Decl(typeGuardOfFormThisMember.ts, 18, 14)) } export class Directory extends FileSystemObject { ->Directory : Symbol(Directory, Decl(typeGuardOfFormThisMember.ts, 18, 2)) +>Directory : Symbol(Directory, Decl(typeGuardOfFormThisMember.ts, 19, 2)) >FileSystemObject : Symbol(FileSystemObject, Decl(typeGuardOfFormThisMember.ts, 1, 16)) children: FileSystemObject[]; ->children : Symbol(children, Decl(typeGuardOfFormThisMember.ts, 19, 50)) +>children : Symbol(children, Decl(typeGuardOfFormThisMember.ts, 20, 50)) >FileSystemObject : Symbol(FileSystemObject, Decl(typeGuardOfFormThisMember.ts, 1, 16)) } export interface Networked { ->Networked : Symbol(Networked, Decl(typeGuardOfFormThisMember.ts, 21, 2)) +>Networked : Symbol(Networked, Decl(typeGuardOfFormThisMember.ts, 22, 2)) host: string; ->host : Symbol(host, Decl(typeGuardOfFormThisMember.ts, 22, 29)) +>host : Symbol(host, Decl(typeGuardOfFormThisMember.ts, 23, 29)) } let file: FileSystemObject = new File("foo/bar.txt", "foo"); ->file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4)) +>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 27, 4)) >FileSystemObject : Symbol(FileSystemObject, Decl(typeGuardOfFormThisMember.ts, 1, 16)) ->File : Symbol(File, Decl(typeGuardOfFormThisMember.ts, 14, 2)) +>File : Symbol(File, Decl(typeGuardOfFormThisMember.ts, 15, 2)) file.isNetworked = false; ->file.isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 11, 3)) ->file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4)) ->isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 11, 3)) - - file.isNetworked = file.isDirectory; ->file.isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 11, 3)) ->file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4)) ->isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 11, 3)) ->file.isDirectory : Symbol(FileSystemObject.isDirectory, Decl(typeGuardOfFormThisMember.ts, 8, 3)) ->file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4)) ->isDirectory : Symbol(FileSystemObject.isDirectory, Decl(typeGuardOfFormThisMember.ts, 8, 3)) - - file.isFile = file.isNetworked; ->file.isFile : Symbol(FileSystemObject.isFile, Decl(typeGuardOfFormThisMember.ts, 2, 32), Decl(typeGuardOfFormThisMember.ts, 5, 3)) ->file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4)) ->isFile : Symbol(FileSystemObject.isFile, Decl(typeGuardOfFormThisMember.ts, 2, 32), Decl(typeGuardOfFormThisMember.ts, 5, 3)) ->file.isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 11, 3)) ->file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4)) ->isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 11, 3)) +>file.isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 12, 3)) +>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 27, 4)) +>isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 12, 3)) + + file.isFSO = file.isFile; +>file.isFSO : Symbol(FileSystemObject.isFSO, Decl(typeGuardOfFormThisMember.ts, 2, 32)) +>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 27, 4)) +>isFSO : Symbol(FileSystemObject.isFSO, Decl(typeGuardOfFormThisMember.ts, 2, 32)) +>file.isFile : Symbol(FileSystemObject.isFile, Decl(typeGuardOfFormThisMember.ts, 3, 34), Decl(typeGuardOfFormThisMember.ts, 6, 3)) +>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 27, 4)) +>isFile : Symbol(FileSystemObject.isFile, Decl(typeGuardOfFormThisMember.ts, 3, 34), Decl(typeGuardOfFormThisMember.ts, 6, 3)) + + file.isFile = true; +>file.isFile : Symbol(FileSystemObject.isFile, Decl(typeGuardOfFormThisMember.ts, 3, 34), Decl(typeGuardOfFormThisMember.ts, 6, 3)) +>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 27, 4)) +>isFile : Symbol(FileSystemObject.isFile, Decl(typeGuardOfFormThisMember.ts, 3, 34), Decl(typeGuardOfFormThisMember.ts, 6, 3)) let x = file.isFile; ->x : Symbol(x, Decl(typeGuardOfFormThisMember.ts, 30, 4)) ->file.isFile : Symbol(FileSystemObject.isFile, Decl(typeGuardOfFormThisMember.ts, 2, 32), Decl(typeGuardOfFormThisMember.ts, 5, 3)) ->file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4)) ->isFile : Symbol(FileSystemObject.isFile, Decl(typeGuardOfFormThisMember.ts, 2, 32), Decl(typeGuardOfFormThisMember.ts, 5, 3)) +>x : Symbol(x, Decl(typeGuardOfFormThisMember.ts, 31, 4)) +>file.isFile : Symbol(FileSystemObject.isFile, Decl(typeGuardOfFormThisMember.ts, 3, 34), Decl(typeGuardOfFormThisMember.ts, 6, 3)) +>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 27, 4)) +>isFile : Symbol(FileSystemObject.isFile, Decl(typeGuardOfFormThisMember.ts, 3, 34), Decl(typeGuardOfFormThisMember.ts, 6, 3)) if (file.isFile) { ->file.isFile : Symbol(FileSystemObject.isFile, Decl(typeGuardOfFormThisMember.ts, 2, 32), Decl(typeGuardOfFormThisMember.ts, 5, 3)) ->file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4)) ->isFile : Symbol(FileSystemObject.isFile, Decl(typeGuardOfFormThisMember.ts, 2, 32), Decl(typeGuardOfFormThisMember.ts, 5, 3)) +>file.isFile : Symbol(FileSystemObject.isFile, Decl(typeGuardOfFormThisMember.ts, 3, 34), Decl(typeGuardOfFormThisMember.ts, 6, 3)) +>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 27, 4)) +>isFile : Symbol(FileSystemObject.isFile, Decl(typeGuardOfFormThisMember.ts, 3, 34), Decl(typeGuardOfFormThisMember.ts, 6, 3)) file.content; ->file.content : Symbol(File.content, Decl(typeGuardOfFormThisMember.ts, 17, 27)) ->file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4)) ->content : Symbol(File.content, Decl(typeGuardOfFormThisMember.ts, 17, 27)) +>file.content : Symbol(File.content, Decl(typeGuardOfFormThisMember.ts, 18, 27)) +>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 27, 4)) +>content : Symbol(File.content, Decl(typeGuardOfFormThisMember.ts, 18, 27)) if (file.isNetworked) { ->file.isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 11, 3)) ->file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4)) ->isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 11, 3)) +>file.isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 12, 3)) +>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 27, 4)) +>isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 12, 3)) file.host; ->file.host : Symbol(Networked.host, Decl(typeGuardOfFormThisMember.ts, 22, 29)) ->file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4)) ->host : Symbol(Networked.host, Decl(typeGuardOfFormThisMember.ts, 22, 29)) +>file.host : Symbol(Networked.host, Decl(typeGuardOfFormThisMember.ts, 23, 29)) +>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 27, 4)) +>host : Symbol(Networked.host, Decl(typeGuardOfFormThisMember.ts, 23, 29)) file.content; ->file.content : Symbol(File.content, Decl(typeGuardOfFormThisMember.ts, 17, 27)) ->file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4)) ->content : Symbol(File.content, Decl(typeGuardOfFormThisMember.ts, 17, 27)) +>file.content : Symbol(File.content, Decl(typeGuardOfFormThisMember.ts, 18, 27)) +>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 27, 4)) +>content : Symbol(File.content, Decl(typeGuardOfFormThisMember.ts, 18, 27)) } } else if (file.isDirectory) { ->file.isDirectory : Symbol(FileSystemObject.isDirectory, Decl(typeGuardOfFormThisMember.ts, 8, 3)) ->file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4)) ->isDirectory : Symbol(FileSystemObject.isDirectory, Decl(typeGuardOfFormThisMember.ts, 8, 3)) +>file.isDirectory : Symbol(FileSystemObject.isDirectory, Decl(typeGuardOfFormThisMember.ts, 9, 3)) +>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 27, 4)) +>isDirectory : Symbol(FileSystemObject.isDirectory, Decl(typeGuardOfFormThisMember.ts, 9, 3)) file.children; ->file.children : Symbol(Directory.children, Decl(typeGuardOfFormThisMember.ts, 19, 50)) ->file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4)) ->children : Symbol(Directory.children, Decl(typeGuardOfFormThisMember.ts, 19, 50)) +>file.children : Symbol(Directory.children, Decl(typeGuardOfFormThisMember.ts, 20, 50)) +>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 27, 4)) +>children : Symbol(Directory.children, Decl(typeGuardOfFormThisMember.ts, 20, 50)) } else if (file.isNetworked) { ->file.isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 11, 3)) ->file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4)) ->isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 11, 3)) +>file.isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 12, 3)) +>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 27, 4)) +>isNetworked : Symbol(FileSystemObject.isNetworked, Decl(typeGuardOfFormThisMember.ts, 12, 3)) file.host; ->file.host : Symbol(Networked.host, Decl(typeGuardOfFormThisMember.ts, 22, 29)) ->file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 26, 4)) ->host : Symbol(Networked.host, Decl(typeGuardOfFormThisMember.ts, 22, 29)) +>file.host : Symbol(Networked.host, Decl(typeGuardOfFormThisMember.ts, 23, 29)) +>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 27, 4)) +>host : Symbol(Networked.host, Decl(typeGuardOfFormThisMember.ts, 23, 29)) } interface GenericLeadGuard extends GenericGuard { ->GenericLeadGuard : Symbol(GenericLeadGuard, Decl(typeGuardOfFormThisMember.ts, 43, 2)) ->T : Symbol(T, Decl(typeGuardOfFormThisMember.ts, 45, 28)) ->GenericGuard : Symbol(GenericGuard, Decl(typeGuardOfFormThisMember.ts, 51, 2)) ->T : Symbol(T, Decl(typeGuardOfFormThisMember.ts, 45, 28)) +>GenericLeadGuard : Symbol(GenericLeadGuard, Decl(typeGuardOfFormThisMember.ts, 44, 2)) +>T : Symbol(T, Decl(typeGuardOfFormThisMember.ts, 46, 28)) +>GenericGuard : Symbol(GenericGuard, Decl(typeGuardOfFormThisMember.ts, 52, 2)) +>T : Symbol(T, Decl(typeGuardOfFormThisMember.ts, 46, 28)) lead(): void; ->lead : Symbol(lead, Decl(typeGuardOfFormThisMember.ts, 45, 56)) +>lead : Symbol(lead, Decl(typeGuardOfFormThisMember.ts, 46, 56)) } interface GenericFollowerGuard extends GenericGuard { ->GenericFollowerGuard : Symbol(GenericFollowerGuard, Decl(typeGuardOfFormThisMember.ts, 47, 2)) ->T : Symbol(T, Decl(typeGuardOfFormThisMember.ts, 49, 32)) ->GenericGuard : Symbol(GenericGuard, Decl(typeGuardOfFormThisMember.ts, 51, 2)) ->T : Symbol(T, Decl(typeGuardOfFormThisMember.ts, 49, 32)) +>GenericFollowerGuard : Symbol(GenericFollowerGuard, Decl(typeGuardOfFormThisMember.ts, 48, 2)) +>T : Symbol(T, Decl(typeGuardOfFormThisMember.ts, 50, 32)) +>GenericGuard : Symbol(GenericGuard, Decl(typeGuardOfFormThisMember.ts, 52, 2)) +>T : Symbol(T, Decl(typeGuardOfFormThisMember.ts, 50, 32)) follow(): void; ->follow : Symbol(follow, Decl(typeGuardOfFormThisMember.ts, 49, 60)) +>follow : Symbol(follow, Decl(typeGuardOfFormThisMember.ts, 50, 60)) } interface GenericGuard { ->GenericGuard : Symbol(GenericGuard, Decl(typeGuardOfFormThisMember.ts, 51, 2)) ->T : Symbol(T, Decl(typeGuardOfFormThisMember.ts, 53, 24)) +>GenericGuard : Symbol(GenericGuard, Decl(typeGuardOfFormThisMember.ts, 52, 2)) +>T : Symbol(T, Decl(typeGuardOfFormThisMember.ts, 54, 24)) target: T; ->target : Symbol(target, Decl(typeGuardOfFormThisMember.ts, 53, 28)) ->T : Symbol(T, Decl(typeGuardOfFormThisMember.ts, 53, 24)) +>target : Symbol(target, Decl(typeGuardOfFormThisMember.ts, 54, 28)) +>T : Symbol(T, Decl(typeGuardOfFormThisMember.ts, 54, 24)) isLeader: this is (GenericLeadGuard); ->isLeader : Symbol(isLeader, Decl(typeGuardOfFormThisMember.ts, 54, 12)) ->GenericLeadGuard : Symbol(GenericLeadGuard, Decl(typeGuardOfFormThisMember.ts, 43, 2)) ->T : Symbol(T, Decl(typeGuardOfFormThisMember.ts, 53, 24)) +>isLeader : Symbol(isLeader, Decl(typeGuardOfFormThisMember.ts, 55, 12)) +>GenericLeadGuard : Symbol(GenericLeadGuard, Decl(typeGuardOfFormThisMember.ts, 44, 2)) +>T : Symbol(T, Decl(typeGuardOfFormThisMember.ts, 54, 24)) isFollower: this is GenericFollowerGuard; ->isFollower : Symbol(isFollower, Decl(typeGuardOfFormThisMember.ts, 55, 42)) ->GenericFollowerGuard : Symbol(GenericFollowerGuard, Decl(typeGuardOfFormThisMember.ts, 47, 2)) ->T : Symbol(T, Decl(typeGuardOfFormThisMember.ts, 53, 24)) +>isFollower : Symbol(isFollower, Decl(typeGuardOfFormThisMember.ts, 56, 42)) +>GenericFollowerGuard : Symbol(GenericFollowerGuard, Decl(typeGuardOfFormThisMember.ts, 48, 2)) +>T : Symbol(T, Decl(typeGuardOfFormThisMember.ts, 54, 24)) } let guard: GenericGuard; ->guard : Symbol(guard, Decl(typeGuardOfFormThisMember.ts, 59, 4)) ->GenericGuard : Symbol(GenericGuard, Decl(typeGuardOfFormThisMember.ts, 51, 2)) ->File : Symbol(File, Decl(typeGuardOfFormThisMember.ts, 14, 2)) +>guard : Symbol(guard, Decl(typeGuardOfFormThisMember.ts, 60, 4)) +>GenericGuard : Symbol(GenericGuard, Decl(typeGuardOfFormThisMember.ts, 52, 2)) +>File : Symbol(File, Decl(typeGuardOfFormThisMember.ts, 15, 2)) if (guard.isLeader) { ->guard.isLeader : Symbol(GenericGuard.isLeader, Decl(typeGuardOfFormThisMember.ts, 54, 12)) ->guard : Symbol(guard, Decl(typeGuardOfFormThisMember.ts, 59, 4)) ->isLeader : Symbol(GenericGuard.isLeader, Decl(typeGuardOfFormThisMember.ts, 54, 12)) +>guard.isLeader : Symbol(GenericGuard.isLeader, Decl(typeGuardOfFormThisMember.ts, 55, 12)) +>guard : Symbol(guard, Decl(typeGuardOfFormThisMember.ts, 60, 4)) +>isLeader : Symbol(GenericGuard.isLeader, Decl(typeGuardOfFormThisMember.ts, 55, 12)) guard.lead(); ->guard.lead : Symbol(GenericLeadGuard.lead, Decl(typeGuardOfFormThisMember.ts, 45, 56)) ->guard : Symbol(guard, Decl(typeGuardOfFormThisMember.ts, 59, 4)) ->lead : Symbol(GenericLeadGuard.lead, Decl(typeGuardOfFormThisMember.ts, 45, 56)) +>guard.lead : Symbol(GenericLeadGuard.lead, Decl(typeGuardOfFormThisMember.ts, 46, 56)) +>guard : Symbol(guard, Decl(typeGuardOfFormThisMember.ts, 60, 4)) +>lead : Symbol(GenericLeadGuard.lead, Decl(typeGuardOfFormThisMember.ts, 46, 56)) } else if (guard.isFollower) { ->guard.isFollower : Symbol(GenericGuard.isFollower, Decl(typeGuardOfFormThisMember.ts, 55, 42)) ->guard : Symbol(guard, Decl(typeGuardOfFormThisMember.ts, 59, 4)) ->isFollower : Symbol(GenericGuard.isFollower, Decl(typeGuardOfFormThisMember.ts, 55, 42)) +>guard.isFollower : Symbol(GenericGuard.isFollower, Decl(typeGuardOfFormThisMember.ts, 56, 42)) +>guard : Symbol(guard, Decl(typeGuardOfFormThisMember.ts, 60, 4)) +>isFollower : Symbol(GenericGuard.isFollower, Decl(typeGuardOfFormThisMember.ts, 56, 42)) guard.follow(); ->guard.follow : Symbol(GenericFollowerGuard.follow, Decl(typeGuardOfFormThisMember.ts, 49, 60)) ->guard : Symbol(guard, Decl(typeGuardOfFormThisMember.ts, 59, 4)) ->follow : Symbol(GenericFollowerGuard.follow, Decl(typeGuardOfFormThisMember.ts, 49, 60)) +>guard.follow : Symbol(GenericFollowerGuard.follow, Decl(typeGuardOfFormThisMember.ts, 50, 60)) +>guard : Symbol(guard, Decl(typeGuardOfFormThisMember.ts, 60, 4)) +>follow : Symbol(GenericFollowerGuard.follow, Decl(typeGuardOfFormThisMember.ts, 50, 60)) } interface SpecificGuard { ->SpecificGuard : Symbol(SpecificGuard, Decl(typeGuardOfFormThisMember.ts, 65, 2)) +>SpecificGuard : Symbol(SpecificGuard, Decl(typeGuardOfFormThisMember.ts, 66, 2)) isMoreSpecific: this is MoreSpecificGuard; ->isMoreSpecific : Symbol(isMoreSpecific, Decl(typeGuardOfFormThisMember.ts, 67, 26)) ->MoreSpecificGuard : Symbol(MoreSpecificGuard, Decl(typeGuardOfFormThisMember.ts, 69, 2)) +>isMoreSpecific : Symbol(isMoreSpecific, Decl(typeGuardOfFormThisMember.ts, 68, 26)) +>MoreSpecificGuard : Symbol(MoreSpecificGuard, Decl(typeGuardOfFormThisMember.ts, 70, 2)) } interface MoreSpecificGuard extends SpecificGuard { ->MoreSpecificGuard : Symbol(MoreSpecificGuard, Decl(typeGuardOfFormThisMember.ts, 69, 2)) ->SpecificGuard : Symbol(SpecificGuard, Decl(typeGuardOfFormThisMember.ts, 65, 2)) +>MoreSpecificGuard : Symbol(MoreSpecificGuard, Decl(typeGuardOfFormThisMember.ts, 70, 2)) +>SpecificGuard : Symbol(SpecificGuard, Decl(typeGuardOfFormThisMember.ts, 66, 2)) do(): void; ->do : Symbol(do, Decl(typeGuardOfFormThisMember.ts, 71, 52)) +>do : Symbol(do, Decl(typeGuardOfFormThisMember.ts, 72, 52)) } let general: SpecificGuard; ->general : Symbol(general, Decl(typeGuardOfFormThisMember.ts, 75, 4)) ->SpecificGuard : Symbol(SpecificGuard, Decl(typeGuardOfFormThisMember.ts, 65, 2)) +>general : Symbol(general, Decl(typeGuardOfFormThisMember.ts, 76, 4)) +>SpecificGuard : Symbol(SpecificGuard, Decl(typeGuardOfFormThisMember.ts, 66, 2)) if (general.isMoreSpecific) { ->general.isMoreSpecific : Symbol(SpecificGuard.isMoreSpecific, Decl(typeGuardOfFormThisMember.ts, 67, 26)) ->general : Symbol(general, Decl(typeGuardOfFormThisMember.ts, 75, 4)) ->isMoreSpecific : Symbol(SpecificGuard.isMoreSpecific, Decl(typeGuardOfFormThisMember.ts, 67, 26)) +>general.isMoreSpecific : Symbol(SpecificGuard.isMoreSpecific, Decl(typeGuardOfFormThisMember.ts, 68, 26)) +>general : Symbol(general, Decl(typeGuardOfFormThisMember.ts, 76, 4)) +>isMoreSpecific : Symbol(SpecificGuard.isMoreSpecific, Decl(typeGuardOfFormThisMember.ts, 68, 26)) general.do(); ->general.do : Symbol(MoreSpecificGuard.do, Decl(typeGuardOfFormThisMember.ts, 71, 52)) ->general : Symbol(general, Decl(typeGuardOfFormThisMember.ts, 75, 4)) ->do : Symbol(MoreSpecificGuard.do, Decl(typeGuardOfFormThisMember.ts, 71, 52)) +>general.do : Symbol(MoreSpecificGuard.do, Decl(typeGuardOfFormThisMember.ts, 72, 52)) +>general : Symbol(general, Decl(typeGuardOfFormThisMember.ts, 76, 4)) +>do : Symbol(MoreSpecificGuard.do, Decl(typeGuardOfFormThisMember.ts, 72, 52)) } } diff --git a/tests/baselines/reference/typeGuardOfFormThisMember.types b/tests/baselines/reference/typeGuardOfFormThisMember.types index 3ecb174653b0f..68343947fb49a 100644 --- a/tests/baselines/reference/typeGuardOfFormThisMember.types +++ b/tests/baselines/reference/typeGuardOfFormThisMember.types @@ -6,6 +6,10 @@ namespace Test { export class FileSystemObject { >FileSystemObject : FileSystemObject + isFSO: this is FileSystemObject; +>isFSO : this is FileSystemObject +>FileSystemObject : FileSystemObject + get isFile(): this is File { >isFile : this is File >File : File @@ -79,23 +83,21 @@ namespace Test { >isNetworked : this is Networked & FileSystemObject >false : boolean - file.isNetworked = file.isDirectory; ->file.isNetworked = file.isDirectory : this is Directory ->file.isNetworked : this is Networked & FileSystemObject + file.isFSO = file.isFile; +>file.isFSO = file.isFile : this is File +>file.isFSO : this is FileSystemObject >file : FileSystemObject ->isNetworked : this is Networked & FileSystemObject ->file.isDirectory : this is Directory +>isFSO : this is FileSystemObject +>file.isFile : this is File >file : FileSystemObject ->isDirectory : this is Directory +>isFile : this is File - file.isFile = file.isNetworked; ->file.isFile = file.isNetworked : this is Networked & FileSystemObject + file.isFile = true; +>file.isFile = true : boolean >file.isFile : this is File >file : FileSystemObject >isFile : this is File ->file.isNetworked : this is Networked & FileSystemObject ->file : FileSystemObject ->isNetworked : this is Networked & FileSystemObject +>true : boolean let x = file.isFile; >x : boolean diff --git a/tests/baselines/reference/typeGuardOfFormThisMemberErrors.errors.txt b/tests/baselines/reference/typeGuardOfFormThisMemberErrors.errors.txt new file mode 100644 index 0000000000000..754cbffe6e61a --- /dev/null +++ b/tests/baselines/reference/typeGuardOfFormThisMemberErrors.errors.txt @@ -0,0 +1,51 @@ +tests/cases/conformance/expressions/typeGuards/typeGuardOfFormThisMemberErrors.ts(29,2): error TS1226: Type predicate 'this is File' is not assignable to 'this is Networked & FileSystemObject'. + Type 'File' is not assignable to type 'Networked & FileSystemObject'. + Type 'File' is not assignable to type 'Networked'. + Property 'host' is missing in type 'File'. +tests/cases/conformance/expressions/typeGuards/typeGuardOfFormThisMemberErrors.ts(31,2): error TS1226: Type predicate 'this is FileSystemObject' is not assignable to 'this is File'. + Type 'FileSystemObject' is not assignable to type 'File'. + Property 'content' is missing in type 'FileSystemObject'. + + +==== tests/cases/conformance/expressions/typeGuards/typeGuardOfFormThisMemberErrors.ts (2 errors) ==== + // There's a 'File' class in the stdlib, wrap with a namespace to avoid collision + namespace Test { + export class FileSystemObject { + isFSO: this is FileSystemObject; + get isFile(): this is File { + return this instanceof File; + } + set isFile(param) { + // noop + } + get isDirectory(): this is Directory { + return this instanceof Directory; + } + isNetworked: this is (Networked & this); + constructor(public path: string) {} + } + + export class File extends FileSystemObject { + constructor(path: string, public content: string) { super(path); } + } + export class Directory extends FileSystemObject { + children: FileSystemObject[]; + } + export interface Networked { + host: string; + } + + let file: FileSystemObject = new File("foo/bar.txt", "foo"); + file.isNetworked = file.isFile; + ~~~~~~~~~~~~~~~~ +!!! error TS1226: Type predicate 'this is File' is not assignable to 'this is Networked & FileSystemObject'. +!!! error TS1226: Type 'File' is not assignable to type 'Networked & FileSystemObject'. +!!! error TS1226: Type 'File' is not assignable to type 'Networked'. +!!! error TS1226: Property 'host' is missing in type 'File'. + file.isFSO = file.isNetworked; + file.isFile = file.isFSO; + ~~~~~~~~~~~ +!!! error TS1226: Type predicate 'this is FileSystemObject' is not assignable to 'this is File'. +!!! error TS1226: Type 'FileSystemObject' is not assignable to type 'File'. +!!! error TS1226: Property 'content' is missing in type 'FileSystemObject'. + } \ No newline at end of file diff --git a/tests/baselines/reference/typeGuardOfFormThisMemberErrors.js b/tests/baselines/reference/typeGuardOfFormThisMemberErrors.js new file mode 100644 index 0000000000000..e6b2f55662110 --- /dev/null +++ b/tests/baselines/reference/typeGuardOfFormThisMemberErrors.js @@ -0,0 +1,112 @@ +//// [typeGuardOfFormThisMemberErrors.ts] +// There's a 'File' class in the stdlib, wrap with a namespace to avoid collision +namespace Test { + export class FileSystemObject { + isFSO: this is FileSystemObject; + get isFile(): this is File { + return this instanceof File; + } + set isFile(param) { + // noop + } + get isDirectory(): this is Directory { + return this instanceof Directory; + } + isNetworked: this is (Networked & this); + constructor(public path: string) {} + } + + export class File extends FileSystemObject { + constructor(path: string, public content: string) { super(path); } + } + export class Directory extends FileSystemObject { + children: FileSystemObject[]; + } + export interface Networked { + host: string; + } + + let file: FileSystemObject = new File("foo/bar.txt", "foo"); + file.isNetworked = file.isFile; + file.isFSO = file.isNetworked; + file.isFile = file.isFSO; +} + +//// [typeGuardOfFormThisMemberErrors.js] +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +// There's a 'File' class in the stdlib, wrap with a namespace to avoid collision +var Test; +(function (Test) { + var FileSystemObject = (function () { + function FileSystemObject(path) { + this.path = path; + } + Object.defineProperty(FileSystemObject.prototype, "isFile", { + get: function () { + return this instanceof File; + }, + set: function (param) { + // noop + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(FileSystemObject.prototype, "isDirectory", { + get: function () { + return this instanceof Directory; + }, + enumerable: true, + configurable: true + }); + return FileSystemObject; + })(); + Test.FileSystemObject = FileSystemObject; + var File = (function (_super) { + __extends(File, _super); + function File(path, content) { + _super.call(this, path); + this.content = content; + } + return File; + })(FileSystemObject); + Test.File = File; + var Directory = (function (_super) { + __extends(Directory, _super); + function Directory() { + _super.apply(this, arguments); + } + return Directory; + })(FileSystemObject); + Test.Directory = Directory; + var file = new File("foo/bar.txt", "foo"); + file.isNetworked = file.isFile; + file.isFSO = file.isNetworked; + file.isFile = file.isFSO; +})(Test || (Test = {})); + + +//// [typeGuardOfFormThisMemberErrors.d.ts] +declare namespace Test { + class FileSystemObject { + path: string; + isFSO: this is FileSystemObject; + isFile: this is File; + isDirectory: this is Directory; + isNetworked: this is (Networked & this); + constructor(path: string); + } + class File extends FileSystemObject { + content: string; + constructor(path: string, content: string); + } + class Directory extends FileSystemObject { + children: FileSystemObject[]; + } + interface Networked { + host: string; + } +} diff --git a/tests/baselines/reference/unionAndIntersectionInference1.types b/tests/baselines/reference/unionAndIntersectionInference1.types index 5d23688f0b7c3..3de2514a2fae6 100644 --- a/tests/baselines/reference/unionAndIntersectionInference1.types +++ b/tests/baselines/reference/unionAndIntersectionInference1.types @@ -110,7 +110,7 @@ function foo1(value: void|a): void { >a : a if (isVoid(value)) { ->isVoid(value) : boolean +>isVoid(value) : value is void >isVoid : (value: void | a) => value is void >value : void | a @@ -130,7 +130,7 @@ function baz1(value: void|a): void { >a : a if (isNonVoid(value)) { ->isNonVoid(value) : boolean +>isNonVoid(value) : value is {} >isNonVoid : (value: void | a) => value is a >value : void | a diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormThisMember.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormThisMember.ts index 707f0f6eea575..decf6d99353eb 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormThisMember.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormThisMember.ts @@ -3,6 +3,7 @@ // There's a 'File' class in the stdlib, wrap with a namespace to avoid collision namespace Test { export class FileSystemObject { + isFSO: this is FileSystemObject; get isFile(): this is File { return this instanceof File; } @@ -28,8 +29,8 @@ namespace Test { let file: FileSystemObject = new File("foo/bar.txt", "foo"); file.isNetworked = false; - file.isNetworked = file.isDirectory; - file.isFile = file.isNetworked; + file.isFSO = file.isFile; + file.isFile = true; let x = file.isFile; if (file.isFile) { file.content; diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormThisMemberErrors.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormThisMemberErrors.ts new file mode 100644 index 0000000000000..d629a2a1cddca --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormThisMemberErrors.ts @@ -0,0 +1,34 @@ +// @target: es5 +// @declaration: true +// There's a 'File' class in the stdlib, wrap with a namespace to avoid collision +namespace Test { + export class FileSystemObject { + isFSO: this is FileSystemObject; + get isFile(): this is File { + return this instanceof File; + } + set isFile(param) { + // noop + } + get isDirectory(): this is Directory { + return this instanceof Directory; + } + isNetworked: this is (Networked & this); + constructor(public path: string) {} + } + + export class File extends FileSystemObject { + constructor(path: string, public content: string) { super(path); } + } + export class Directory extends FileSystemObject { + children: FileSystemObject[]; + } + export interface Networked { + host: string; + } + + let file: FileSystemObject = new File("foo/bar.txt", "foo"); + file.isNetworked = file.isFile; + file.isFSO = file.isNetworked; + file.isFile = file.isFSO; +} \ No newline at end of file From 0228ec3ced6e47bd9775efcc75f718ea81acecd8 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 8 Dec 2015 17:45:11 -0800 Subject: [PATCH 10/15] preserve some old behavior at @DanielRosenwassers request --- src/compiler/checker.ts | 10 ++++++++++ .../reference/stringLiteralTypesAsTags01.errors.txt | 5 ++++- .../reference/typeGuardFunctionErrors.errors.txt | 7 ++++++- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e496a0b07bcb9..3296824123a35 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5633,6 +5633,16 @@ namespace ts { if (targetReturnType === voidType) return result; const sourceReturnType = getReturnTypeOfSignature(source); + // The follow block preserves old behavior forbidding boolean returning functions from being assignable to type guard returning functions + if (targetReturnType.flags & TypeFlags.PredicateType && (targetReturnType as PredicateType).predicate.kind === TypePredicateKind.Identifier) { + if (!(sourceReturnType.flags & TypeFlags.PredicateType)) { + if (reportErrors) { + reportError(Diagnostics.Signature_0_must_have_a_type_predicate, signatureToString(source)); + } + return Ternary.False; + } + } + return result & isRelatedTo(sourceReturnType, targetReturnType, reportErrors); } diff --git a/tests/baselines/reference/stringLiteralTypesAsTags01.errors.txt b/tests/baselines/reference/stringLiteralTypesAsTags01.errors.txt index de3b7be2612a0..90c8c3efdf745 100644 --- a/tests/baselines/reference/stringLiteralTypesAsTags01.errors.txt +++ b/tests/baselines/reference/stringLiteralTypesAsTags01.errors.txt @@ -1,7 +1,8 @@ +tests/cases/conformance/types/stringLiteral/stringLiteralTypesAsTags01.ts(20,10): error TS2394: Overload signature is not compatible with function implementation. tests/cases/conformance/types/stringLiteral/stringLiteralTypesAsTags01.ts(22,21): error TS2304: Cannot find name 'is'. -==== tests/cases/conformance/types/stringLiteral/stringLiteralTypesAsTags01.ts (1 errors) ==== +==== tests/cases/conformance/types/stringLiteral/stringLiteralTypesAsTags01.ts (2 errors) ==== type Kind = "A" | "B" @@ -22,6 +23,8 @@ tests/cases/conformance/types/stringLiteral/stringLiteralTypesAsTags01.ts(22,21) function hasKind(entity: Entity, kind: "A"): entity is A; function hasKind(entity: Entity, kind: "B"): entity is B; function hasKind(entity: Entity, kind: Kind): entity is Entity; + ~~~~~~~ +!!! error TS2394: Overload signature is not compatible with function implementation. function hasKind(entity: Entity, kind: Kind): boolean { return kind === is; ~~ diff --git a/tests/baselines/reference/typeGuardFunctionErrors.errors.txt b/tests/baselines/reference/typeGuardFunctionErrors.errors.txt index 445738e97839a..e1fdf303870ec 100644 --- a/tests/baselines/reference/typeGuardFunctionErrors.errors.txt +++ b/tests/baselines/reference/typeGuardFunctionErrors.errors.txt @@ -17,6 +17,8 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(75,46) Type predicate 'p1 is C' is not assignable to 'p1 is B'. Type 'C' is not assignable to type 'B'. Property 'propB' is missing in type 'C'. +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(79,1): error TS2322: Type '(p1: any, p2: any) => boolean' is not assignable to type '(p1: any, p2: any) => p1 is A'. + Signature '(p1: any, p2: any): boolean' must have a type predicate. tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(85,1): error TS2322: Type '(p1: any, p2: any) => p2 is A' is not assignable to type '(p1: any, p2: any) => p1 is A'. Type predicate 'p2 is A' is not assignable to 'p1 is A'. Parameter 'p2' is not in the same position as parameter 'p1'. @@ -39,7 +41,7 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(133,34 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 (32 errors) ==== +==== tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts (33 errors) ==== class A { propA: number; @@ -153,6 +155,9 @@ tests/cases/conformance/expressions/typeGuards/typeGuardFunctionErrors.ts(137,39 // Boolean not assignable to type guard var assign1: (p1, p2) => p1 is A; assign1 = function(p1, p2): boolean { + ~~~~~~~ +!!! error TS2322: Type '(p1: any, p2: any) => boolean' is not assignable to type '(p1: any, p2: any) => p1 is A'. +!!! error TS2322: Signature '(p1: any, p2: any): boolean' must have a type predicate. return true; }; From dc765b8b5af963f7e7fd7eeb7a4c85cdc375a8f6 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 8 Dec 2015 17:55:05 -0800 Subject: [PATCH 11/15] accept new baseline --- tests/baselines/reference/unionAndIntersectionInference1.types | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/baselines/reference/unionAndIntersectionInference1.types b/tests/baselines/reference/unionAndIntersectionInference1.types index 3de2514a2fae6..073a677b659e7 100644 --- a/tests/baselines/reference/unionAndIntersectionInference1.types +++ b/tests/baselines/reference/unionAndIntersectionInference1.types @@ -130,7 +130,7 @@ function baz1(value: void|a): void { >a : a if (isNonVoid(value)) { ->isNonVoid(value) : value is {} +>isNonVoid(value) : value is a >isNonVoid : (value: void | a) => value is a >value : void | a From 028484664d019153ed23791ffa88014890986a54 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 9 Dec 2015 15:53:39 -0800 Subject: [PATCH 12/15] most pr feedback --- src/compiler/checker.ts | 29 +++++++++++++--------------- src/compiler/diagnosticMessages.json | 4 ++-- src/compiler/types.ts | 2 +- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 45f4e43e42401..35f92de8fbea2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5103,7 +5103,7 @@ namespace ts { const targetPredicate = target as PredicateType; if (sourcePredicate.predicate.kind !== targetPredicate.predicate.kind) { if (reportErrors) { - reportError(Diagnostics.A_this_based_type_guard_is_not_assignable_to_a_parameter_based_type_guard); + reportError(Diagnostics.A_this_based_type_guard_is_not_compatible_with_a_parameter_based_type_guard); reportError(Diagnostics.Type_predicate_0_is_not_assignable_to_1, typeToString(source), typeToString(target)); } return Ternary.False; @@ -6506,10 +6506,7 @@ namespace ts { function isAssignedInBinaryExpression(node: BinaryExpression) { if (node.operatorToken.kind >= SyntaxKind.FirstAssignment && node.operatorToken.kind <= SyntaxKind.LastAssignment) { - let n = node.left; - while (n.kind === SyntaxKind.ParenthesizedExpression) { - n = (n).expression; - } + const n = skipParenthesizedNodes(node.left); if (n.kind === SyntaxKind.Identifier && getResolvedSymbol(n) === symbol) { return true; } @@ -6844,13 +6841,6 @@ namespace ts { return type; } - function skipParenthesizedNodes(expression: Expression): Expression { - while (expression.kind === SyntaxKind.ParenthesizedExpression) { - expression = (expression as ParenthesizedExpression).expression; - } - return expression; - } - function getSymbolAtTypePredicatePosition(expr: Expression): Symbol { expr = skipParenthesizedNodes(expr); switch (expr.kind) { @@ -6897,6 +6887,13 @@ namespace ts { } } + function skipParenthesizedNodes(expression: Expression): Expression { + while (expression.kind === SyntaxKind.ParenthesizedExpression) { + expression = (expression as ParenthesizedExpression).expression; + } + return expression; + } + function checkIdentifier(node: Identifier): Type { const symbol = getResolvedSymbol(node); @@ -11085,7 +11082,7 @@ namespace ts { return -1; } - function isInLegalTypePredicatePosition(node: Node): boolean { + function isInLegalParameterTypePredicatePosition(node: Node): boolean { switch (node.parent.kind) { case SyntaxKind.ArrowFunction: case SyntaxKind.CallSignature: @@ -11100,7 +11097,7 @@ namespace ts { } function isInLegalThisTypePredicatePosition(node: Node): boolean { - if (isInLegalTypePredicatePosition(node)) { + if (isInLegalParameterTypePredicatePosition(node)) { return true; } switch (node.parent.kind) { @@ -14332,12 +14329,12 @@ namespace ts { } function checkTypePredicate(node: TypePredicateNode) { - if (node.parameterName.kind === SyntaxKind.Identifier && !isInLegalTypePredicatePosition(node)) { + if (node.parameterName.kind === SyntaxKind.Identifier && !isInLegalParameterTypePredicatePosition(node)) { error(node, Diagnostics.A_type_predicate_is_only_allowed_in_return_type_position_for_functions_and_methods); } else if (node.parameterName.kind === SyntaxKind.ThisType) { if (!isInLegalThisTypePredicatePosition(node)) { - error(node, Diagnostics.A_this_based_type_predicate_is_only_allowed_in_class_or_interface_members_get_accessors_or_return_type_positions_for_functions_and_methods); + 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(node.parameterName as ThisTypeNode); diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 959363f148abb..4698355133815 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1647,11 +1647,11 @@ "category": "Error", "code": 2517 }, - "A 'this'-based type guard is not assignable to a parameter-based type guard": { + "A 'this'-based type guard is not compatible with a parameter-based type guard.": { "category": "Error", "code": 2518 }, - "A 'this'-based type predicate is only allowed in class or interface members, get accessors, or return type positions for functions and methods": { + "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 }, diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 7b79584619012..85ae020753f83 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2110,7 +2110,7 @@ namespace ts { ESSymbol = 0x01000000, // Type of symbol primitive introduced in ES6 ThisType = 0x02000000, // This type ObjectLiteralPatternWithComputedProperties = 0x04000000, // Object literal type implied by binding pattern has computed properties - PredicateType = 0x08000000, + PredicateType = 0x08000000, // Predicate types are also Boolean types, but should not be considered Intrinsics - there's no way to capture this with flags /* @internal */ Intrinsic = Any | String | Number | Boolean | ESSymbol | Void | Undefined | Null, From 82386564d497772632c4bbd18e25048e4cb6368c Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 9 Dec 2015 15:58:57 -0800 Subject: [PATCH 13/15] destructuring applied --- src/compiler/binder.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 837260e5e1ba4..2682a5938d106 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1277,13 +1277,14 @@ namespace ts { } function checkTypePredicate(node: TypePredicateNode) { - if (node.parameterName && node.parameterName.kind === SyntaxKind.Identifier) { - checkStrictModeIdentifier(node.parameterName as Identifier); + const { parameterName, type } = node; + if (parameterName && parameterName.kind === SyntaxKind.Identifier) { + checkStrictModeIdentifier(parameterName as Identifier); } - if (node.parameterName && node.parameterName.kind === SyntaxKind.ThisType) { + if (parameterName && parameterName.kind === SyntaxKind.ThisType) { seenThisKeyword = true; } - bind(node.type); + bind(type); } function bindSourceFileIfExternalModule() { From bc01b16057f3f7f88859a081922206d8821fa213 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 9 Dec 2015 16:06:21 -0800 Subject: [PATCH 14/15] reorder if --- src/compiler/checker.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 35f92de8fbea2..0ee73756e572c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6038,12 +6038,12 @@ namespace ts { function getWidenedType(type: Type): Type { if (type.flags & TypeFlags.RequiresWidening) { - if (type.flags & TypeFlags.PredicateType) { - return booleanType; - } if (type.flags & (TypeFlags.Undefined | TypeFlags.Null)) { return anyType; } + if (type.flags & TypeFlags.PredicateType) { + return booleanType; + } if (type.flags & TypeFlags.ObjectLiteral) { return getWidenedTypeOfObjectLiteral(type); } @@ -14329,15 +14329,16 @@ namespace ts { } function checkTypePredicate(node: TypePredicateNode) { - if (node.parameterName.kind === SyntaxKind.Identifier && !isInLegalParameterTypePredicatePosition(node)) { + const { parameterName } = node; + if (parameterName.kind === SyntaxKind.Identifier && !isInLegalParameterTypePredicatePosition(node)) { error(node, Diagnostics.A_type_predicate_is_only_allowed_in_return_type_position_for_functions_and_methods); } - else if (node.parameterName.kind === SyntaxKind.ThisType) { + else 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(node.parameterName as ThisTypeNode); + getTypeFromThisTypeNode(parameterName as ThisTypeNode); } } } From 8e586943a1b60075e51fc482af2d9be278eab08b Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 9 Dec 2015 16:43:45 -0800 Subject: [PATCH 15/15] accept baselines --- .../reference/typeGuardFunctionOfFormThis.js | 18 +++++++++--------- .../typeGuardFunctionOfFormThisErrors.js | 6 +++--- .../reference/typeGuardOfFormThisMember.js | 6 +++--- .../typeGuardOfFormThisMemberErrors.js | 6 +++--- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/baselines/reference/typeGuardFunctionOfFormThis.js b/tests/baselines/reference/typeGuardFunctionOfFormThis.js index c1c670e8d7d2c..0d1dceccede6a 100644 --- a/tests/baselines/reference/typeGuardFunctionOfFormThis.js +++ b/tests/baselines/reference/typeGuardFunctionOfFormThis.js @@ -157,7 +157,7 @@ var RoyalGuard = (function () { return this instanceof FollowerGuard; }; return RoyalGuard; -})(); +}()); var LeadGuard = (function (_super) { __extends(LeadGuard, _super); function LeadGuard() { @@ -166,7 +166,7 @@ var LeadGuard = (function (_super) { LeadGuard.prototype.lead = function () { }; ; return LeadGuard; -})(RoyalGuard); +}(RoyalGuard)); var FollowerGuard = (function (_super) { __extends(FollowerGuard, _super); function FollowerGuard() { @@ -175,7 +175,7 @@ var FollowerGuard = (function (_super) { FollowerGuard.prototype.follow = function () { }; ; return FollowerGuard; -})(RoyalGuard); +}(RoyalGuard)); var a = new FollowerGuard(); if (a.isLeader()) { a.lead(); @@ -220,7 +220,7 @@ var ArrowGuard = (function () { }; } return ArrowGuard; -})(); +}()); var ArrowElite = (function (_super) { __extends(ArrowElite, _super); function ArrowElite() { @@ -228,7 +228,7 @@ var ArrowElite = (function (_super) { } ArrowElite.prototype.defend = function () { }; return ArrowElite; -})(ArrowGuard); +}(ArrowGuard)); var ArrowMedic = (function (_super) { __extends(ArrowMedic, _super); function ArrowMedic() { @@ -236,7 +236,7 @@ var ArrowMedic = (function (_super) { } ArrowMedic.prototype.heal = function () { }; return ArrowMedic; -})(ArrowGuard); +}(ArrowGuard)); var guard = new ArrowGuard(); if (guard.isElite()) { guard.defend(); @@ -262,7 +262,7 @@ var MimicGuard = (function () { MimicGuard.prototype.isFollower = function () { return this instanceof MimicFollower; }; ; return MimicGuard; -})(); +}()); var MimicLeader = (function (_super) { __extends(MimicLeader, _super); function MimicLeader() { @@ -270,7 +270,7 @@ var MimicLeader = (function (_super) { } MimicLeader.prototype.lead = function () { }; return MimicLeader; -})(MimicGuard); +}(MimicGuard)); var MimicFollower = (function (_super) { __extends(MimicFollower, _super); function MimicFollower() { @@ -278,7 +278,7 @@ var MimicFollower = (function (_super) { } MimicFollower.prototype.follow = function () { }; return MimicFollower; -})(MimicGuard); +}(MimicGuard)); var mimic = new MimicGuard(); a.isLeader = mimic.isLeader; a.isFollower = mimic.isFollower; diff --git a/tests/baselines/reference/typeGuardFunctionOfFormThisErrors.js b/tests/baselines/reference/typeGuardFunctionOfFormThisErrors.js index 0495e3cd77380..73622bf60cc18 100644 --- a/tests/baselines/reference/typeGuardFunctionOfFormThisErrors.js +++ b/tests/baselines/reference/typeGuardFunctionOfFormThisErrors.js @@ -75,7 +75,7 @@ var RoyalGuard = (function () { return this instanceof FollowerGuard; }; return RoyalGuard; -})(); +}()); var LeadGuard = (function (_super) { __extends(LeadGuard, _super); function LeadGuard() { @@ -84,7 +84,7 @@ var LeadGuard = (function (_super) { LeadGuard.prototype.lead = function () { }; ; return LeadGuard; -})(RoyalGuard); +}(RoyalGuard)); var FollowerGuard = (function (_super) { __extends(FollowerGuard, _super); function FollowerGuard() { @@ -93,7 +93,7 @@ var FollowerGuard = (function (_super) { FollowerGuard.prototype.follow = function () { }; ; return FollowerGuard; -})(RoyalGuard); +}(RoyalGuard)); var a = new FollowerGuard(); var b = new LeadGuard(); // Mismatched guards shouldn't be assignable diff --git a/tests/baselines/reference/typeGuardOfFormThisMember.js b/tests/baselines/reference/typeGuardOfFormThisMember.js index 8cb5b89aa90c5..2f983d2cdf841 100644 --- a/tests/baselines/reference/typeGuardOfFormThisMember.js +++ b/tests/baselines/reference/typeGuardOfFormThisMember.js @@ -113,7 +113,7 @@ var Test; configurable: true }); return FileSystemObject; - })(); + }()); Test.FileSystemObject = FileSystemObject; var File = (function (_super) { __extends(File, _super); @@ -122,7 +122,7 @@ var Test; this.content = content; } return File; - })(FileSystemObject); + }(FileSystemObject)); Test.File = File; var Directory = (function (_super) { __extends(Directory, _super); @@ -130,7 +130,7 @@ var Test; _super.apply(this, arguments); } return Directory; - })(FileSystemObject); + }(FileSystemObject)); Test.Directory = Directory; var file = new File("foo/bar.txt", "foo"); file.isNetworked = false; diff --git a/tests/baselines/reference/typeGuardOfFormThisMemberErrors.js b/tests/baselines/reference/typeGuardOfFormThisMemberErrors.js index e6b2f55662110..6f681ab09cffc 100644 --- a/tests/baselines/reference/typeGuardOfFormThisMemberErrors.js +++ b/tests/baselines/reference/typeGuardOfFormThisMemberErrors.js @@ -63,7 +63,7 @@ var Test; configurable: true }); return FileSystemObject; - })(); + }()); Test.FileSystemObject = FileSystemObject; var File = (function (_super) { __extends(File, _super); @@ -72,7 +72,7 @@ var Test; this.content = content; } return File; - })(FileSystemObject); + }(FileSystemObject)); Test.File = File; var Directory = (function (_super) { __extends(Directory, _super); @@ -80,7 +80,7 @@ var Test; _super.apply(this, arguments); } return Directory; - })(FileSystemObject); + }(FileSystemObject)); Test.Directory = Directory; var file = new File("foo/bar.txt", "foo"); file.isNetworked = file.isFile;