diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 7561783d1398b..2682a5938d106 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,17 @@ namespace ts { } } + function checkTypePredicate(node: TypePredicateNode) { + const { parameterName, type } = node; + if (parameterName && parameterName.kind === SyntaxKind.Identifier) { + checkStrictModeIdentifier(parameterName as Identifier); + } + if (parameterName && parameterName.kind === SyntaxKind.ThisType) { + seenThisKeyword = true; + } + bind(type); + } + function bindSourceFileIfExternalModule() { setExportContextFlag(file); if (isExternalModule(file)) { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 50a3aee995528..0ee73756e572c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -124,8 +124,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 = {}; @@ -1743,10 +1743,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) { @@ -2116,6 +2122,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); @@ -2126,17 +2144,7 @@ namespace ts { } writeSpace(writer); - let returnType: Type; - if (signature.typePredicate) { - writer.writeParameter(signature.typePredicate.parameterName); - writeSpace(writer); - writeKeyword(writer, SyntaxKind.IsKeyword); - writeSpace(writer); - returnType = signature.typePredicate.type; - } - else { - returnType = getReturnTypeOfSignature(signature); - } + const returnType = getReturnTypeOfSignature(signature); buildTypeDisplay(returnType, writer, enclosingDeclaration, flags, symbolStack); } @@ -2693,7 +2701,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 @@ -3427,13 +3441,12 @@ namespace ts { } function createSignature(declaration: SignatureDeclaration, typeParameters: TypeParameter[], parameters: Symbol[], - resolvedReturnType: Type, typePredicate: TypePredicate, 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; @@ -3441,7 +3454,7 @@ 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); } @@ -3449,7 +3462,7 @@ namespace ts { const baseConstructorType = getBaseConstructorTypeOfClass(classType); const baseSignatures = getSignaturesOfType(baseConstructorType, SignatureKind.Construct); if (baseSignatures.length === 0) { - 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 baseTypeNode = getBaseTypeNodeOfClass(classType); const typeArguments = map(baseTypeNode.typeArguments, getTypeFromTypeNode); @@ -3915,6 +3928,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) { @@ -3955,20 +3986,11 @@ namespace ts { } let returnType: Type; - let typePredicate: TypePredicate; 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) - }; - } } else { // TypeScript 1.0 spec (April 2014): @@ -3983,8 +4005,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; } @@ -4643,6 +4664,25 @@ namespace ts { return links.resolvedType; } + function getPredicateType(node: TypePredicateNode): Type { + return createPredicateType(getSymbolOfNode(node), createTypePredicateFromTypePredicateNode(node)); + } + + function createPredicateType(symbol: Symbol, predicate: ThisTypePredicate | IdentifierTypePredicate) { + const type = createType(TypeFlags.Boolean | TypeFlags.PredicateType) as PredicateType; + type.symbol = symbol; + type.predicate = predicate; + 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: @@ -4664,7 +4704,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: @@ -4787,24 +4827,32 @@ namespace ts { return result; } + function cloneTypePredicate(predicate: TypePredicate, mapper: TypeMapper): ThisTypePredicate | IdentifierTypePredicate { + if (isIdentifierTypePredicate(predicate)) { + return { + kind: TypePredicateKind.Identifier, + parameterName: predicate.parameterName, + parameterIndex: predicate.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; 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) - }; - } const result = createSignature(signature.declaration, freshTypeParameters, instantiateList(signature.parameters, mapper, instantiateSymbol), instantiateType(signature.resolvedReturnType, mapper), - freshTypePredicate, signature.minArgumentCount, signature.hasRestParameter, signature.hasStringLiterals); result.target = signature; result.mapper = mapper; @@ -4874,6 +4922,10 @@ namespace ts { if (type.flags & TypeFlags.Intersection) { return getIntersectionType(instantiateList((type).types, mapper, instantiateType)); } + if (type.flags & TypeFlags.PredicateType) { + const predicate = (type as PredicateType).predicate; + return createPredicateType(type.symbol, cloneTypePredicate(predicate, mapper)); + } } return type; } @@ -5045,6 +5097,36 @@ namespace ts { 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) { + 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_compatible_with_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) { if (hasExcessProperties(source, target, reportErrors)) { @@ -5610,46 +5692,19 @@ namespace ts { result &= related; } - 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))) { + const targetReturnType = getReturnTypeOfSignature(target); + 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) { - 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.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}`); + reportError(Diagnostics.Signature_0_must_have_a_type_predicate, signatureToString(source)); } 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); return result & isRelatedTo(sourceReturnType, targetReturnType, reportErrors); } @@ -5986,6 +6041,9 @@ namespace ts { if (type.flags & (TypeFlags.Undefined | TypeFlags.Null)) { return anyType; } + if (type.flags & TypeFlags.PredicateType) { + return booleanType; + } if (type.flags & TypeFlags.ObjectLiteral) { return getWidenedTypeOfObjectLiteral(type); } @@ -6209,6 +6267,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; @@ -6304,17 +6367,7 @@ 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); - } - } - else { - inferFromTypes(getReturnTypeOfSignature(source), getReturnTypeOfSignature(target)); - } + inferFromTypes(getReturnTypeOfSignature(source), getReturnTypeOfSignature(target)); } function inferFromIndexTypes(source: Type, target: Type, sourceKind: IndexKind, targetKind: IndexKind) { @@ -6453,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; } @@ -6712,20 +6762,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) { @@ -6748,22 +6798,59 @@ namespace ts { return type; } const signature = getResolvedSignature(expr); + const predicateType = getReturnTypeOfSignature(signature); - if (signature.typePredicate && - expr.arguments[signature.typePredicate.parameterIndex] && - getSymbolAtLocation(expr.arguments[signature.typePredicate.parameterIndex]) === symbol) { + if (!predicateType || !(predicateType.flags & TypeFlags.PredicateType)) { + return type; + } + const predicate = (predicateType as PredicateType).predicate; + if (isIdentifierTypePredicate(predicate)) { + 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 (!assumeTrue) { - if (type.flags & TypeFlags.Union) { - return getUnionType(filter((type).types, t => !isTypeSubtypeOf(t, signature.typePredicate.type))); - } - return type; + 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 as ThisTypePredicate, 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 getNarrowedType(type, signature.typePredicate.type); } return type; } + 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)); + } + } + // Narrow the given type based on the given expression having the assumed boolean value. The returned type // will be a subtype or the same type as the argument. function narrowType(type: Type, expr: Expression, assumeTrue: boolean): Type { @@ -6792,11 +6879,21 @@ 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; } } + 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); @@ -10985,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: @@ -10999,6 +11096,19 @@ namespace ts { return false; } + function isInLegalThisTypePredicatePosition(node: Node): boolean { + if (isInLegalParameterTypePredicatePosition(node)) { + return true; + } + switch (node.parent.kind) { + case SyntaxKind.PropertyDeclaration: + case SyntaxKind.PropertySignature: + case SyntaxKind.GetAccessor: + return node === (node.parent as (PropertyDeclaration | GetAccessorDeclaration | PropertySignature)).type; + } + return false; + } + function checkSignatureDeclaration(node: SignatureDeclaration) { // Grammar checking if (node.kind === SyntaxKind.IndexSignature) { @@ -11017,9 +11127,14 @@ namespace ts { if (node.type) { if (node.type.kind === SyntaxKind.TypePredicate) { - const typePredicate = getSignatureFromDeclaration(node).typePredicate; - const typePredicateNode = node.type; - if (isInLegalTypePredicatePosition(typePredicateNode)) { + 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) { if (node.parameters[typePredicate.parameterIndex].dotDotDotToken) { error(typePredicateNode.parameterName, @@ -11067,10 +11182,6 @@ namespace ts { } } } - else { - error(typePredicateNode, - Diagnostics.A_type_predicate_is_only_allowed_in_return_type_position_for_functions_and_methods); - } } else { checkSourceElement(node.type); @@ -13027,7 +13138,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); @@ -14218,9 +14329,18 @@ namespace ts { } function checkTypePredicate(node: TypePredicateNode) { - if (!isInLegalTypePredicatePosition(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 (parameterName.kind === SyntaxKind.ThisType) { + if (!isInLegalThisTypePredicatePosition(node)) { + error(node, Diagnostics.A_this_based_type_predicate_is_only_allowed_within_a_class_or_interface_s_members_get_accessors_or_return_type_positions_for_functions_and_methods); + } + else { + getTypeFromThisTypeNode(parameterName as ThisTypeNode); + } + } } function checkSourceElement(node: Node): void { diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 331568eae224d..4698355133815 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1647,6 +1647,14 @@ "category": "Error", "code": 2517 }, + "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 within a class or interface's members, get accessors, or return type positions for functions and methods.": { + "category": "Error", + "code": 2519 + }, "Duplicate identifier '{0}'. Compiler uses declaration '{1}' to support async functions.": { "category": "Error", "code": 2520 diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index f5273e38ecf6f..62d9833710201 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1963,11 +1963,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; @@ -1977,8 +1973,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); } @@ -2424,8 +2428,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 0b9776e07e6aa..85ae020753f83 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,25 @@ namespace ts { CannotBeNamed } + 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 */ @@ -2091,6 +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, // 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, @@ -2102,7 +2122,7 @@ namespace ts { UnionOrIntersection = Union | Intersection, StructuredType = ObjectType | Union | Intersection, /* @internal */ - RequiresWidening = ContainsUndefinedOrNull | ContainsObjectLiteral, + RequiresWidening = ContainsUndefinedOrNull | ContainsObjectLiteral | PredicateType, /* @internal */ PropagatingFlags = ContainsUndefinedOrNull | ContainsObjectLiteral | ContainsAnyFunctionType } @@ -2123,6 +2143,11 @@ namespace ts { intrinsicName: string; // Name of intrinsic type } + // Predicate types (TypeFlags.Predicate) + export interface PredicateType extends Type { + predicate: ThisTypePredicate | IdentifierTypePredicate; + } + // String literal types (TypeFlags.StringLiteral) export interface StringLiteralType extends Type { text: string; // Text of string literal @@ -2239,7 +2264,6 @@ namespace ts { declaration: SignatureDeclaration; // Originating declaration typeParameters: TypeParameter[]; // Type parameters (undefined if non-generic) parameters: Symbol[]; // Parameters - typePredicate?: TypePredicate; // Type predicate /* @internal */ resolvedReturnType: Type; // Resolved return type /* @internal */ diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index f955db2d0c2a9..eee09fc641f8c 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -693,6 +693,10 @@ namespace ts { return node && node.kind === SyntaxKind.MethodDeclaration && node.parent.kind === SyntaxKind.ObjectLiteralExpression; } + export function isIdentifierTypePredicate(predicate: TypePredicate): predicate is IdentifierTypePredicate { + return predicate && predicate.kind === TypePredicateKind.Identifier; + } + export function getContainingFunction(node: Node): FunctionLikeDeclaration { while (true) { node = node.parent; 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..90c8c3efdf745 100644 --- a/tests/baselines/reference/stringLiteralTypesAsTags01.errors.txt +++ b/tests/baselines/reference/stringLiteralTypesAsTags01.errors.txt @@ -1,10 +1,8 @@ -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 (2 errors) ==== type Kind = "A" | "B" @@ -23,11 +21,7 @@ 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. 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 92d9d21f0cc85..e1fdf303870ec 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,6 +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'. + 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'. @@ -25,7 +26,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 @@ -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. @@ -57,7 +58,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 { @@ -149,6 +150,7 @@ 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; @@ -193,8 +195,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'. @@ -224,6 +224,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/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.js b/tests/baselines/reference/typeGuardFunctionOfFormThis.js new file mode 100644 index 0000000000000..0d1dceccede6a --- /dev/null +++ b/tests/baselines/reference/typeGuardFunctionOfFormThis.js @@ -0,0 +1,347 @@ +//// [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; +} + + +interface MimicGuardInterface { + isLeader(): this is LeadGuard; + isFollower(): this is FollowerGuard; +} + + +//// [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; +} + + +//// [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; +interface MimicGuardInterface { + isLeader(): this is LeadGuard; + isFollower(): this is FollowerGuard; +} diff --git a/tests/baselines/reference/typeGuardFunctionOfFormThis.symbols b/tests/baselines/reference/typeGuardFunctionOfFormThis.symbols new file mode 100644 index 0000000000000..2968063dc517b --- /dev/null +++ b/tests/baselines/reference/typeGuardFunctionOfFormThis.symbols @@ -0,0 +1,385 @@ +=== 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)) +} + + +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 new file mode 100644 index 0000000000000..e91c77dd07aaa --- /dev/null +++ b/tests/baselines/reference/typeGuardFunctionOfFormThis.types @@ -0,0 +1,441 @@ +=== 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() : 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() : this is FollowerGuard +>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() : this is LeadGuard +>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() : this is FollowerGuard +>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)()) : this is LeadGuard +>(a.isLeader)() : this is LeadGuard +>(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()) : this is FollowerGuard +>(a).isFollower() : this is FollowerGuard +>(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"])()) : this is LeadGuard +>(a["isLeader"])() : this is LeadGuard +>(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"]()) : this is FollowerGuard +>(a)["isFollower"]() : this is FollowerGuard +>(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() : this is LeadGuard +>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() : this is ArrowElite +>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() : this is ArrowMedic +>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() : this is Crate +>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() : this is Crate +>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() : this is MimicFollower +>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 +} + + +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/typeGuardFunctionOfFormThisErrors.errors.txt b/tests/baselines/reference/typeGuardFunctionOfFormThisErrors.errors.txt new file mode 100644 index 0000000000000..9f9f5d6533cab --- /dev/null +++ b/tests/baselines/reference/typeGuardFunctionOfFormThisErrors.errors.txt @@ -0,0 +1,103 @@ +tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors.ts(23,1): error TS2322: Type '() => this is LeadGuard' is not assignable to type '() => this is 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 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 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 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'. + + +==== tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors.ts (7 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 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 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 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 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 { + ~~~~ +!!! 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; + } + + 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 new file mode 100644 index 0000000000000..73622bf60cc18 --- /dev/null +++ b/tests/baselines/reference/typeGuardFunctionOfFormThisErrors.js @@ -0,0 +1,152 @@ +//// [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; +} + +let detached = a.isFollower; + +if (detached()) { + a.follow(); +} +else { + a.lead(); +} + +//// [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; +} +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/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 new file mode 100644 index 0000000000000..2f983d2cdf841 --- /dev/null +++ b/tests/baselines/reference/typeGuardOfFormThisMember.js @@ -0,0 +1,187 @@ +//// [typeGuardOfFormThisMember.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 = false; + file.isFSO = file.isFile; + file.isFile = true; + let x = file.isFile; + if (file.isFile) { + file.content; + if (file.isNetworked) { + file.host; + file.content; + } + } + else if (file.isDirectory) { + file.children; + } + 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) { + 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.isFSO = file.isFile; + file.isFile = true; + var x = file.isFile; + if (file.isFile) { + file.content; + if (file.isNetworked) { + file.host; + file.content; + } + } + else if (file.isDirectory) { + file.children; + } + 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 = {})); + + +//// [typeGuardOfFormThisMember.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/typeGuardOfFormThisMember.symbols b/tests/baselines/reference/typeGuardOfFormThisMember.symbols new file mode 100644 index 0000000000000..50e15b68ca40d --- /dev/null +++ b/tests/baselines/reference/typeGuardOfFormThisMember.symbols @@ -0,0 +1,240 @@ +=== 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)) + + 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, 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, 15, 2)) + } + set isFile(param) { +>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, 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, 19, 2)) + } + isNetworked: this is (Networked & this); +>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, 14, 14)) + } + + export class File extends FileSystemObject { +>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, 18, 14)) +>content : Symbol(content, Decl(typeGuardOfFormThisMember.ts, 18, 27)) +>super : Symbol(FileSystemObject, Decl(typeGuardOfFormThisMember.ts, 1, 16)) +>path : Symbol(path, Decl(typeGuardOfFormThisMember.ts, 18, 14)) + } + export class Directory extends FileSystemObject { +>Directory : Symbol(Directory, Decl(typeGuardOfFormThisMember.ts, 19, 2)) +>FileSystemObject : Symbol(FileSystemObject, Decl(typeGuardOfFormThisMember.ts, 1, 16)) + + children: FileSystemObject[]; +>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, 22, 2)) + + host: string; +>host : Symbol(host, Decl(typeGuardOfFormThisMember.ts, 23, 29)) + } + + let file: FileSystemObject = new File("foo/bar.txt", "foo"); +>file : Symbol(file, Decl(typeGuardOfFormThisMember.ts, 27, 4)) +>FileSystemObject : Symbol(FileSystemObject, Decl(typeGuardOfFormThisMember.ts, 1, 16)) +>File : Symbol(File, Decl(typeGuardOfFormThisMember.ts, 15, 2)) + + file.isNetworked = false; +>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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 46, 56)) + } + + interface GenericFollowerGuard extends GenericGuard { +>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, 50, 60)) + } + + interface GenericGuard { +>GenericGuard : Symbol(GenericGuard, Decl(typeGuardOfFormThisMember.ts, 52, 2)) +>T : Symbol(T, Decl(typeGuardOfFormThisMember.ts, 54, 24)) + + target: T; +>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, 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, 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, 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, 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, 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, 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, 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, 66, 2)) + + isMoreSpecific: this is MoreSpecificGuard; +>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, 70, 2)) +>SpecificGuard : Symbol(SpecificGuard, Decl(typeGuardOfFormThisMember.ts, 66, 2)) + + do(): void; +>do : Symbol(do, Decl(typeGuardOfFormThisMember.ts, 72, 52)) + } + + let general: SpecificGuard; +>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, 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, 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 new file mode 100644 index 0000000000000..68343947fb49a --- /dev/null +++ b/tests/baselines/reference/typeGuardOfFormThisMember.types @@ -0,0 +1,254 @@ +=== 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 + + isFSO: this is FileSystemObject; +>isFSO : this is 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 : boolean + + // 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 & FileSystemObject +>file : FileSystemObject +>isNetworked : this is Networked & FileSystemObject +>false : boolean + + file.isFSO = file.isFile; +>file.isFSO = file.isFile : this is File +>file.isFSO : this is FileSystemObject +>file : FileSystemObject +>isFSO : this is FileSystemObject +>file.isFile : this is File +>file : FileSystemObject +>isFile : this is File + + file.isFile = true; +>file.isFile = true : boolean +>file.isFile : this is File +>file : FileSystemObject +>isFile : this is File +>true : boolean + + let x = file.isFile; +>x : boolean +>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 + + 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 +>file : FileSystemObject +>isDirectory : this is Directory + + file.children; +>file.children : FileSystemObject[] +>file : Directory +>children : FileSystemObject[] + } + else if (file.isNetworked) { +>file.isNetworked : this is Networked & FileSystemObject +>file : FileSystemObject +>isNetworked : this is Networked & FileSystemObject + + file.host; +>file.host : string +>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/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..6f681ab09cffc --- /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..073a677b659e7 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 a >isNonVoid : (value: void | a) => value is a >value : void | a diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThis.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThis.ts new file mode 100644 index 0000000000000..aadbf3cd9c584 --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThis.ts @@ -0,0 +1,141 @@ +// @declaration: true +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; +} + + +interface MimicGuardInterface { + isLeader(): this is LeadGuard; + isFollower(): this is FollowerGuard; +} diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors.ts new file mode 100644 index 0000000000000..1c449e624f15c --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardFunctionOfFormThisErrors.ts @@ -0,0 +1,60 @@ +// @declaration: true +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; +} + +let detached = a.isFollower; + +if (detached()) { + a.follow(); +} +else { + a.lead(); +} \ No newline at end of file diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormThisMember.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormThisMember.ts new file mode 100644 index 0000000000000..decf6d99353eb --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardOfFormThisMember.ts @@ -0,0 +1,83 @@ +// @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 = false; + file.isFSO = file.isFile; + file.isFile = true; + let x = file.isFile; + if (file.isFile) { + file.content; + if (file.isNetworked) { + file.host; + file.content; + } + } + else if (file.isDirectory) { + file.children; + } + 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(); + } +} 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