From a11c41621bbbab100a391dd348651c6661549663 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 19 Sep 2022 14:16:01 -0700 Subject: [PATCH] Improve checking of `in` operator (#50666) * Improve checking of `in` operator * Accept new baselines * Add tests * Delete old and accept new baselines * Disallow right operand of type '{}' * Accept new baselines * Support number and symbol literals * Add tests * Disallow {} typed right operand only in strictNullChecks mode * Accept new baselines * Detect {} resulting from intersections * Accept new baselines * Don't attempt to reduce intersections with Record * Accept new baselines * Return undefined instead of unknownSymbol from getGlobalRecordSymbol() --- src/compiler/checker.ts | 93 +- src/compiler/diagnosticMessages.json | 12 +- ...onditionalTypeDoesntSpinForever.errors.txt | 153 +++ .../conditionalTypeDoesntSpinForever.types | 10 +- .../reference/controlFlowInOperator.types | 4 +- .../reference/fixSignatureCaching.errors.txt | 5 +- .../reference/fixSignatureCaching.symbols | 2 - .../reference/fixSignatureCaching.types | 10 +- ...nDoesNotOperateOnPrimitiveTypes.errors.txt | 71 +- .../reference/inKeywordAndUnknown.errors.txt | 23 + .../reference/inKeywordAndUnknown.types | 4 +- ...KeywordTypeguard(strict=false).errors.txt} | 158 ++- .../inKeywordTypeguard(strict=false).js | 541 +++++++++++ ... inKeywordTypeguard(strict=false).symbols} | 352 ++++++- .../inKeywordTypeguard(strict=false).types | 914 ++++++++++++++++++ ...inKeywordTypeguard(strict=true).errors.txt | 383 ++++++++ .../inKeywordTypeguard(strict=true).js | 542 +++++++++++ .../inKeywordTypeguard(strict=true).symbols | 727 ++++++++++++++ .../inKeywordTypeguard(strict=true).types | 914 ++++++++++++++++++ .../baselines/reference/inKeywordTypeguard.js | 333 ------- .../reference/inKeywordTypeguard.types | 454 --------- .../baselines/reference/inOperator.errors.txt | 4 +- .../inOperatorWithInvalidOperands.errors.txt | 72 +- .../inOperatorWithValidOperands.errors.txt | 65 ++ .../keyofAndIndexedAccess.errors.txt | 18 +- .../reference/mappedTypeProperties.errors.txt | 4 +- ...ressionTransform(target=es2020).errors.txt | 8 +- ...ressionTransform(target=es2022).errors.txt | 8 +- ...ressionTransform(target=esnext).errors.txt | 8 +- .../reference/symbolType2.errors.txt | 4 +- tests/cases/compiler/inKeywordTypeguard.ts | 130 +++ 31 files changed, 5090 insertions(+), 936 deletions(-) create mode 100644 tests/baselines/reference/conditionalTypeDoesntSpinForever.errors.txt create mode 100644 tests/baselines/reference/inKeywordAndUnknown.errors.txt rename tests/baselines/reference/{inKeywordTypeguard.errors.txt => inKeywordTypeguard(strict=false).errors.txt} (57%) create mode 100644 tests/baselines/reference/inKeywordTypeguard(strict=false).js rename tests/baselines/reference/{inKeywordTypeguard.symbols => inKeywordTypeguard(strict=false).symbols} (55%) create mode 100644 tests/baselines/reference/inKeywordTypeguard(strict=false).types create mode 100644 tests/baselines/reference/inKeywordTypeguard(strict=true).errors.txt create mode 100644 tests/baselines/reference/inKeywordTypeguard(strict=true).js create mode 100644 tests/baselines/reference/inKeywordTypeguard(strict=true).symbols create mode 100644 tests/baselines/reference/inKeywordTypeguard(strict=true).types delete mode 100644 tests/baselines/reference/inKeywordTypeguard.js delete mode 100644 tests/baselines/reference/inKeywordTypeguard.types create mode 100644 tests/baselines/reference/inOperatorWithValidOperands.errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 32655087467b5..f992dd0ea458f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -858,7 +858,8 @@ namespace ts { emptyTypeLiteralSymbol.members = createSymbolTable(); const emptyTypeLiteralType = createAnonymousType(emptyTypeLiteralSymbol, emptySymbols, emptyArray, emptyArray, emptyArray); - const unknownUnionType = strictNullChecks ? getUnionType([undefinedType, nullType, createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, emptyArray)]) : unknownType; + const unknownEmptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, emptyArray); + const unknownUnionType = strictNullChecks ? getUnionType([undefinedType, nullType, unknownEmptyObjectType]) : unknownType; const emptyGenericType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, emptyArray) as ObjectType as GenericType; emptyGenericType.instantiations = new Map(); @@ -998,6 +999,7 @@ namespace ts { let deferredGlobalOmitSymbol: Symbol | undefined; let deferredGlobalAwaitedSymbol: Symbol | undefined; let deferredGlobalBigIntType: ObjectType | undefined; + let deferredGlobalRecordSymbol: Symbol | undefined; const allPotentiallyUnusedIdentifiers = new Map(); // key is file name @@ -14314,6 +14316,11 @@ namespace ts { return (deferredGlobalBigIntType ||= getGlobalType("BigInt" as __String, /*arity*/ 0, /*reportErrors*/ false)) || emptyObjectType; } + function getGlobalRecordSymbol(): Symbol | undefined { + deferredGlobalRecordSymbol ||= getGlobalTypeAliasSymbol("Record" as __String, /*arity*/ 2, /*reportErrors*/ true) || unknownSymbol; + return deferredGlobalRecordSymbol === unknownSymbol ? undefined : deferredGlobalRecordSymbol; + } + /** * Instantiates a global type that is generic with some element type, and returns that instantiation. */ @@ -25199,19 +25206,27 @@ namespace ts { function isTypePresencePossible(type: Type, propName: __String, assumeTrue: boolean) { const prop = getPropertyOfType(type, propName); - if (prop) { - return prop.flags & SymbolFlags.Optional ? true : assumeTrue; - } - return getApplicableIndexInfoForName(type, propName) ? true : !assumeTrue; + return prop ? + !!(prop.flags & SymbolFlags.Optional) || assumeTrue : + !!getApplicableIndexInfoForName(type, propName) || !assumeTrue; } - function narrowByInKeyword(type: Type, name: __String, assumeTrue: boolean) { - if (type.flags & TypeFlags.Union - || type.flags & TypeFlags.Object && declaredType !== type && !(declaredType === unknownType && isEmptyAnonymousObjectType(type)) - || isThisTypeParameter(type) - || type.flags & TypeFlags.Intersection && every((type as IntersectionType).types, t => t.symbol !== globalThisSymbol)) { + function narrowByInKeyword(type: Type, nameType: StringLiteralType | NumberLiteralType | UniqueESSymbolType, assumeTrue: boolean) { + const name = getPropertyNameFromType(nameType); + const isKnownProperty = someType(type, t => isTypePresencePossible(t, name, /*assumeTrue*/ true)); + if (isKnownProperty) { + // If the check is for a known property (i.e. a property declared in some constituent of + // the target type), we filter the target type by presence of absence of the property. return filterType(type, t => isTypePresencePossible(t, name, assumeTrue)); } + if (assumeTrue) { + // If the check is for an unknown property, we intersect the target type with `Record`, + // where X is the name of the property. + const recordSymbol = getGlobalRecordSymbol(); + if (recordSymbol) { + return getIntersectionType([type, getTypeAliasInstantiation(recordSymbol, [nameType, unknownType])]); + } + } return type; } @@ -25271,15 +25286,14 @@ namespace ts { return narrowTypeByPrivateIdentifierInInExpression(type, expr, assumeTrue); } const target = getReferenceCandidate(expr.right); - const leftType = getTypeOfNode(expr.left); - if (leftType.flags & TypeFlags.StringLiteral) { - const name = escapeLeadingUnderscores((leftType as StringLiteralType).value); + const leftType = getTypeOfExpression(expr.left); + if (leftType.flags & TypeFlags.StringOrNumberLiteralOrUnique) { if (containsMissingType(type) && isAccessExpression(reference) && isMatchingReference(reference.expression, target) && - getAccessedPropertyName(reference) === name) { + getAccessedPropertyName(reference) === getPropertyNameFromType(leftType as StringLiteralType | NumberLiteralType | UniqueESSymbolType)) { return getTypeWithFacts(type, assumeTrue ? TypeFacts.NEUndefined : TypeFacts.EQUndefined); } if (isMatchingReference(reference, target)) { - return narrowByInKeyword(type, name, assumeTrue); + return narrowByInKeyword(type, leftType as StringLiteralType | NumberLiteralType | UniqueESSymbolType, assumeTrue); } } break; @@ -33848,6 +33862,10 @@ namespace ts { return booleanType; } + function hasEmptyObjectIntersection(type: Type): boolean { + return someType(type, t => t === unknownEmptyObjectType || !!(t.flags & TypeFlags.Intersection) && some((t as IntersectionType).types, isEmptyAnonymousObjectType)); + } + function checkInExpression(left: Expression, right: Expression, leftType: Type, rightType: Type): Type { if (leftType === silentNeverType || rightType === silentNeverType) { return silentNeverType; @@ -33864,43 +33882,20 @@ namespace ts { } } else { - leftType = checkNonNullType(leftType, left); - // TypeScript 1.0 spec (April 2014): 4.15.5 - // Require the left operand to be of type Any, the String primitive type, or the Number primitive type. - if (!(allTypesAssignableToKind(leftType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbolLike) || - isTypeAssignableToKind(leftType, TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping | TypeFlags.TypeParameter))) { - error(left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_a_private_identifier_or_of_type_any_string_number_or_symbol); + // The type of the lef operand must be assignable to string, number, or symbol. + checkTypeAssignableTo(checkNonNullType(leftType, left), stringNumberSymbolType, left); + } + // The type of the right operand must be assignable to 'object'. + if (checkTypeAssignableTo(checkNonNullType(rightType, right), nonPrimitiveType, right)) { + // The {} type is assignable to the object type, yet {} might represent a primitive type. Here we + // detect and error on {} that results from narrowing the unknown type, as well as intersections + // that include {} (we know that the other types in such intersections are assignable to object + // since we already checked for that). + if (hasEmptyObjectIntersection(rightType)) { + error(right, Diagnostics.Type_0_may_represent_a_primitive_value_which_is_not_permitted_as_the_right_operand_of_the_in_operator, typeToString(rightType)); } } - rightType = checkNonNullType(rightType, right); - // TypeScript 1.0 spec (April 2014): 4.15.5 - // The in operator requires the right operand to be - // - // 1. assignable to the non-primitive type, - // 2. an unconstrained type parameter, - // 3. a union or intersection including one or more type parameters, whose constituents are all assignable to the - // the non-primitive type, or are unconstrainted type parameters, or have constraints assignable to the - // non-primitive type, or - // 4. a type parameter whose constraint is - // i. an object type, - // ii. the non-primitive type, or - // iii. a union or intersection with at least one constituent assignable to an object or non-primitive type. - // - // The divergent behavior for type parameters and unions containing type parameters is a workaround for type - // parameters not being narrowable. If the right operand is a concrete type, we can error if there is any chance - // it is a primitive. But if the operand is a type parameter, it cannot be narrowed, so we don't issue an error - // unless *all* instantiations would result in an error. - // // The result is always of the Boolean primitive type. - const rightTypeConstraint = getConstraintOfType(rightType); - if (!allTypesAssignableToKind(rightType, TypeFlags.NonPrimitive | TypeFlags.InstantiableNonPrimitive) || - rightTypeConstraint && ( - isTypeAssignableToKind(rightType, TypeFlags.UnionOrIntersection) && !allTypesAssignableToKind(rightTypeConstraint, TypeFlags.NonPrimitive | TypeFlags.InstantiableNonPrimitive) || - !maybeTypeOfKind(rightTypeConstraint, TypeFlags.NonPrimitive | TypeFlags.InstantiableNonPrimitive | TypeFlags.Object) - ) - ) { - error(right, Diagnostics.The_right_hand_side_of_an_in_expression_must_not_be_a_primitive); - } return booleanType; } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 2ccc8197830cc..ad6b6615fcab6 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1844,14 +1844,6 @@ "category": "Error", "code": 2359 }, - "The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.": { - "category": "Error", - "code": 2360 - }, - "The right-hand side of an 'in' expression must not be a primitive.": { - "category": "Error", - "code": 2361 - }, "The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.": { "category": "Error", "code": 2362 @@ -2845,6 +2837,10 @@ "category": "Error", "code": 2637 }, + "Type '{0}' may represent a primitive value, which is not permitted as the right operand of the 'in' operator.": { + "category": "Error", + "code": 2638 + }, "Cannot augment module '{0}' with value exports because it resolves to a non-module entity.": { "category": "Error", diff --git a/tests/baselines/reference/conditionalTypeDoesntSpinForever.errors.txt b/tests/baselines/reference/conditionalTypeDoesntSpinForever.errors.txt new file mode 100644 index 0000000000000..3beaed59ca5c8 --- /dev/null +++ b/tests/baselines/reference/conditionalTypeDoesntSpinForever.errors.txt @@ -0,0 +1,153 @@ +tests/cases/compiler/conditionalTypeDoesntSpinForever.ts(23,15): error TS2322: Type 'SO_FAR' is not assignable to type 'object'. +tests/cases/compiler/conditionalTypeDoesntSpinForever.ts(36,19): error TS2322: Type 'SO_FAR' is not assignable to type 'object'. +tests/cases/compiler/conditionalTypeDoesntSpinForever.ts(53,21): error TS2322: Type 'SO_FAR' is not assignable to type 'object'. +tests/cases/compiler/conditionalTypeDoesntSpinForever.ts(53,45): error TS2322: Type 'SO_FAR' is not assignable to type 'object'. +tests/cases/compiler/conditionalTypeDoesntSpinForever.ts(65,17): error TS2322: Type 'SO_FAR' is not assignable to type 'object'. +tests/cases/compiler/conditionalTypeDoesntSpinForever.ts(78,38): error TS2322: Type 'SO_FAR' is not assignable to type 'object'. +tests/cases/compiler/conditionalTypeDoesntSpinForever.ts(94,21): error TS2322: Type 'SO_FAR' is not assignable to type 'object'. +tests/cases/compiler/conditionalTypeDoesntSpinForever.ts(97,71): error TS2322: Type 'SO_FAR' is not assignable to type 'object'. + + +==== tests/cases/compiler/conditionalTypeDoesntSpinForever.ts (8 errors) ==== + // A *self-contained* demonstration of the problem follows... + // Test this by running `tsc --target es6` on the command-line, rather than through another build tool such as Gulp, Webpack, etc. + + export enum PubSubRecordIsStoredInRedisAsA { + redisHash = "redisHash", + jsonEncodedRedisString = "jsonEncodedRedisString" + } + + export interface PubSubRecord> { + name: NAME; + record: RECORD; + identifier: IDENTIFIER; + storedAs: PubSubRecordIsStoredInRedisAsA; + maxMsToWaitBeforePublishing: number; + } + + type NameFieldConstructor = + SO_FAR extends {name: any} ? {} : { + name: (t?: TYPE) => BuildPubSubRecordType + } + + const buildNameFieldConstructor = (soFar: SO_FAR) => ( + "name" in soFar ? {} : { + ~~~~~ +!!! error TS2322: Type 'SO_FAR' is not assignable to type 'object'. +!!! related TS2208 tests/cases/compiler/conditionalTypeDoesntSpinForever.ts:22:38: This type parameter might need an `extends object` constraint. + name: (instance: TYPE = undefined) => + buildPubSubRecordType(Object.assign({}, soFar, {name: instance as TYPE}) as SO_FAR & {name: TYPE}) as BuildPubSubRecordType + } + ); + + type StoredAsConstructor = + SO_FAR extends {storedAs: any} ? {} : { + storedAsJsonEncodedRedisString: () => BuildPubSubRecordType; + storedRedisHash: () => BuildPubSubRecordType; + } + + const buildStoredAsConstructor = (soFar: SO_FAR) => ( + "storedAs" in soFar ? {} : { + ~~~~~ +!!! error TS2322: Type 'SO_FAR' is not assignable to type 'object'. +!!! related TS2208 tests/cases/compiler/conditionalTypeDoesntSpinForever.ts:35:37: This type parameter might need an `extends object` constraint. + storedAsJsonEncodedRedisString: () => + buildPubSubRecordType(Object.assign({}, soFar, {storedAs: PubSubRecordIsStoredInRedisAsA.jsonEncodedRedisString})) as + BuildPubSubRecordType, + storedAsRedisHash: () => + buildPubSubRecordType(Object.assign({}, soFar, {storedAs: PubSubRecordIsStoredInRedisAsA.redisHash})) as + BuildPubSubRecordType, + } + ); + + type IdentifierFieldConstructor = + SO_FAR extends {identifier: any} ? {} : + SO_FAR extends {record: any} ? { + identifier: >(t?: TYPE) => BuildPubSubRecordType + } : {} + + const buildIdentifierFieldConstructor = (soFar: SO_FAR) => ( + "identifier" in soFar || (!("record" in soFar)) ? {} : { + ~~~~~ +!!! error TS2322: Type 'SO_FAR' is not assignable to type 'object'. +!!! related TS2208 tests/cases/compiler/conditionalTypeDoesntSpinForever.ts:52:44: This type parameter might need an `extends object` constraint. + ~~~~~ +!!! error TS2322: Type 'SO_FAR' is not assignable to type 'object'. +!!! related TS2208 tests/cases/compiler/conditionalTypeDoesntSpinForever.ts:52:44: This type parameter might need an `extends object` constraint. + identifier: (instance: TYPE = undefined) => + buildPubSubRecordType(Object.assign({}, soFar, {identifier: instance as TYPE}) as SO_FAR & {identifier: TYPE}) as BuildPubSubRecordType + } + ); + + type RecordFieldConstructor = + SO_FAR extends {record: any} ? {} : { + record: (t?: TYPE) => BuildPubSubRecordType + } + + const buildRecordFieldConstructor = (soFar: SO_FAR) => ( + "record" in soFar ? {} : { + ~~~~~ +!!! error TS2322: Type 'SO_FAR' is not assignable to type 'object'. +!!! related TS2208 tests/cases/compiler/conditionalTypeDoesntSpinForever.ts:64:40: This type parameter might need an `extends object` constraint. + record: (instance: TYPE = undefined) => + buildPubSubRecordType(Object.assign({}, soFar, {record: instance as TYPE}) as SO_FAR & {record: TYPE}) as BuildPubSubRecordType + } + ); + + type MaxMsToWaitBeforePublishingFieldConstructor = + SO_FAR extends {maxMsToWaitBeforePublishing: any} ? {} : { + maxMsToWaitBeforePublishing: (t: number) => BuildPubSubRecordType, + neverDelayPublishing: () => BuildPubSubRecordType, + } + + const buildMaxMsToWaitBeforePublishingFieldConstructor = (soFar: SO_FAR): MaxMsToWaitBeforePublishingFieldConstructor => ( + "maxMsToWaitBeforePublishing" in soFar ? {} : { + ~~~~~ +!!! error TS2322: Type 'SO_FAR' is not assignable to type 'object'. +!!! related TS2208 tests/cases/compiler/conditionalTypeDoesntSpinForever.ts:77:61: This type parameter might need an `extends object` constraint. + maxMsToWaitBeforePublishing: (instance: number = 0) => + buildPubSubRecordType(Object.assign({}, soFar, {maxMsToWaitBeforePublishing: instance})) as BuildPubSubRecordType, + neverDelayPublishing: () => + buildPubSubRecordType(Object.assign({}, soFar, {maxMsToWaitBeforePublishing: 0})) as BuildPubSubRecordType, + } + ) as MaxMsToWaitBeforePublishingFieldConstructor; + + type TypeConstructor = + SO_FAR extends {identifier: any, record: any, maxMsToWaitBeforePublishing: number, storedAs: PubSubRecordIsStoredInRedisAsA} ? { + type: SO_FAR, + fields: Set, + hasField: (fieldName: string | number | symbol) => fieldName is keyof SO_FAR + } : {} + + const buildType = (soFar: SO_FAR) => ( + "identifier" in soFar && "object" in soFar && "maxMsToWaitBeforePublishing" in soFar && "PubSubRecordIsStoredInRedisAsA" in soFar ? {} : { + ~~~~~ +!!! error TS2322: Type 'SO_FAR' is not assignable to type 'object'. +!!! related TS2208 tests/cases/compiler/conditionalTypeDoesntSpinForever.ts:93:22: This type parameter might need an `extends object` constraint. + type: soFar, + fields: () => new Set(Object.keys(soFar) as (keyof SO_FAR)[]), + hasField: (fieldName: string | number | symbol) => fieldName in soFar + ~~~~~ +!!! error TS2322: Type 'SO_FAR' is not assignable to type 'object'. +!!! related TS2208 tests/cases/compiler/conditionalTypeDoesntSpinForever.ts:93:22: This type parameter might need an `extends object` constraint. + } + ); + + type BuildPubSubRecordType = + NameFieldConstructor & + IdentifierFieldConstructor & + RecordFieldConstructor & + StoredAsConstructor & // infinite loop goes away when you comment out this line + MaxMsToWaitBeforePublishingFieldConstructor & + TypeConstructor + + const buildPubSubRecordType = (soFar: SO_FAR) => Object.assign( + {}, + buildNameFieldConstructor(soFar), + buildIdentifierFieldConstructor(soFar), + buildRecordFieldConstructor(soFar), + buildStoredAsConstructor(soFar), + buildMaxMsToWaitBeforePublishingFieldConstructor(soFar), + buildType(soFar) + ) as BuildPubSubRecordType; + const PubSubRecordType = buildPubSubRecordType({}); \ No newline at end of file diff --git a/tests/baselines/reference/conditionalTypeDoesntSpinForever.types b/tests/baselines/reference/conditionalTypeDoesntSpinForever.types index c1eee6deff83f..a7b17a7cc6d0a 100644 --- a/tests/baselines/reference/conditionalTypeDoesntSpinForever.types +++ b/tests/baselines/reference/conditionalTypeDoesntSpinForever.types @@ -212,12 +212,12 @@ export enum PubSubRecordIsStoredInRedisAsA { >buildPubSubRecordType(Object.assign({}, soFar, {identifier: instance as TYPE}) as SO_FAR & {identifier: TYPE}) : BuildPubSubRecordType >buildPubSubRecordType : (soFar: SO_FAR) => BuildPubSubRecordType >Object.assign({}, soFar, {identifier: instance as TYPE}) as SO_FAR & {identifier: TYPE} : SO_FAR & { identifier: TYPE; } ->Object.assign({}, soFar, {identifier: instance as TYPE}) : SO_FAR & { identifier: TYPE; } +>Object.assign({}, soFar, {identifier: instance as TYPE}) : SO_FAR & Record<"record", unknown> & { identifier: TYPE; } >Object.assign : { (target: T, source: U): T & U; (target: T, source1: U, source2: V): T & U & V; (target: T, source1: U, source2: V, source3: W): T & U & V & W; (target: object, ...sources: any[]): any; } >Object : ObjectConstructor >assign : { (target: T, source: U): T & U; (target: T, source1: U, source2: V): T & U & V; (target: T, source1: U, source2: V, source3: W): T & U & V & W; (target: object, ...sources: any[]): any; } >{} : {} ->soFar : SO_FAR +>soFar : SO_FAR & Record<"record", unknown> >{identifier: instance as TYPE} : { identifier: TYPE; } >identifier : TYPE >instance as TYPE : TYPE @@ -389,13 +389,13 @@ export enum PubSubRecordIsStoredInRedisAsA { >soFar : SO_FAR >"object" in soFar : boolean >"object" : "object" ->soFar : SO_FAR +>soFar : SO_FAR & Record<"identifier", unknown> >"maxMsToWaitBeforePublishing" in soFar : boolean >"maxMsToWaitBeforePublishing" : "maxMsToWaitBeforePublishing" ->soFar : SO_FAR +>soFar : SO_FAR & Record<"identifier", unknown> & Record<"object", unknown> >"PubSubRecordIsStoredInRedisAsA" in soFar : boolean >"PubSubRecordIsStoredInRedisAsA" : "PubSubRecordIsStoredInRedisAsA" ->soFar : SO_FAR +>soFar : SO_FAR & Record<"identifier", unknown> & Record<"object", unknown> & Record<"maxMsToWaitBeforePublishing", unknown> >{} : {} >{ type: soFar, fields: () => new Set(Object.keys(soFar) as (keyof SO_FAR)[]), hasField: (fieldName: string | number | symbol) => fieldName in soFar } : { type: SO_FAR; fields: () => Set; hasField: (fieldName: string | number | symbol) => boolean; } diff --git a/tests/baselines/reference/controlFlowInOperator.types b/tests/baselines/reference/controlFlowInOperator.types index fb1c3f8ccc044..fffddfffe260b 100644 --- a/tests/baselines/reference/controlFlowInOperator.types +++ b/tests/baselines/reference/controlFlowInOperator.types @@ -44,7 +44,7 @@ if ('d' in c) { >c : A | B c; // never ->c : never +>c : (A | B) & Record<"d", unknown> } if (a in c) { @@ -67,6 +67,6 @@ if (d in c) { >c : A | B c; // never ->c : never +>c : (A | B) & Record<"d", unknown> } diff --git a/tests/baselines/reference/fixSignatureCaching.errors.txt b/tests/baselines/reference/fixSignatureCaching.errors.txt index d1c3ce6394fa8..4bb2276c3c554 100644 --- a/tests/baselines/reference/fixSignatureCaching.errors.txt +++ b/tests/baselines/reference/fixSignatureCaching.errors.txt @@ -3,6 +3,7 @@ tests/cases/conformance/fixSignatureCaching.ts(284,10): error TS2339: Property ' tests/cases/conformance/fixSignatureCaching.ts(293,10): error TS2339: Property 'FALLBACK_PHONE' does not exist on type '{}'. tests/cases/conformance/fixSignatureCaching.ts(294,10): error TS2339: Property 'FALLBACK_TABLET' does not exist on type '{}'. tests/cases/conformance/fixSignatureCaching.ts(295,10): error TS2339: Property 'FALLBACK_MOBILE' does not exist on type '{}'. +tests/cases/conformance/fixSignatureCaching.ts(301,17): error TS2339: Property 'isArray' does not exist on type 'never'. tests/cases/conformance/fixSignatureCaching.ts(330,74): error TS2339: Property 'mobileDetectRules' does not exist on type '{}'. tests/cases/conformance/fixSignatureCaching.ts(369,10): error TS2339: Property 'findMatch' does not exist on type '{}'. tests/cases/conformance/fixSignatureCaching.ts(387,10): error TS2339: Property 'findMatches' does not exist on type '{}'. @@ -58,7 +59,7 @@ tests/cases/conformance/fixSignatureCaching.ts(981,16): error TS2304: Cannot fin tests/cases/conformance/fixSignatureCaching.ts(983,44): error TS2339: Property 'MobileDetect' does not exist on type 'Window & typeof globalThis'. -==== tests/cases/conformance/fixSignatureCaching.ts (58 errors) ==== +==== tests/cases/conformance/fixSignatureCaching.ts (59 errors) ==== // Repro from #10697 (function (define, undefined) { @@ -370,6 +371,8 @@ tests/cases/conformance/fixSignatureCaching.ts(983,44): error TS2339: Property ' isArray = 'isArray' in Array ? function (value) { return Object.prototype.toString.call(value) === '[object Array]'; } : Array.isArray; + ~~~~~~~ +!!! error TS2339: Property 'isArray' does not exist on type 'never'. function equalIC(a, b) { return a != null && b != null && a.toLowerCase() === b.toLowerCase(); diff --git a/tests/baselines/reference/fixSignatureCaching.symbols b/tests/baselines/reference/fixSignatureCaching.symbols index 0ec1d2c63aae8..97efe41ce1205 100644 --- a/tests/baselines/reference/fixSignatureCaching.symbols +++ b/tests/baselines/reference/fixSignatureCaching.symbols @@ -825,9 +825,7 @@ define(function () { >value : Symbol(value, Decl(fixSignatureCaching.ts, 299, 20)) : Array.isArray; ->Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) >Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) ->isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --)) function equalIC(a, b) { >equalIC : Symbol(equalIC, Decl(fixSignatureCaching.ts, 300, 24)) diff --git a/tests/baselines/reference/fixSignatureCaching.types b/tests/baselines/reference/fixSignatureCaching.types index 06ab4f3df7eee..0a0d657a64d10 100644 --- a/tests/baselines/reference/fixSignatureCaching.types +++ b/tests/baselines/reference/fixSignatureCaching.types @@ -1127,9 +1127,9 @@ define(function () { >'[object Array]' : "[object Array]" isArray = 'isArray' in Array ->isArray = 'isArray' in Array ? function (value) { return Object.prototype.toString.call(value) === '[object Array]'; } : Array.isArray : (value: any) => boolean +>isArray = 'isArray' in Array ? function (value) { return Object.prototype.toString.call(value) === '[object Array]'; } : Array.isArray : any >isArray : any ->'isArray' in Array ? function (value) { return Object.prototype.toString.call(value) === '[object Array]'; } : Array.isArray : (value: any) => boolean +>'isArray' in Array ? function (value) { return Object.prototype.toString.call(value) === '[object Array]'; } : Array.isArray : any >'isArray' in Array : boolean >'isArray' : "isArray" >Array : ArrayConstructor @@ -1150,9 +1150,9 @@ define(function () { >'[object Array]' : "[object Array]" : Array.isArray; ->Array.isArray : (arg: any) => arg is any[] ->Array : ArrayConstructor ->isArray : (arg: any) => arg is any[] +>Array.isArray : any +>Array : never +>isArray : any function equalIC(a, b) { >equalIC : (a: any, b: any) => boolean diff --git a/tests/baselines/reference/inDoesNotOperateOnPrimitiveTypes.errors.txt b/tests/baselines/reference/inDoesNotOperateOnPrimitiveTypes.errors.txt index 049b46621e8ac..b4add2674a153 100644 --- a/tests/baselines/reference/inDoesNotOperateOnPrimitiveTypes.errors.txt +++ b/tests/baselines/reference/inDoesNotOperateOnPrimitiveTypes.errors.txt @@ -1,13 +1,31 @@ -tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(19,17): error TS2361: The right-hand side of an 'in' expression must not be a primitive. -tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(23,12): error TS2361: The right-hand side of an 'in' expression must not be a primitive. -tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(27,12): error TS2361: The right-hand side of an 'in' expression must not be a primitive. -tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(34,12): error TS2361: The right-hand side of an 'in' expression must not be a primitive. -tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(53,14): error TS2361: The right-hand side of an 'in' expression must not be a primitive. -tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(60,12): error TS2361: The right-hand side of an 'in' expression must not be a primitive. -tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(64,12): error TS2361: The right-hand side of an 'in' expression must not be a primitive. +tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(12,17): error TS2322: Type 'T' is not assignable to type 'object'. +tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(19,17): error TS2322: Type 'T' is not assignable to type 'object'. + Type 'string | number' is not assignable to type 'object'. + Type 'string' is not assignable to type 'object'. +tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(23,12): error TS2322: Type 'T | U' is not assignable to type 'object'. + Type 'T' is not assignable to type 'object'. + Type 'string | number' is not assignable to type 'object'. + Type 'string' is not assignable to type 'object'. +tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(27,12): error TS2322: Type 'T | U' is not assignable to type 'object'. + Type 'U' is not assignable to type 'object'. + Type 'string | number' is not assignable to type 'object'. + Type 'string' is not assignable to type 'object'. +tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(34,12): error TS2322: Type 'string | number | T' is not assignable to type 'object'. + Type 'string' is not assignable to type 'object'. +tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(36,14): error TS2322: Type 'T' is not assignable to type 'object'. +tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(41,12): error TS2322: Type 'T' is not assignable to type 'object'. + Type 'object | "hello"' is not assignable to type 'object'. + Type 'string' is not assignable to type 'object'. +tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(53,14): error TS2322: Type 'T | U' is not assignable to type 'object'. + Type 'T' is not assignable to type 'object'. + Type 'string | object' is not assignable to type 'object'. + Type 'string' is not assignable to type 'object'. +tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(60,12): error TS2322: Type 'T & U' is not assignable to type 'object'. +tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(64,12): error TS2322: Type 'T & (0 | 1 | 2)' is not assignable to type 'object'. + Type 'T & 0' is not assignable to type 'object'. -==== tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts (7 errors) ==== +==== tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts (10 errors) ==== const validHasKey = ( thing: T, key: string, @@ -20,6 +38,9 @@ tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(64,12): error TS2361: T key: string, ): boolean => { return key in thing; // Ok (as T may be instantiated with a valid type) + ~~~~~ +!!! error TS2322: Type 'T' is not assignable to type 'object'. +!!! related TS2208 tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts:8:26: This type parameter might need an `extends object` constraint. }; function invalidHasKey( @@ -28,19 +49,27 @@ tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(64,12): error TS2361: T ): boolean { return key in thing; // Error (because all possible instantiations are errors) ~~~~~ -!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. +!!! error TS2322: Type 'T' is not assignable to type 'object'. +!!! error TS2322: Type 'string | number' is not assignable to type 'object'. +!!! error TS2322: Type 'string' is not assignable to type 'object'. } function union1(thing: T | U) { "key" in thing; // Error (because all possible instantiations are errors) ~~~~~ -!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. +!!! error TS2322: Type 'T | U' is not assignable to type 'object'. +!!! error TS2322: Type 'T' is not assignable to type 'object'. +!!! error TS2322: Type 'string | number' is not assignable to type 'object'. +!!! error TS2322: Type 'string' is not assignable to type 'object'. } function union2(thing: T | U) { "key" in thing; // Error (because narrowing is possible) ~~~~~ -!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. +!!! error TS2322: Type 'T | U' is not assignable to type 'object'. +!!! error TS2322: Type 'U' is not assignable to type 'object'. +!!! error TS2322: Type 'string | number' is not assignable to type 'object'. +!!! error TS2322: Type 'string' is not assignable to type 'object'. if (typeof thing === "object") { "key" in thing; // Ok } @@ -49,14 +78,22 @@ tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(64,12): error TS2361: T function union3(thing: T | string | number) { "key" in thing; // Error (because narrowing is possible) ~~~~~ -!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. +!!! error TS2322: Type 'string | number | T' is not assignable to type 'object'. +!!! error TS2322: Type 'string' is not assignable to type 'object'. if (typeof thing !== "string" && typeof thing !== "number") { "key" in thing; // Ok (because further narrowing is impossible) + ~~~~~ +!!! error TS2322: Type 'T' is not assignable to type 'object'. +!!! related TS2208 tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts:33:17: This type parameter might need an `extends object` constraint. } } function union4(thing: T) { "key" in thing; // Ok (because narrowing is impossible) + ~~~~~ +!!! error TS2322: Type 'T' is not assignable to type 'object'. +!!! error TS2322: Type 'object | "hello"' is not assignable to type 'object'. +!!! error TS2322: Type 'string' is not assignable to type 'object'. } function union5(p: T | U) { @@ -70,7 +107,10 @@ tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(64,12): error TS2361: T // error seems very low-value. "key" in p; ~ -!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. +!!! error TS2322: Type 'T | U' is not assignable to type 'object'. +!!! error TS2322: Type 'T' is not assignable to type 'object'. +!!! error TS2322: Type 'string | object' is not assignable to type 'object'. +!!! error TS2322: Type 'string' is not assignable to type 'object'. if (typeof p === "object") { "key" in p; } @@ -79,12 +119,13 @@ tests/cases/compiler/inDoesNotOperateOnPrimitiveTypes.ts(64,12): error TS2361: T function intersection1(thing: T & U) { "key" in thing; // Error (because all possible instantiations are errors) ~~~~~ -!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. +!!! error TS2322: Type 'T & U' is not assignable to type 'object'. } function intersection2(thing: T & (0 | 1 | 2)) { "key" in thing; // Error (because all possible instantations are errors) ~~~~~ -!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. +!!! error TS2322: Type 'T & (0 | 1 | 2)' is not assignable to type 'object'. +!!! error TS2322: Type 'T & 0' is not assignable to type 'object'. } \ No newline at end of file diff --git a/tests/baselines/reference/inKeywordAndUnknown.errors.txt b/tests/baselines/reference/inKeywordAndUnknown.errors.txt new file mode 100644 index 0000000000000..61c2cd2a2a364 --- /dev/null +++ b/tests/baselines/reference/inKeywordAndUnknown.errors.txt @@ -0,0 +1,23 @@ +tests/cases/compiler/inKeywordAndUnknown.ts(12,18): error TS2638: Type '{}' may represent a primitive value, which is not permitted as the right operand of the 'in' operator. + + +==== tests/cases/compiler/inKeywordAndUnknown.ts (1 errors) ==== + // Repro from #50531 + + function f(x: {}, y: unknown) { + if (!("a" in x)) { + return; + } + x; // {} + if (!y) { + return; + } + y; // {} + if (!("a" in y)) { + ~ +!!! error TS2638: Type '{}' may represent a primitive value, which is not permitted as the right operand of the 'in' operator. + return; + } + y; // {} + } + \ No newline at end of file diff --git a/tests/baselines/reference/inKeywordAndUnknown.types b/tests/baselines/reference/inKeywordAndUnknown.types index 74345ca004547..59a587598b23d 100644 --- a/tests/baselines/reference/inKeywordAndUnknown.types +++ b/tests/baselines/reference/inKeywordAndUnknown.types @@ -16,7 +16,7 @@ function f(x: {}, y: unknown) { return; } x; // {} ->x : {} +>x : Record<"a", unknown> if (!y) { >!y : boolean @@ -37,6 +37,6 @@ function f(x: {}, y: unknown) { return; } y; // {} ->y : {} +>y : Record<"a", unknown> } diff --git a/tests/baselines/reference/inKeywordTypeguard.errors.txt b/tests/baselines/reference/inKeywordTypeguard(strict=false).errors.txt similarity index 57% rename from tests/baselines/reference/inKeywordTypeguard.errors.txt rename to tests/baselines/reference/inKeywordTypeguard(strict=false).errors.txt index cfb2b090a7af5..6feabe9f5c65e 100644 --- a/tests/baselines/reference/inKeywordTypeguard.errors.txt +++ b/tests/baselines/reference/inKeywordTypeguard(strict=false).errors.txt @@ -5,8 +5,10 @@ tests/cases/compiler/inKeywordTypeguard.ts(16,11): error TS2339: Property 'a' do tests/cases/compiler/inKeywordTypeguard.ts(27,11): error TS2339: Property 'b' does not exist on type 'AWithOptionalProp | BWithOptionalProp'. Property 'b' does not exist on type 'AWithOptionalProp'. tests/cases/compiler/inKeywordTypeguard.ts(42,11): error TS2339: Property 'b' does not exist on type 'AWithMethod'. -tests/cases/compiler/inKeywordTypeguard.ts(49,11): error TS2339: Property 'a' does not exist on type 'never'. -tests/cases/compiler/inKeywordTypeguard.ts(50,11): error TS2339: Property 'b' does not exist on type 'never'. +tests/cases/compiler/inKeywordTypeguard.ts(49,11): error TS2339: Property 'a' does not exist on type '(AWithMethod | BWithMethod) & Record<"c", unknown>'. + Property 'a' does not exist on type 'BWithMethod & Record<"c", unknown>'. +tests/cases/compiler/inKeywordTypeguard.ts(50,11): error TS2339: Property 'b' does not exist on type '(AWithMethod | BWithMethod) & Record<"c", unknown>'. + Property 'b' does not exist on type 'AWithMethod & Record<"c", unknown>'. tests/cases/compiler/inKeywordTypeguard.ts(52,11): error TS2339: Property 'a' does not exist on type 'AWithMethod | BWithMethod'. Property 'a' does not exist on type 'BWithMethod'. tests/cases/compiler/inKeywordTypeguard.ts(53,11): error TS2339: Property 'b' does not exist on type 'AWithMethod | BWithMethod'. @@ -19,9 +21,14 @@ tests/cases/compiler/inKeywordTypeguard.ts(74,32): error TS2339: Property 'a' do tests/cases/compiler/inKeywordTypeguard.ts(82,39): error TS2339: Property 'b' does not exist on type 'A'. tests/cases/compiler/inKeywordTypeguard.ts(84,39): error TS2339: Property 'a' does not exist on type 'B'. tests/cases/compiler/inKeywordTypeguard.ts(94,26): error TS2339: Property 'a' does not exist on type 'never'. +tests/cases/compiler/inKeywordTypeguard.ts(150,16): error TS2339: Property 'ontouchstart' does not exist on type 'never'. +tests/cases/compiler/inKeywordTypeguard.ts(155,16): error TS2322: Type 'unknown' is not assignable to type 'object'. +tests/cases/compiler/inKeywordTypeguard.ts(158,21): error TS2322: Type 'unknown' is not assignable to type 'object'. +tests/cases/compiler/inKeywordTypeguard.ts(183,16): error TS2322: Type 'T' is not assignable to type 'object'. +tests/cases/compiler/inKeywordTypeguard.ts(186,21): error TS2322: Type 'T' is not assignable to type 'object'. -==== tests/cases/compiler/inKeywordTypeguard.ts (17 errors) ==== +==== tests/cases/compiler/inKeywordTypeguard.ts (22 errors) ==== class A { a: string; } class B { b: string; } @@ -85,10 +92,12 @@ tests/cases/compiler/inKeywordTypeguard.ts(94,26): error TS2339: Property 'a' do if ("c" in x) { x.a(); ~ -!!! error TS2339: Property 'a' does not exist on type 'never'. +!!! error TS2339: Property 'a' does not exist on type '(AWithMethod | BWithMethod) & Record<"c", unknown>'. +!!! error TS2339: Property 'a' does not exist on type 'BWithMethod & Record<"c", unknown>'. x.b(); ~ -!!! error TS2339: Property 'b' does not exist on type 'never'. +!!! error TS2339: Property 'b' does not exist on type '(AWithMethod | BWithMethod) & Record<"c", unknown>'. +!!! error TS2339: Property 'b' does not exist on type 'AWithMethod & Record<"c", unknown>'. } else { x.a(); ~ @@ -210,6 +219,145 @@ tests/cases/compiler/inKeywordTypeguard.ts(94,26): error TS2339: Property 'a' do window.ontouchstart } else { window.ontouchstart + ~~~~~~~~~~~~ +!!! error TS2339: Property 'ontouchstart' does not exist on type 'never'. + } + } + + function f1(x: unknown) { + if ("a" in x) { + ~ +!!! error TS2322: Type 'unknown' is not assignable to type 'object'. + x.a; + } + if (x && "a" in x) { + ~ +!!! error TS2322: Type 'unknown' is not assignable to type 'object'. + x.a; + } + if (x && typeof x === "object" && "a" in x) { + x.a; + } + if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) { + x.a; + x.b; + x.c; + } + } + + function f2(x: object) { + if ("a" in x) { + x.a; + } + if ("a" in x && "b" in x && "c" in x) { + x.a; + x.b; + x.c; + } + } + + function f3(x: T) { + if ("a" in x) { + ~ +!!! error TS2322: Type 'T' is not assignable to type 'object'. +!!! related TS2208 tests/cases/compiler/inKeywordTypeguard.ts:182:13: This type parameter might need an `extends object` constraint. + x.a; + } + if (x && "a" in x) { + ~ +!!! error TS2322: Type 'T' is not assignable to type 'object'. +!!! related TS2208 tests/cases/compiler/inKeywordTypeguard.ts:182:13: This type parameter might need an `extends object` constraint. + x.a; + } + if (x && typeof x === "object" && "a" in x) { + x.a; + } + if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) { + x.a; + x.b; + x.c; + } + } + + function f4(x: { a: string }) { + if ("a" in x) { + x.a; + } + if ("a" in x && "b" in x && "c" in x) { + x.a; + x.b; + x.c; + } + } + + function f5(x: { a: string } | { b: string }) { + if ("a" in x) { + x; // { a: string } + } + else if ("b" in x) { + x; // { b: string } + } + else { + x; // never + } + } + + function f6(x: { a: string } | { b: string }) { + if ("a" in x) { + x; // { a: string } + } + else if ("a" in x) { + x; // { b: string } & Record<"a", unknown> + } + else { + x; // { b: string } + } + } + + // Object and corresponding intersection should narrow the same + + function f7(x: { a: string, b: number }, y: { a: string } & { b: number }) { + if ("a" in x) { + x; + } + else { + x; // never + } + if ("a" in y) { + y; + } + else { + y; // never + } + } + + const sym = Symbol(); + + function f8(x: object) { + if ("a" in x && 1 in x && sym in x) { + x.a; + x["a"]; + x[1]; + x["1"]; + x[sym]; + } + } + + function f9(x: object) { + if ("a" in x && "1" in x && sym in x) { + x.a; + x["a"]; + x[1]; + x["1"]; + x[sym]; + } + } + + // Repro from #50639 + + function foo(value: A) { + if (typeof value === "object" && value !== null && "prop" in value) { + value; // A & object & Record<"prop", unknown> } } \ No newline at end of file diff --git a/tests/baselines/reference/inKeywordTypeguard(strict=false).js b/tests/baselines/reference/inKeywordTypeguard(strict=false).js new file mode 100644 index 0000000000000..08e5a870d5ab1 --- /dev/null +++ b/tests/baselines/reference/inKeywordTypeguard(strict=false).js @@ -0,0 +1,541 @@ +//// [inKeywordTypeguard.ts] +class A { a: string; } +class B { b: string; } + +function negativeClassesTest(x: A | B) { + if ("a" in x) { + x.b = "1"; + } else { + x.a = "1"; + } +} + +function positiveClassesTest(x: A | B) { + if ("a" in x) { + x.b = "1"; + } else { + x.a = "1"; + } +} + +class AWithOptionalProp { a?: string; } +class BWithOptionalProp { b?: string; } + +function positiveTestClassesWithOptionalProperties(x: AWithOptionalProp | BWithOptionalProp) { + if ("a" in x) { + x.a = "1"; + } else { + x.b = "1"; + } +} + +class AWithMethod { + a(): string { return ""; } +} + +class BWithMethod { + b(): string { return ""; } +} + +function negativeTestClassesWithMembers(x: AWithMethod | BWithMethod) { + if ("a" in x) { + x.a(); + x.b(); + } else { + } +} + +function negativeTestClassesWithMemberMissingInBothClasses(x: AWithMethod | BWithMethod) { + if ("c" in x) { + x.a(); + x.b(); + } else { + x.a(); + x.b(); + } +} + +class C { a: string; } +class D { a: string; } + +function negativeMultipleClassesTest(x: A | B | C | D) { + if ("a" in x) { + x.b = "1"; + } else { + x.a = "1"; + } +} + +class ClassWithUnionProp { prop: A | B } + +function negativePropTest(x: ClassWithUnionProp) { + if ("a" in x.prop) { + let y: string = x.prop.b; + } else { + let z: string = x.prop.a; + } +} + +class NegativeClassTest { + protected prop: A | B; + inThis() { + if ("a" in this.prop) { + let z: number = this.prop.b; + } else { + let y: string = this.prop.a; + } + } +} + +class UnreachableCodeDetection { + a: string; + inThis() { + if ("a" in this) { + } else { + let y = this.a; + } + } +} + +function positiveIntersectionTest(x: { a: string } & { b: string }) { + if ("a" in x) { + let s: string = x.a; + } else { + let n: never = x; + } +} + +// Repro from #38608 +declare const error: Error; +if ('extra' in error) { + error // Still Error +} else { + error // Error +} + +function narrowsToNever(x: { l: number } | { r: number }) { + let v: number; + if ("l" in x) { + v = x.l; + } + else if ("r" in x) { + v = x.r; + } + else { + v = x + } + return v; +} + +type AOrB = { aProp: number } | { bProp: number }; +declare function isAOrB(x: unknown): x is AOrB; + +declare var x: unknown; +if (isAOrB(x)) { + if ("aProp" in x) { + x.aProp; + } + else if ("bProp" in x) { + x.bProp; + } + // x is never because of the type predicate from unknown + else if ("cProp" in x) { + const _never: never = x; + } +} + +function negativeIntersectionTest() { + if ("ontouchstart" in window) { + window.ontouchstart + } else { + window.ontouchstart + } +} + +function f1(x: unknown) { + if ("a" in x) { + x.a; + } + if (x && "a" in x) { + x.a; + } + if (x && typeof x === "object" && "a" in x) { + x.a; + } + if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) { + x.a; + x.b; + x.c; + } +} + +function f2(x: object) { + if ("a" in x) { + x.a; + } + if ("a" in x && "b" in x && "c" in x) { + x.a; + x.b; + x.c; + } +} + +function f3(x: T) { + if ("a" in x) { + x.a; + } + if (x && "a" in x) { + x.a; + } + if (x && typeof x === "object" && "a" in x) { + x.a; + } + if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) { + x.a; + x.b; + x.c; + } +} + +function f4(x: { a: string }) { + if ("a" in x) { + x.a; + } + if ("a" in x && "b" in x && "c" in x) { + x.a; + x.b; + x.c; + } +} + +function f5(x: { a: string } | { b: string }) { + if ("a" in x) { + x; // { a: string } + } + else if ("b" in x) { + x; // { b: string } + } + else { + x; // never + } +} + +function f6(x: { a: string } | { b: string }) { + if ("a" in x) { + x; // { a: string } + } + else if ("a" in x) { + x; // { b: string } & Record<"a", unknown> + } + else { + x; // { b: string } + } +} + +// Object and corresponding intersection should narrow the same + +function f7(x: { a: string, b: number }, y: { a: string } & { b: number }) { + if ("a" in x) { + x; + } + else { + x; // never + } + if ("a" in y) { + y; + } + else { + y; // never + } +} + +const sym = Symbol(); + +function f8(x: object) { + if ("a" in x && 1 in x && sym in x) { + x.a; + x["a"]; + x[1]; + x["1"]; + x[sym]; + } +} + +function f9(x: object) { + if ("a" in x && "1" in x && sym in x) { + x.a; + x["a"]; + x[1]; + x["1"]; + x[sym]; + } +} + +// Repro from #50639 + +function foo(value: A) { + if (typeof value === "object" && value !== null && "prop" in value) { + value; // A & object & Record<"prop", unknown> + } +} + + +//// [inKeywordTypeguard.js] +class A { +} +class B { +} +function negativeClassesTest(x) { + if ("a" in x) { + x.b = "1"; + } + else { + x.a = "1"; + } +} +function positiveClassesTest(x) { + if ("a" in x) { + x.b = "1"; + } + else { + x.a = "1"; + } +} +class AWithOptionalProp { +} +class BWithOptionalProp { +} +function positiveTestClassesWithOptionalProperties(x) { + if ("a" in x) { + x.a = "1"; + } + else { + x.b = "1"; + } +} +class AWithMethod { + a() { return ""; } +} +class BWithMethod { + b() { return ""; } +} +function negativeTestClassesWithMembers(x) { + if ("a" in x) { + x.a(); + x.b(); + } + else { + } +} +function negativeTestClassesWithMemberMissingInBothClasses(x) { + if ("c" in x) { + x.a(); + x.b(); + } + else { + x.a(); + x.b(); + } +} +class C { +} +class D { +} +function negativeMultipleClassesTest(x) { + if ("a" in x) { + x.b = "1"; + } + else { + x.a = "1"; + } +} +class ClassWithUnionProp { +} +function negativePropTest(x) { + if ("a" in x.prop) { + let y = x.prop.b; + } + else { + let z = x.prop.a; + } +} +class NegativeClassTest { + inThis() { + if ("a" in this.prop) { + let z = this.prop.b; + } + else { + let y = this.prop.a; + } + } +} +class UnreachableCodeDetection { + inThis() { + if ("a" in this) { + } + else { + let y = this.a; + } + } +} +function positiveIntersectionTest(x) { + if ("a" in x) { + let s = x.a; + } + else { + let n = x; + } +} +if ('extra' in error) { + error; // Still Error +} +else { + error; // Error +} +function narrowsToNever(x) { + let v; + if ("l" in x) { + v = x.l; + } + else if ("r" in x) { + v = x.r; + } + else { + v = x; + } + return v; +} +if (isAOrB(x)) { + if ("aProp" in x) { + x.aProp; + } + else if ("bProp" in x) { + x.bProp; + } + // x is never because of the type predicate from unknown + else if ("cProp" in x) { + const _never = x; + } +} +function negativeIntersectionTest() { + if ("ontouchstart" in window) { + window.ontouchstart; + } + else { + window.ontouchstart; + } +} +function f1(x) { + if ("a" in x) { + x.a; + } + if (x && "a" in x) { + x.a; + } + if (x && typeof x === "object" && "a" in x) { + x.a; + } + if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) { + x.a; + x.b; + x.c; + } +} +function f2(x) { + if ("a" in x) { + x.a; + } + if ("a" in x && "b" in x && "c" in x) { + x.a; + x.b; + x.c; + } +} +function f3(x) { + if ("a" in x) { + x.a; + } + if (x && "a" in x) { + x.a; + } + if (x && typeof x === "object" && "a" in x) { + x.a; + } + if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) { + x.a; + x.b; + x.c; + } +} +function f4(x) { + if ("a" in x) { + x.a; + } + if ("a" in x && "b" in x && "c" in x) { + x.a; + x.b; + x.c; + } +} +function f5(x) { + if ("a" in x) { + x; // { a: string } + } + else if ("b" in x) { + x; // { b: string } + } + else { + x; // never + } +} +function f6(x) { + if ("a" in x) { + x; // { a: string } + } + else if ("a" in x) { + x; // { b: string } & Record<"a", unknown> + } + else { + x; // { b: string } + } +} +// Object and corresponding intersection should narrow the same +function f7(x, y) { + if ("a" in x) { + x; + } + else { + x; // never + } + if ("a" in y) { + y; + } + else { + y; // never + } +} +const sym = Symbol(); +function f8(x) { + if ("a" in x && 1 in x && sym in x) { + x.a; + x["a"]; + x[1]; + x["1"]; + x[sym]; + } +} +function f9(x) { + if ("a" in x && "1" in x && sym in x) { + x.a; + x["a"]; + x[1]; + x["1"]; + x[sym]; + } +} +// Repro from #50639 +function foo(value) { + if (typeof value === "object" && value !== null && "prop" in value) { + value; // A & object & Record<"prop", unknown> + } +} diff --git a/tests/baselines/reference/inKeywordTypeguard.symbols b/tests/baselines/reference/inKeywordTypeguard(strict=false).symbols similarity index 55% rename from tests/baselines/reference/inKeywordTypeguard.symbols rename to tests/baselines/reference/inKeywordTypeguard(strict=false).symbols index 91d9c42597740..33ff5a5749fca 100644 --- a/tests/baselines/reference/inKeywordTypeguard.symbols +++ b/tests/baselines/reference/inKeywordTypeguard(strict=false).symbols @@ -371,9 +371,357 @@ function negativeIntersectionTest() { } else { window.ontouchstart ->window.ontouchstart : Symbol(ontouchstart, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --)) >window : Symbol(window, Decl(lib.dom.d.ts, --, --)) ->ontouchstart : Symbol(ontouchstart, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --)) + } +} + +function f1(x: unknown) { +>f1 : Symbol(f1, Decl(inKeywordTypeguard.ts, 151, 1)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) + + if ("a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) + + x.a; +>x.a : Symbol(a) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) +>a : Symbol(a) + } + if (x && "a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) + + x.a; +>x.a : Symbol(a) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) +>a : Symbol(a) + } + if (x && typeof x === "object" && "a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) + + x.a; +>x.a : Symbol(a) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) +>a : Symbol(a) + } + if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) + + x.a; +>x.a : Symbol(a) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) +>a : Symbol(a) + + x.b; +>x.b : Symbol(b) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) +>b : Symbol(b) + + x.c; +>x.c : Symbol(c) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) +>c : Symbol(c) + } +} + +function f2(x: object) { +>f2 : Symbol(f2, Decl(inKeywordTypeguard.ts, 168, 1)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12)) + + if ("a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12)) + + x.a; +>x.a : Symbol(a) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12)) +>a : Symbol(a) + } + if ("a" in x && "b" in x && "c" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12)) + + x.a; +>x.a : Symbol(a) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12)) +>a : Symbol(a) + + x.b; +>x.b : Symbol(b) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12)) +>b : Symbol(b) + + x.c; +>x.c : Symbol(c) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12)) +>c : Symbol(c) + } +} + +function f3(x: T) { +>f3 : Symbol(f3, Decl(inKeywordTypeguard.ts, 179, 1)) +>T : Symbol(T, Decl(inKeywordTypeguard.ts, 181, 12)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) +>T : Symbol(T, Decl(inKeywordTypeguard.ts, 181, 12)) + + if ("a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) + + x.a; +>x.a : Symbol(a) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) +>a : Symbol(a) + } + if (x && "a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) + + x.a; +>x.a : Symbol(a) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) +>a : Symbol(a) + } + if (x && typeof x === "object" && "a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) + + x.a; +>x.a : Symbol(a) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) +>a : Symbol(a) + } + if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) + + x.a; +>x.a : Symbol(a) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) +>a : Symbol(a) + + x.b; +>x.b : Symbol(b) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) +>b : Symbol(b) + + x.c; +>x.c : Symbol(c) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) +>c : Symbol(c) + } +} + +function f4(x: { a: string }) { +>f4 : Symbol(f4, Decl(inKeywordTypeguard.ts, 196, 1)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12)) +>a : Symbol(a, Decl(inKeywordTypeguard.ts, 198, 16)) + + if ("a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12)) + + x.a; +>x.a : Symbol(a, Decl(inKeywordTypeguard.ts, 198, 16)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12)) +>a : Symbol(a, Decl(inKeywordTypeguard.ts, 198, 16)) + } + if ("a" in x && "b" in x && "c" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12)) + + x.a; +>x.a : Symbol(a, Decl(inKeywordTypeguard.ts, 198, 16)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12)) +>a : Symbol(a, Decl(inKeywordTypeguard.ts, 198, 16)) + + x.b; +>x.b : Symbol(b) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12)) +>b : Symbol(b) + + x.c; +>x.c : Symbol(c) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12)) +>c : Symbol(c) + } +} + +function f5(x: { a: string } | { b: string }) { +>f5 : Symbol(f5, Decl(inKeywordTypeguard.ts, 207, 1)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 209, 12)) +>a : Symbol(a, Decl(inKeywordTypeguard.ts, 209, 16)) +>b : Symbol(b, Decl(inKeywordTypeguard.ts, 209, 32)) + + if ("a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 209, 12)) + + x; // { a: string } +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 209, 12)) + } + else if ("b" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 209, 12)) + + x; // { b: string } +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 209, 12)) + } + else { + x; // never +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 209, 12)) + } +} + +function f6(x: { a: string } | { b: string }) { +>f6 : Symbol(f6, Decl(inKeywordTypeguard.ts, 219, 1)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 221, 12)) +>a : Symbol(a, Decl(inKeywordTypeguard.ts, 221, 16)) +>b : Symbol(b, Decl(inKeywordTypeguard.ts, 221, 32)) + + if ("a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 221, 12)) + + x; // { a: string } +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 221, 12)) + } + else if ("a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 221, 12)) + + x; // { b: string } & Record<"a", unknown> +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 221, 12)) + } + else { + x; // { b: string } +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 221, 12)) + } +} + +// Object and corresponding intersection should narrow the same + +function f7(x: { a: string, b: number }, y: { a: string } & { b: number }) { +>f7 : Symbol(f7, Decl(inKeywordTypeguard.ts, 231, 1)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 235, 12)) +>a : Symbol(a, Decl(inKeywordTypeguard.ts, 235, 16)) +>b : Symbol(b, Decl(inKeywordTypeguard.ts, 235, 27)) +>y : Symbol(y, Decl(inKeywordTypeguard.ts, 235, 40)) +>a : Symbol(a, Decl(inKeywordTypeguard.ts, 235, 45)) +>b : Symbol(b, Decl(inKeywordTypeguard.ts, 235, 61)) + + if ("a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 235, 12)) + + x; +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 235, 12)) + } + else { + x; // never +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 235, 12)) + } + if ("a" in y) { +>y : Symbol(y, Decl(inKeywordTypeguard.ts, 235, 40)) + + y; +>y : Symbol(y, Decl(inKeywordTypeguard.ts, 235, 40)) + } + else { + y; // never +>y : Symbol(y, Decl(inKeywordTypeguard.ts, 235, 40)) + } +} + +const sym = Symbol(); +>sym : Symbol(sym, Decl(inKeywordTypeguard.ts, 250, 5)) +>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) + +function f8(x: object) { +>f8 : Symbol(f8, Decl(inKeywordTypeguard.ts, 250, 21)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12)) + + if ("a" in x && 1 in x && sym in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12)) +>sym : Symbol(sym, Decl(inKeywordTypeguard.ts, 250, 5)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12)) + + x.a; +>x.a : Symbol(a) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12)) +>a : Symbol(a) + + x["a"]; +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12)) +>"a" : Symbol(a) + + x[1]; +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12)) +>1 : Symbol(1) + + x["1"]; +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12)) +>"1" : Symbol(1) + + x[sym]; +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12)) +>sym : Symbol(sym, Decl(inKeywordTypeguard.ts, 250, 5)) + } +} + +function f9(x: object) { +>f9 : Symbol(f9, Decl(inKeywordTypeguard.ts, 260, 1)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12)) + + if ("a" in x && "1" in x && sym in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12)) +>sym : Symbol(sym, Decl(inKeywordTypeguard.ts, 250, 5)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12)) + + x.a; +>x.a : Symbol(a) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12)) +>a : Symbol(a) + + x["a"]; +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12)) +>"a" : Symbol(a) + + x[1]; +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12)) +>1 : Symbol(1) + + x["1"]; +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12)) +>"1" : Symbol(1) + + x[sym]; +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12)) +>sym : Symbol(sym, Decl(inKeywordTypeguard.ts, 250, 5)) + } +} + +// Repro from #50639 + +function foo(value: A) { +>foo : Symbol(foo, Decl(inKeywordTypeguard.ts, 270, 1)) +>A : Symbol(A, Decl(inKeywordTypeguard.ts, 274, 13)) +>value : Symbol(value, Decl(inKeywordTypeguard.ts, 274, 16)) +>A : Symbol(A, Decl(inKeywordTypeguard.ts, 274, 13)) + + if (typeof value === "object" && value !== null && "prop" in value) { +>value : Symbol(value, Decl(inKeywordTypeguard.ts, 274, 16)) +>value : Symbol(value, Decl(inKeywordTypeguard.ts, 274, 16)) +>value : Symbol(value, Decl(inKeywordTypeguard.ts, 274, 16)) + + value; // A & object & Record<"prop", unknown> +>value : Symbol(value, Decl(inKeywordTypeguard.ts, 274, 16)) } } diff --git a/tests/baselines/reference/inKeywordTypeguard(strict=false).types b/tests/baselines/reference/inKeywordTypeguard(strict=false).types new file mode 100644 index 0000000000000..e8e414520d3cf --- /dev/null +++ b/tests/baselines/reference/inKeywordTypeguard(strict=false).types @@ -0,0 +1,914 @@ +=== tests/cases/compiler/inKeywordTypeguard.ts === +class A { a: string; } +>A : A +>a : string + +class B { b: string; } +>B : B +>b : string + +function negativeClassesTest(x: A | B) { +>negativeClassesTest : (x: A | B) => void +>x : A | B + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : A | B + + x.b = "1"; +>x.b = "1" : "1" +>x.b : any +>x : A +>b : any +>"1" : "1" + + } else { + x.a = "1"; +>x.a = "1" : "1" +>x.a : any +>x : B +>a : any +>"1" : "1" + } +} + +function positiveClassesTest(x: A | B) { +>positiveClassesTest : (x: A | B) => void +>x : A | B + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : A | B + + x.b = "1"; +>x.b = "1" : "1" +>x.b : any +>x : A +>b : any +>"1" : "1" + + } else { + x.a = "1"; +>x.a = "1" : "1" +>x.a : any +>x : B +>a : any +>"1" : "1" + } +} + +class AWithOptionalProp { a?: string; } +>AWithOptionalProp : AWithOptionalProp +>a : string + +class BWithOptionalProp { b?: string; } +>BWithOptionalProp : BWithOptionalProp +>b : string + +function positiveTestClassesWithOptionalProperties(x: AWithOptionalProp | BWithOptionalProp) { +>positiveTestClassesWithOptionalProperties : (x: AWithOptionalProp | BWithOptionalProp) => void +>x : AWithOptionalProp | BWithOptionalProp + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : AWithOptionalProp | BWithOptionalProp + + x.a = "1"; +>x.a = "1" : "1" +>x.a : string +>x : AWithOptionalProp +>a : string +>"1" : "1" + + } else { + x.b = "1"; +>x.b = "1" : "1" +>x.b : any +>x : AWithOptionalProp | BWithOptionalProp +>b : any +>"1" : "1" + } +} + +class AWithMethod { +>AWithMethod : AWithMethod + + a(): string { return ""; } +>a : () => string +>"" : "" +} + +class BWithMethod { +>BWithMethod : BWithMethod + + b(): string { return ""; } +>b : () => string +>"" : "" +} + +function negativeTestClassesWithMembers(x: AWithMethod | BWithMethod) { +>negativeTestClassesWithMembers : (x: AWithMethod | BWithMethod) => void +>x : AWithMethod | BWithMethod + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : AWithMethod | BWithMethod + + x.a(); +>x.a() : string +>x.a : () => string +>x : AWithMethod +>a : () => string + + x.b(); +>x.b() : any +>x.b : any +>x : AWithMethod +>b : any + + } else { + } +} + +function negativeTestClassesWithMemberMissingInBothClasses(x: AWithMethod | BWithMethod) { +>negativeTestClassesWithMemberMissingInBothClasses : (x: AWithMethod | BWithMethod) => void +>x : AWithMethod | BWithMethod + + if ("c" in x) { +>"c" in x : boolean +>"c" : "c" +>x : AWithMethod | BWithMethod + + x.a(); +>x.a() : any +>x.a : any +>x : (AWithMethod | BWithMethod) & Record<"c", unknown> +>a : any + + x.b(); +>x.b() : any +>x.b : any +>x : (AWithMethod | BWithMethod) & Record<"c", unknown> +>b : any + + } else { + x.a(); +>x.a() : any +>x.a : any +>x : AWithMethod | BWithMethod +>a : any + + x.b(); +>x.b() : any +>x.b : any +>x : AWithMethod | BWithMethod +>b : any + } +} + +class C { a: string; } +>C : C +>a : string + +class D { a: string; } +>D : D +>a : string + +function negativeMultipleClassesTest(x: A | B | C | D) { +>negativeMultipleClassesTest : (x: A | B | C | D) => void +>x : A | B | C | D + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : A | B | C | D + + x.b = "1"; +>x.b = "1" : "1" +>x.b : any +>x : A | C | D +>b : any +>"1" : "1" + + } else { + x.a = "1"; +>x.a = "1" : "1" +>x.a : any +>x : B +>a : any +>"1" : "1" + } +} + +class ClassWithUnionProp { prop: A | B } +>ClassWithUnionProp : ClassWithUnionProp +>prop : A | B + +function negativePropTest(x: ClassWithUnionProp) { +>negativePropTest : (x: ClassWithUnionProp) => void +>x : ClassWithUnionProp + + if ("a" in x.prop) { +>"a" in x.prop : boolean +>"a" : "a" +>x.prop : A | B +>x : ClassWithUnionProp +>prop : A | B + + let y: string = x.prop.b; +>y : string +>x.prop.b : any +>x.prop : A +>x : ClassWithUnionProp +>prop : A +>b : any + + } else { + let z: string = x.prop.a; +>z : string +>x.prop.a : any +>x.prop : B +>x : ClassWithUnionProp +>prop : B +>a : any + } +} + +class NegativeClassTest { +>NegativeClassTest : NegativeClassTest + + protected prop: A | B; +>prop : A | B + + inThis() { +>inThis : () => void + + if ("a" in this.prop) { +>"a" in this.prop : boolean +>"a" : "a" +>this.prop : A | B +>this : this +>prop : A | B + + let z: number = this.prop.b; +>z : number +>this.prop.b : any +>this.prop : A +>this : this +>prop : A +>b : any + + } else { + let y: string = this.prop.a; +>y : string +>this.prop.a : any +>this.prop : B +>this : this +>prop : B +>a : any + } + } +} + +class UnreachableCodeDetection { +>UnreachableCodeDetection : UnreachableCodeDetection + + a: string; +>a : string + + inThis() { +>inThis : () => void + + if ("a" in this) { +>"a" in this : boolean +>"a" : "a" +>this : this + + } else { + let y = this.a; +>y : any +>this.a : any +>this : never +>a : any + } + } +} + +function positiveIntersectionTest(x: { a: string } & { b: string }) { +>positiveIntersectionTest : (x: { a: string;} & { b: string;}) => void +>x : { a: string; } & { b: string; } +>a : string +>b : string + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : { a: string; } & { b: string; } + + let s: string = x.a; +>s : string +>x.a : string +>x : { a: string; } & { b: string; } +>a : string + + } else { + let n: never = x; +>n : never +>x : never + } +} + +// Repro from #38608 +declare const error: Error; +>error : Error + +if ('extra' in error) { +>'extra' in error : boolean +>'extra' : "extra" +>error : Error + + error // Still Error +>error : Error & Record<"extra", unknown> + +} else { + error // Error +>error : Error +} + +function narrowsToNever(x: { l: number } | { r: number }) { +>narrowsToNever : (x: { l: number;} | { r: number;}) => number +>x : { l: number; } | { r: number; } +>l : number +>r : number + + let v: number; +>v : number + + if ("l" in x) { +>"l" in x : boolean +>"l" : "l" +>x : { l: number; } | { r: number; } + + v = x.l; +>v = x.l : number +>v : number +>x.l : number +>x : { l: number; } +>l : number + } + else if ("r" in x) { +>"r" in x : boolean +>"r" : "r" +>x : { r: number; } + + v = x.r; +>v = x.r : number +>v : number +>x.r : number +>x : { r: number; } +>r : number + } + else { + v = x +>v = x : never +>v : number +>x : never + } + return v; +>v : number +} + +type AOrB = { aProp: number } | { bProp: number }; +>AOrB : { aProp: number; } | { bProp: number; } +>aProp : number +>bProp : number + +declare function isAOrB(x: unknown): x is AOrB; +>isAOrB : (x: unknown) => x is AOrB +>x : unknown + +declare var x: unknown; +>x : unknown + +if (isAOrB(x)) { +>isAOrB(x) : boolean +>isAOrB : (x: unknown) => x is AOrB +>x : unknown + + if ("aProp" in x) { +>"aProp" in x : boolean +>"aProp" : "aProp" +>x : AOrB + + x.aProp; +>x.aProp : number +>x : { aProp: number; } +>aProp : number + } + else if ("bProp" in x) { +>"bProp" in x : boolean +>"bProp" : "bProp" +>x : { bProp: number; } + + x.bProp; +>x.bProp : number +>x : { bProp: number; } +>bProp : number + } + // x is never because of the type predicate from unknown + else if ("cProp" in x) { +>"cProp" in x : boolean +>"cProp" : "cProp" +>x : never + + const _never: never = x; +>_never : never +>x : never + } +} + +function negativeIntersectionTest() { +>negativeIntersectionTest : () => void + + if ("ontouchstart" in window) { +>"ontouchstart" in window : boolean +>"ontouchstart" : "ontouchstart" +>window : Window & typeof globalThis + + window.ontouchstart +>window.ontouchstart : ((this: GlobalEventHandlers, ev: TouchEvent) => any) & ((this: Window, ev: TouchEvent) => any) +>window : Window & typeof globalThis +>ontouchstart : ((this: GlobalEventHandlers, ev: TouchEvent) => any) & ((this: Window, ev: TouchEvent) => any) + + } else { + window.ontouchstart +>window.ontouchstart : any +>window : never +>ontouchstart : any + } +} + +function f1(x: unknown) { +>f1 : (x: unknown) => void +>x : unknown + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : unknown + + x.a; +>x.a : unknown +>x : Record<"a", unknown> +>a : unknown + } + if (x && "a" in x) { +>x && "a" in x : boolean +>x : unknown +>"a" in x : boolean +>"a" : "a" +>x : unknown + + x.a; +>x.a : unknown +>x : Record<"a", unknown> +>a : unknown + } + if (x && typeof x === "object" && "a" in x) { +>x && typeof x === "object" && "a" in x : boolean +>x && typeof x === "object" : boolean +>x : unknown +>typeof x === "object" : boolean +>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>x : unknown +>"object" : "object" +>"a" in x : boolean +>"a" : "a" +>x : object + + x.a; +>x.a : unknown +>x : object & Record<"a", unknown> +>a : unknown + } + if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) { +>x && typeof x === "object" && "a" in x && "b" in x && "c" in x : boolean +>x && typeof x === "object" && "a" in x && "b" in x : boolean +>x && typeof x === "object" && "a" in x : boolean +>x && typeof x === "object" : boolean +>x : unknown +>typeof x === "object" : boolean +>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>x : unknown +>"object" : "object" +>"a" in x : boolean +>"a" : "a" +>x : object +>"b" in x : boolean +>"b" : "b" +>x : object & Record<"a", unknown> +>"c" in x : boolean +>"c" : "c" +>x : object & Record<"a", unknown> & Record<"b", unknown> + + x.a; +>x.a : unknown +>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>a : unknown + + x.b; +>x.b : unknown +>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>b : unknown + + x.c; +>x.c : unknown +>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>c : unknown + } +} + +function f2(x: object) { +>f2 : (x: object) => void +>x : object + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : object + + x.a; +>x.a : unknown +>x : object & Record<"a", unknown> +>a : unknown + } + if ("a" in x && "b" in x && "c" in x) { +>"a" in x && "b" in x && "c" in x : boolean +>"a" in x && "b" in x : boolean +>"a" in x : boolean +>"a" : "a" +>x : object +>"b" in x : boolean +>"b" : "b" +>x : object & Record<"a", unknown> +>"c" in x : boolean +>"c" : "c" +>x : object & Record<"a", unknown> & Record<"b", unknown> + + x.a; +>x.a : unknown +>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>a : unknown + + x.b; +>x.b : unknown +>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>b : unknown + + x.c; +>x.c : unknown +>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>c : unknown + } +} + +function f3(x: T) { +>f3 : (x: T) => void +>x : T + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : T + + x.a; +>x.a : unknown +>x : T & Record<"a", unknown> +>a : unknown + } + if (x && "a" in x) { +>x && "a" in x : boolean +>x : T +>"a" in x : boolean +>"a" : "a" +>x : T + + x.a; +>x.a : unknown +>x : T & Record<"a", unknown> +>a : unknown + } + if (x && typeof x === "object" && "a" in x) { +>x && typeof x === "object" && "a" in x : boolean +>x && typeof x === "object" : boolean +>x : T +>typeof x === "object" : boolean +>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>x : T +>"object" : "object" +>"a" in x : boolean +>"a" : "a" +>x : T & object + + x.a; +>x.a : unknown +>x : T & object & Record<"a", unknown> +>a : unknown + } + if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) { +>x && typeof x === "object" && "a" in x && "b" in x && "c" in x : boolean +>x && typeof x === "object" && "a" in x && "b" in x : boolean +>x && typeof x === "object" && "a" in x : boolean +>x && typeof x === "object" : boolean +>x : T +>typeof x === "object" : boolean +>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>x : T +>"object" : "object" +>"a" in x : boolean +>"a" : "a" +>x : T & object +>"b" in x : boolean +>"b" : "b" +>x : T & object & Record<"a", unknown> +>"c" in x : boolean +>"c" : "c" +>x : T & object & Record<"a", unknown> & Record<"b", unknown> + + x.a; +>x.a : unknown +>x : T & object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>a : unknown + + x.b; +>x.b : unknown +>x : T & object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>b : unknown + + x.c; +>x.c : unknown +>x : T & object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>c : unknown + } +} + +function f4(x: { a: string }) { +>f4 : (x: { a: string;}) => void +>x : { a: string; } +>a : string + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : { a: string; } + + x.a; +>x.a : string +>x : { a: string; } +>a : string + } + if ("a" in x && "b" in x && "c" in x) { +>"a" in x && "b" in x && "c" in x : boolean +>"a" in x && "b" in x : boolean +>"a" in x : boolean +>"a" : "a" +>x : { a: string; } +>"b" in x : boolean +>"b" : "b" +>x : { a: string; } +>"c" in x : boolean +>"c" : "c" +>x : { a: string; } & Record<"b", unknown> + + x.a; +>x.a : string +>x : { a: string; } & Record<"b", unknown> & Record<"c", unknown> +>a : string + + x.b; +>x.b : unknown +>x : { a: string; } & Record<"b", unknown> & Record<"c", unknown> +>b : unknown + + x.c; +>x.c : unknown +>x : { a: string; } & Record<"b", unknown> & Record<"c", unknown> +>c : unknown + } +} + +function f5(x: { a: string } | { b: string }) { +>f5 : (x: { a: string;} | { b: string;}) => void +>x : { a: string; } | { b: string; } +>a : string +>b : string + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : { a: string; } | { b: string; } + + x; // { a: string } +>x : { a: string; } + } + else if ("b" in x) { +>"b" in x : boolean +>"b" : "b" +>x : { b: string; } + + x; // { b: string } +>x : { b: string; } + } + else { + x; // never +>x : never + } +} + +function f6(x: { a: string } | { b: string }) { +>f6 : (x: { a: string;} | { b: string;}) => void +>x : { a: string; } | { b: string; } +>a : string +>b : string + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : { a: string; } | { b: string; } + + x; // { a: string } +>x : { a: string; } + } + else if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : { b: string; } + + x; // { b: string } & Record<"a", unknown> +>x : { b: string; } & Record<"a", unknown> + } + else { + x; // { b: string } +>x : { b: string; } + } +} + +// Object and corresponding intersection should narrow the same + +function f7(x: { a: string, b: number }, y: { a: string } & { b: number }) { +>f7 : (x: { a: string; b: number;}, y: { a: string;} & { b: number;}) => void +>x : { a: string; b: number; } +>a : string +>b : number +>y : { a: string; } & { b: number; } +>a : string +>b : number + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : { a: string; b: number; } + + x; +>x : { a: string; b: number; } + } + else { + x; // never +>x : never + } + if ("a" in y) { +>"a" in y : boolean +>"a" : "a" +>y : { a: string; } & { b: number; } + + y; +>y : { a: string; } & { b: number; } + } + else { + y; // never +>y : never + } +} + +const sym = Symbol(); +>sym : unique symbol +>Symbol() : unique symbol +>Symbol : SymbolConstructor + +function f8(x: object) { +>f8 : (x: object) => void +>x : object + + if ("a" in x && 1 in x && sym in x) { +>"a" in x && 1 in x && sym in x : boolean +>"a" in x && 1 in x : boolean +>"a" in x : boolean +>"a" : "a" +>x : object +>1 in x : boolean +>1 : 1 +>x : object & Record<"a", unknown> +>sym in x : boolean +>sym : unique symbol +>x : object & Record<"a", unknown> & Record<1, unknown> + + x.a; +>x.a : unknown +>x : object & Record<"a", unknown> & Record<1, unknown> & Record +>a : unknown + + x["a"]; +>x["a"] : unknown +>x : object & Record<"a", unknown> & Record<1, unknown> & Record +>"a" : "a" + + x[1]; +>x[1] : unknown +>x : object & Record<"a", unknown> & Record<1, unknown> & Record +>1 : 1 + + x["1"]; +>x["1"] : unknown +>x : object & Record<"a", unknown> & Record<1, unknown> & Record +>"1" : "1" + + x[sym]; +>x[sym] : unknown +>x : object & Record<"a", unknown> & Record<1, unknown> & Record +>sym : unique symbol + } +} + +function f9(x: object) { +>f9 : (x: object) => void +>x : object + + if ("a" in x && "1" in x && sym in x) { +>"a" in x && "1" in x && sym in x : boolean +>"a" in x && "1" in x : boolean +>"a" in x : boolean +>"a" : "a" +>x : object +>"1" in x : boolean +>"1" : "1" +>x : object & Record<"a", unknown> +>sym in x : boolean +>sym : unique symbol +>x : object & Record<"a", unknown> & Record<"1", unknown> + + x.a; +>x.a : unknown +>x : object & Record<"a", unknown> & Record<"1", unknown> & Record +>a : unknown + + x["a"]; +>x["a"] : unknown +>x : object & Record<"a", unknown> & Record<"1", unknown> & Record +>"a" : "a" + + x[1]; +>x[1] : unknown +>x : object & Record<"a", unknown> & Record<"1", unknown> & Record +>1 : 1 + + x["1"]; +>x["1"] : unknown +>x : object & Record<"a", unknown> & Record<"1", unknown> & Record +>"1" : "1" + + x[sym]; +>x[sym] : unknown +>x : object & Record<"a", unknown> & Record<"1", unknown> & Record +>sym : unique symbol + } +} + +// Repro from #50639 + +function foo(value: A) { +>foo : (value: A) => void +>value : A + + if (typeof value === "object" && value !== null && "prop" in value) { +>typeof value === "object" && value !== null && "prop" in value : boolean +>typeof value === "object" && value !== null : boolean +>typeof value === "object" : boolean +>typeof value : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>value : A +>"object" : "object" +>value !== null : boolean +>value : A & object +>null : null +>"prop" in value : boolean +>"prop" : "prop" +>value : A & object + + value; // A & object & Record<"prop", unknown> +>value : A & object & Record<"prop", unknown> + } +} + diff --git a/tests/baselines/reference/inKeywordTypeguard(strict=true).errors.txt b/tests/baselines/reference/inKeywordTypeguard(strict=true).errors.txt new file mode 100644 index 0000000000000..dbae773cac10c --- /dev/null +++ b/tests/baselines/reference/inKeywordTypeguard(strict=true).errors.txt @@ -0,0 +1,383 @@ +tests/cases/compiler/inKeywordTypeguard.ts(1,11): error TS2564: Property 'a' has no initializer and is not definitely assigned in the constructor. +tests/cases/compiler/inKeywordTypeguard.ts(2,11): error TS2564: Property 'b' has no initializer and is not definitely assigned in the constructor. +tests/cases/compiler/inKeywordTypeguard.ts(6,11): error TS2339: Property 'b' does not exist on type 'A'. +tests/cases/compiler/inKeywordTypeguard.ts(8,11): error TS2339: Property 'a' does not exist on type 'B'. +tests/cases/compiler/inKeywordTypeguard.ts(14,11): error TS2339: Property 'b' does not exist on type 'A'. +tests/cases/compiler/inKeywordTypeguard.ts(16,11): error TS2339: Property 'a' does not exist on type 'B'. +tests/cases/compiler/inKeywordTypeguard.ts(27,11): error TS2339: Property 'b' does not exist on type 'AWithOptionalProp | BWithOptionalProp'. + Property 'b' does not exist on type 'AWithOptionalProp'. +tests/cases/compiler/inKeywordTypeguard.ts(42,11): error TS2339: Property 'b' does not exist on type 'AWithMethod'. +tests/cases/compiler/inKeywordTypeguard.ts(49,11): error TS2339: Property 'a' does not exist on type '(AWithMethod | BWithMethod) & Record<"c", unknown>'. + Property 'a' does not exist on type 'BWithMethod & Record<"c", unknown>'. +tests/cases/compiler/inKeywordTypeguard.ts(50,11): error TS2339: Property 'b' does not exist on type '(AWithMethod | BWithMethod) & Record<"c", unknown>'. + Property 'b' does not exist on type 'AWithMethod & Record<"c", unknown>'. +tests/cases/compiler/inKeywordTypeguard.ts(52,11): error TS2339: Property 'a' does not exist on type 'AWithMethod | BWithMethod'. + Property 'a' does not exist on type 'BWithMethod'. +tests/cases/compiler/inKeywordTypeguard.ts(53,11): error TS2339: Property 'b' does not exist on type 'AWithMethod | BWithMethod'. + Property 'b' does not exist on type 'AWithMethod'. +tests/cases/compiler/inKeywordTypeguard.ts(57,11): error TS2564: Property 'a' has no initializer and is not definitely assigned in the constructor. +tests/cases/compiler/inKeywordTypeguard.ts(58,11): error TS2564: Property 'a' has no initializer and is not definitely assigned in the constructor. +tests/cases/compiler/inKeywordTypeguard.ts(62,11): error TS2339: Property 'b' does not exist on type 'A | C | D'. + Property 'b' does not exist on type 'A'. +tests/cases/compiler/inKeywordTypeguard.ts(64,11): error TS2339: Property 'a' does not exist on type 'B'. +tests/cases/compiler/inKeywordTypeguard.ts(68,28): error TS2564: Property 'prop' has no initializer and is not definitely assigned in the constructor. +tests/cases/compiler/inKeywordTypeguard.ts(72,32): error TS2339: Property 'b' does not exist on type 'A'. +tests/cases/compiler/inKeywordTypeguard.ts(74,32): error TS2339: Property 'a' does not exist on type 'B'. +tests/cases/compiler/inKeywordTypeguard.ts(79,15): error TS2564: Property 'prop' has no initializer and is not definitely assigned in the constructor. +tests/cases/compiler/inKeywordTypeguard.ts(82,39): error TS2339: Property 'b' does not exist on type 'A'. +tests/cases/compiler/inKeywordTypeguard.ts(84,39): error TS2339: Property 'a' does not exist on type 'B'. +tests/cases/compiler/inKeywordTypeguard.ts(90,5): error TS2564: Property 'a' has no initializer and is not definitely assigned in the constructor. +tests/cases/compiler/inKeywordTypeguard.ts(94,26): error TS2339: Property 'a' does not exist on type 'never'. +tests/cases/compiler/inKeywordTypeguard.ts(150,16): error TS2339: Property 'ontouchstart' does not exist on type 'never'. +tests/cases/compiler/inKeywordTypeguard.ts(155,16): error TS18046: 'x' is of type 'unknown'. +tests/cases/compiler/inKeywordTypeguard.ts(158,21): error TS2638: Type '{}' may represent a primitive value, which is not permitted as the right operand of the 'in' operator. +tests/cases/compiler/inKeywordTypeguard.ts(183,16): error TS2322: Type 'T' is not assignable to type 'object'. +tests/cases/compiler/inKeywordTypeguard.ts(186,21): error TS2638: Type 'NonNullable' may represent a primitive value, which is not permitted as the right operand of the 'in' operator. + + +==== tests/cases/compiler/inKeywordTypeguard.ts (29 errors) ==== + class A { a: string; } + ~ +!!! error TS2564: Property 'a' has no initializer and is not definitely assigned in the constructor. + class B { b: string; } + ~ +!!! error TS2564: Property 'b' has no initializer and is not definitely assigned in the constructor. + + function negativeClassesTest(x: A | B) { + if ("a" in x) { + x.b = "1"; + ~ +!!! error TS2339: Property 'b' does not exist on type 'A'. + } else { + x.a = "1"; + ~ +!!! error TS2339: Property 'a' does not exist on type 'B'. + } + } + + function positiveClassesTest(x: A | B) { + if ("a" in x) { + x.b = "1"; + ~ +!!! error TS2339: Property 'b' does not exist on type 'A'. + } else { + x.a = "1"; + ~ +!!! error TS2339: Property 'a' does not exist on type 'B'. + } + } + + class AWithOptionalProp { a?: string; } + class BWithOptionalProp { b?: string; } + + function positiveTestClassesWithOptionalProperties(x: AWithOptionalProp | BWithOptionalProp) { + if ("a" in x) { + x.a = "1"; + } else { + x.b = "1"; + ~ +!!! error TS2339: Property 'b' does not exist on type 'AWithOptionalProp | BWithOptionalProp'. +!!! error TS2339: Property 'b' does not exist on type 'AWithOptionalProp'. + } + } + + class AWithMethod { + a(): string { return ""; } + } + + class BWithMethod { + b(): string { return ""; } + } + + function negativeTestClassesWithMembers(x: AWithMethod | BWithMethod) { + if ("a" in x) { + x.a(); + x.b(); + ~ +!!! error TS2339: Property 'b' does not exist on type 'AWithMethod'. + } else { + } + } + + function negativeTestClassesWithMemberMissingInBothClasses(x: AWithMethod | BWithMethod) { + if ("c" in x) { + x.a(); + ~ +!!! error TS2339: Property 'a' does not exist on type '(AWithMethod | BWithMethod) & Record<"c", unknown>'. +!!! error TS2339: Property 'a' does not exist on type 'BWithMethod & Record<"c", unknown>'. + x.b(); + ~ +!!! error TS2339: Property 'b' does not exist on type '(AWithMethod | BWithMethod) & Record<"c", unknown>'. +!!! error TS2339: Property 'b' does not exist on type 'AWithMethod & Record<"c", unknown>'. + } else { + x.a(); + ~ +!!! error TS2339: Property 'a' does not exist on type 'AWithMethod | BWithMethod'. +!!! error TS2339: Property 'a' does not exist on type 'BWithMethod'. + x.b(); + ~ +!!! error TS2339: Property 'b' does not exist on type 'AWithMethod | BWithMethod'. +!!! error TS2339: Property 'b' does not exist on type 'AWithMethod'. + } + } + + class C { a: string; } + ~ +!!! error TS2564: Property 'a' has no initializer and is not definitely assigned in the constructor. + class D { a: string; } + ~ +!!! error TS2564: Property 'a' has no initializer and is not definitely assigned in the constructor. + + function negativeMultipleClassesTest(x: A | B | C | D) { + if ("a" in x) { + x.b = "1"; + ~ +!!! error TS2339: Property 'b' does not exist on type 'A | C | D'. +!!! error TS2339: Property 'b' does not exist on type 'A'. + } else { + x.a = "1"; + ~ +!!! error TS2339: Property 'a' does not exist on type 'B'. + } + } + + class ClassWithUnionProp { prop: A | B } + ~~~~ +!!! error TS2564: Property 'prop' has no initializer and is not definitely assigned in the constructor. + + function negativePropTest(x: ClassWithUnionProp) { + if ("a" in x.prop) { + let y: string = x.prop.b; + ~ +!!! error TS2339: Property 'b' does not exist on type 'A'. + } else { + let z: string = x.prop.a; + ~ +!!! error TS2339: Property 'a' does not exist on type 'B'. + } + } + + class NegativeClassTest { + protected prop: A | B; + ~~~~ +!!! error TS2564: Property 'prop' has no initializer and is not definitely assigned in the constructor. + inThis() { + if ("a" in this.prop) { + let z: number = this.prop.b; + ~ +!!! error TS2339: Property 'b' does not exist on type 'A'. + } else { + let y: string = this.prop.a; + ~ +!!! error TS2339: Property 'a' does not exist on type 'B'. + } + } + } + + class UnreachableCodeDetection { + a: string; + ~ +!!! error TS2564: Property 'a' has no initializer and is not definitely assigned in the constructor. + inThis() { + if ("a" in this) { + } else { + let y = this.a; + ~ +!!! error TS2339: Property 'a' does not exist on type 'never'. + } + } + } + + function positiveIntersectionTest(x: { a: string } & { b: string }) { + if ("a" in x) { + let s: string = x.a; + } else { + let n: never = x; + } + } + + // Repro from #38608 + declare const error: Error; + if ('extra' in error) { + error // Still Error + } else { + error // Error + } + + function narrowsToNever(x: { l: number } | { r: number }) { + let v: number; + if ("l" in x) { + v = x.l; + } + else if ("r" in x) { + v = x.r; + } + else { + v = x + } + return v; + } + + type AOrB = { aProp: number } | { bProp: number }; + declare function isAOrB(x: unknown): x is AOrB; + + declare var x: unknown; + if (isAOrB(x)) { + if ("aProp" in x) { + x.aProp; + } + else if ("bProp" in x) { + x.bProp; + } + // x is never because of the type predicate from unknown + else if ("cProp" in x) { + const _never: never = x; + } + } + + function negativeIntersectionTest() { + if ("ontouchstart" in window) { + window.ontouchstart + } else { + window.ontouchstart + ~~~~~~~~~~~~ +!!! error TS2339: Property 'ontouchstart' does not exist on type 'never'. + } + } + + function f1(x: unknown) { + if ("a" in x) { + ~ +!!! error TS18046: 'x' is of type 'unknown'. + x.a; + } + if (x && "a" in x) { + ~ +!!! error TS2638: Type '{}' may represent a primitive value, which is not permitted as the right operand of the 'in' operator. + x.a; + } + if (x && typeof x === "object" && "a" in x) { + x.a; + } + if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) { + x.a; + x.b; + x.c; + } + } + + function f2(x: object) { + if ("a" in x) { + x.a; + } + if ("a" in x && "b" in x && "c" in x) { + x.a; + x.b; + x.c; + } + } + + function f3(x: T) { + if ("a" in x) { + ~ +!!! error TS2322: Type 'T' is not assignable to type 'object'. +!!! related TS2208 tests/cases/compiler/inKeywordTypeguard.ts:182:13: This type parameter might need an `extends object` constraint. + x.a; + } + if (x && "a" in x) { + ~ +!!! error TS2638: Type 'NonNullable' may represent a primitive value, which is not permitted as the right operand of the 'in' operator. + x.a; + } + if (x && typeof x === "object" && "a" in x) { + x.a; + } + if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) { + x.a; + x.b; + x.c; + } + } + + function f4(x: { a: string }) { + if ("a" in x) { + x.a; + } + if ("a" in x && "b" in x && "c" in x) { + x.a; + x.b; + x.c; + } + } + + function f5(x: { a: string } | { b: string }) { + if ("a" in x) { + x; // { a: string } + } + else if ("b" in x) { + x; // { b: string } + } + else { + x; // never + } + } + + function f6(x: { a: string } | { b: string }) { + if ("a" in x) { + x; // { a: string } + } + else if ("a" in x) { + x; // { b: string } & Record<"a", unknown> + } + else { + x; // { b: string } + } + } + + // Object and corresponding intersection should narrow the same + + function f7(x: { a: string, b: number }, y: { a: string } & { b: number }) { + if ("a" in x) { + x; + } + else { + x; // never + } + if ("a" in y) { + y; + } + else { + y; // never + } + } + + const sym = Symbol(); + + function f8(x: object) { + if ("a" in x && 1 in x && sym in x) { + x.a; + x["a"]; + x[1]; + x["1"]; + x[sym]; + } + } + + function f9(x: object) { + if ("a" in x && "1" in x && sym in x) { + x.a; + x["a"]; + x[1]; + x["1"]; + x[sym]; + } + } + + // Repro from #50639 + + function foo(value: A) { + if (typeof value === "object" && value !== null && "prop" in value) { + value; // A & object & Record<"prop", unknown> + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/inKeywordTypeguard(strict=true).js b/tests/baselines/reference/inKeywordTypeguard(strict=true).js new file mode 100644 index 0000000000000..b2a0d92f7b18b --- /dev/null +++ b/tests/baselines/reference/inKeywordTypeguard(strict=true).js @@ -0,0 +1,542 @@ +//// [inKeywordTypeguard.ts] +class A { a: string; } +class B { b: string; } + +function negativeClassesTest(x: A | B) { + if ("a" in x) { + x.b = "1"; + } else { + x.a = "1"; + } +} + +function positiveClassesTest(x: A | B) { + if ("a" in x) { + x.b = "1"; + } else { + x.a = "1"; + } +} + +class AWithOptionalProp { a?: string; } +class BWithOptionalProp { b?: string; } + +function positiveTestClassesWithOptionalProperties(x: AWithOptionalProp | BWithOptionalProp) { + if ("a" in x) { + x.a = "1"; + } else { + x.b = "1"; + } +} + +class AWithMethod { + a(): string { return ""; } +} + +class BWithMethod { + b(): string { return ""; } +} + +function negativeTestClassesWithMembers(x: AWithMethod | BWithMethod) { + if ("a" in x) { + x.a(); + x.b(); + } else { + } +} + +function negativeTestClassesWithMemberMissingInBothClasses(x: AWithMethod | BWithMethod) { + if ("c" in x) { + x.a(); + x.b(); + } else { + x.a(); + x.b(); + } +} + +class C { a: string; } +class D { a: string; } + +function negativeMultipleClassesTest(x: A | B | C | D) { + if ("a" in x) { + x.b = "1"; + } else { + x.a = "1"; + } +} + +class ClassWithUnionProp { prop: A | B } + +function negativePropTest(x: ClassWithUnionProp) { + if ("a" in x.prop) { + let y: string = x.prop.b; + } else { + let z: string = x.prop.a; + } +} + +class NegativeClassTest { + protected prop: A | B; + inThis() { + if ("a" in this.prop) { + let z: number = this.prop.b; + } else { + let y: string = this.prop.a; + } + } +} + +class UnreachableCodeDetection { + a: string; + inThis() { + if ("a" in this) { + } else { + let y = this.a; + } + } +} + +function positiveIntersectionTest(x: { a: string } & { b: string }) { + if ("a" in x) { + let s: string = x.a; + } else { + let n: never = x; + } +} + +// Repro from #38608 +declare const error: Error; +if ('extra' in error) { + error // Still Error +} else { + error // Error +} + +function narrowsToNever(x: { l: number } | { r: number }) { + let v: number; + if ("l" in x) { + v = x.l; + } + else if ("r" in x) { + v = x.r; + } + else { + v = x + } + return v; +} + +type AOrB = { aProp: number } | { bProp: number }; +declare function isAOrB(x: unknown): x is AOrB; + +declare var x: unknown; +if (isAOrB(x)) { + if ("aProp" in x) { + x.aProp; + } + else if ("bProp" in x) { + x.bProp; + } + // x is never because of the type predicate from unknown + else if ("cProp" in x) { + const _never: never = x; + } +} + +function negativeIntersectionTest() { + if ("ontouchstart" in window) { + window.ontouchstart + } else { + window.ontouchstart + } +} + +function f1(x: unknown) { + if ("a" in x) { + x.a; + } + if (x && "a" in x) { + x.a; + } + if (x && typeof x === "object" && "a" in x) { + x.a; + } + if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) { + x.a; + x.b; + x.c; + } +} + +function f2(x: object) { + if ("a" in x) { + x.a; + } + if ("a" in x && "b" in x && "c" in x) { + x.a; + x.b; + x.c; + } +} + +function f3(x: T) { + if ("a" in x) { + x.a; + } + if (x && "a" in x) { + x.a; + } + if (x && typeof x === "object" && "a" in x) { + x.a; + } + if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) { + x.a; + x.b; + x.c; + } +} + +function f4(x: { a: string }) { + if ("a" in x) { + x.a; + } + if ("a" in x && "b" in x && "c" in x) { + x.a; + x.b; + x.c; + } +} + +function f5(x: { a: string } | { b: string }) { + if ("a" in x) { + x; // { a: string } + } + else if ("b" in x) { + x; // { b: string } + } + else { + x; // never + } +} + +function f6(x: { a: string } | { b: string }) { + if ("a" in x) { + x; // { a: string } + } + else if ("a" in x) { + x; // { b: string } & Record<"a", unknown> + } + else { + x; // { b: string } + } +} + +// Object and corresponding intersection should narrow the same + +function f7(x: { a: string, b: number }, y: { a: string } & { b: number }) { + if ("a" in x) { + x; + } + else { + x; // never + } + if ("a" in y) { + y; + } + else { + y; // never + } +} + +const sym = Symbol(); + +function f8(x: object) { + if ("a" in x && 1 in x && sym in x) { + x.a; + x["a"]; + x[1]; + x["1"]; + x[sym]; + } +} + +function f9(x: object) { + if ("a" in x && "1" in x && sym in x) { + x.a; + x["a"]; + x[1]; + x["1"]; + x[sym]; + } +} + +// Repro from #50639 + +function foo(value: A) { + if (typeof value === "object" && value !== null && "prop" in value) { + value; // A & object & Record<"prop", unknown> + } +} + + +//// [inKeywordTypeguard.js] +"use strict"; +class A { +} +class B { +} +function negativeClassesTest(x) { + if ("a" in x) { + x.b = "1"; + } + else { + x.a = "1"; + } +} +function positiveClassesTest(x) { + if ("a" in x) { + x.b = "1"; + } + else { + x.a = "1"; + } +} +class AWithOptionalProp { +} +class BWithOptionalProp { +} +function positiveTestClassesWithOptionalProperties(x) { + if ("a" in x) { + x.a = "1"; + } + else { + x.b = "1"; + } +} +class AWithMethod { + a() { return ""; } +} +class BWithMethod { + b() { return ""; } +} +function negativeTestClassesWithMembers(x) { + if ("a" in x) { + x.a(); + x.b(); + } + else { + } +} +function negativeTestClassesWithMemberMissingInBothClasses(x) { + if ("c" in x) { + x.a(); + x.b(); + } + else { + x.a(); + x.b(); + } +} +class C { +} +class D { +} +function negativeMultipleClassesTest(x) { + if ("a" in x) { + x.b = "1"; + } + else { + x.a = "1"; + } +} +class ClassWithUnionProp { +} +function negativePropTest(x) { + if ("a" in x.prop) { + let y = x.prop.b; + } + else { + let z = x.prop.a; + } +} +class NegativeClassTest { + inThis() { + if ("a" in this.prop) { + let z = this.prop.b; + } + else { + let y = this.prop.a; + } + } +} +class UnreachableCodeDetection { + inThis() { + if ("a" in this) { + } + else { + let y = this.a; + } + } +} +function positiveIntersectionTest(x) { + if ("a" in x) { + let s = x.a; + } + else { + let n = x; + } +} +if ('extra' in error) { + error; // Still Error +} +else { + error; // Error +} +function narrowsToNever(x) { + let v; + if ("l" in x) { + v = x.l; + } + else if ("r" in x) { + v = x.r; + } + else { + v = x; + } + return v; +} +if (isAOrB(x)) { + if ("aProp" in x) { + x.aProp; + } + else if ("bProp" in x) { + x.bProp; + } + // x is never because of the type predicate from unknown + else if ("cProp" in x) { + const _never = x; + } +} +function negativeIntersectionTest() { + if ("ontouchstart" in window) { + window.ontouchstart; + } + else { + window.ontouchstart; + } +} +function f1(x) { + if ("a" in x) { + x.a; + } + if (x && "a" in x) { + x.a; + } + if (x && typeof x === "object" && "a" in x) { + x.a; + } + if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) { + x.a; + x.b; + x.c; + } +} +function f2(x) { + if ("a" in x) { + x.a; + } + if ("a" in x && "b" in x && "c" in x) { + x.a; + x.b; + x.c; + } +} +function f3(x) { + if ("a" in x) { + x.a; + } + if (x && "a" in x) { + x.a; + } + if (x && typeof x === "object" && "a" in x) { + x.a; + } + if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) { + x.a; + x.b; + x.c; + } +} +function f4(x) { + if ("a" in x) { + x.a; + } + if ("a" in x && "b" in x && "c" in x) { + x.a; + x.b; + x.c; + } +} +function f5(x) { + if ("a" in x) { + x; // { a: string } + } + else if ("b" in x) { + x; // { b: string } + } + else { + x; // never + } +} +function f6(x) { + if ("a" in x) { + x; // { a: string } + } + else if ("a" in x) { + x; // { b: string } & Record<"a", unknown> + } + else { + x; // { b: string } + } +} +// Object and corresponding intersection should narrow the same +function f7(x, y) { + if ("a" in x) { + x; + } + else { + x; // never + } + if ("a" in y) { + y; + } + else { + y; // never + } +} +const sym = Symbol(); +function f8(x) { + if ("a" in x && 1 in x && sym in x) { + x.a; + x["a"]; + x[1]; + x["1"]; + x[sym]; + } +} +function f9(x) { + if ("a" in x && "1" in x && sym in x) { + x.a; + x["a"]; + x[1]; + x["1"]; + x[sym]; + } +} +// Repro from #50639 +function foo(value) { + if (typeof value === "object" && value !== null && "prop" in value) { + value; // A & object & Record<"prop", unknown> + } +} diff --git a/tests/baselines/reference/inKeywordTypeguard(strict=true).symbols b/tests/baselines/reference/inKeywordTypeguard(strict=true).symbols new file mode 100644 index 0000000000000..33ff5a5749fca --- /dev/null +++ b/tests/baselines/reference/inKeywordTypeguard(strict=true).symbols @@ -0,0 +1,727 @@ +=== tests/cases/compiler/inKeywordTypeguard.ts === +class A { a: string; } +>A : Symbol(A, Decl(inKeywordTypeguard.ts, 0, 0)) +>a : Symbol(A.a, Decl(inKeywordTypeguard.ts, 0, 9)) + +class B { b: string; } +>B : Symbol(B, Decl(inKeywordTypeguard.ts, 0, 22)) +>b : Symbol(B.b, Decl(inKeywordTypeguard.ts, 1, 9)) + +function negativeClassesTest(x: A | B) { +>negativeClassesTest : Symbol(negativeClassesTest, Decl(inKeywordTypeguard.ts, 1, 22)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 3, 29)) +>A : Symbol(A, Decl(inKeywordTypeguard.ts, 0, 0)) +>B : Symbol(B, Decl(inKeywordTypeguard.ts, 0, 22)) + + if ("a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 3, 29)) + + x.b = "1"; +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 3, 29)) + + } else { + x.a = "1"; +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 3, 29)) + } +} + +function positiveClassesTest(x: A | B) { +>positiveClassesTest : Symbol(positiveClassesTest, Decl(inKeywordTypeguard.ts, 9, 1)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 11, 29)) +>A : Symbol(A, Decl(inKeywordTypeguard.ts, 0, 0)) +>B : Symbol(B, Decl(inKeywordTypeguard.ts, 0, 22)) + + if ("a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 11, 29)) + + x.b = "1"; +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 11, 29)) + + } else { + x.a = "1"; +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 11, 29)) + } +} + +class AWithOptionalProp { a?: string; } +>AWithOptionalProp : Symbol(AWithOptionalProp, Decl(inKeywordTypeguard.ts, 17, 1)) +>a : Symbol(AWithOptionalProp.a, Decl(inKeywordTypeguard.ts, 19, 25)) + +class BWithOptionalProp { b?: string; } +>BWithOptionalProp : Symbol(BWithOptionalProp, Decl(inKeywordTypeguard.ts, 19, 39)) +>b : Symbol(BWithOptionalProp.b, Decl(inKeywordTypeguard.ts, 20, 25)) + +function positiveTestClassesWithOptionalProperties(x: AWithOptionalProp | BWithOptionalProp) { +>positiveTestClassesWithOptionalProperties : Symbol(positiveTestClassesWithOptionalProperties, Decl(inKeywordTypeguard.ts, 20, 39)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 22, 51)) +>AWithOptionalProp : Symbol(AWithOptionalProp, Decl(inKeywordTypeguard.ts, 17, 1)) +>BWithOptionalProp : Symbol(BWithOptionalProp, Decl(inKeywordTypeguard.ts, 19, 39)) + + if ("a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 22, 51)) + + x.a = "1"; +>x.a : Symbol(AWithOptionalProp.a, Decl(inKeywordTypeguard.ts, 19, 25)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 22, 51)) +>a : Symbol(AWithOptionalProp.a, Decl(inKeywordTypeguard.ts, 19, 25)) + + } else { + x.b = "1"; +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 22, 51)) + } +} + +class AWithMethod { +>AWithMethod : Symbol(AWithMethod, Decl(inKeywordTypeguard.ts, 28, 1)) + + a(): string { return ""; } +>a : Symbol(AWithMethod.a, Decl(inKeywordTypeguard.ts, 30, 19)) +} + +class BWithMethod { +>BWithMethod : Symbol(BWithMethod, Decl(inKeywordTypeguard.ts, 32, 1)) + + b(): string { return ""; } +>b : Symbol(BWithMethod.b, Decl(inKeywordTypeguard.ts, 34, 19)) +} + +function negativeTestClassesWithMembers(x: AWithMethod | BWithMethod) { +>negativeTestClassesWithMembers : Symbol(negativeTestClassesWithMembers, Decl(inKeywordTypeguard.ts, 36, 1)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 38, 40)) +>AWithMethod : Symbol(AWithMethod, Decl(inKeywordTypeguard.ts, 28, 1)) +>BWithMethod : Symbol(BWithMethod, Decl(inKeywordTypeguard.ts, 32, 1)) + + if ("a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 38, 40)) + + x.a(); +>x.a : Symbol(AWithMethod.a, Decl(inKeywordTypeguard.ts, 30, 19)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 38, 40)) +>a : Symbol(AWithMethod.a, Decl(inKeywordTypeguard.ts, 30, 19)) + + x.b(); +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 38, 40)) + + } else { + } +} + +function negativeTestClassesWithMemberMissingInBothClasses(x: AWithMethod | BWithMethod) { +>negativeTestClassesWithMemberMissingInBothClasses : Symbol(negativeTestClassesWithMemberMissingInBothClasses, Decl(inKeywordTypeguard.ts, 44, 1)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 46, 59)) +>AWithMethod : Symbol(AWithMethod, Decl(inKeywordTypeguard.ts, 28, 1)) +>BWithMethod : Symbol(BWithMethod, Decl(inKeywordTypeguard.ts, 32, 1)) + + if ("c" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 46, 59)) + + x.a(); +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 46, 59)) + + x.b(); +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 46, 59)) + + } else { + x.a(); +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 46, 59)) + + x.b(); +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 46, 59)) + } +} + +class C { a: string; } +>C : Symbol(C, Decl(inKeywordTypeguard.ts, 54, 1)) +>a : Symbol(C.a, Decl(inKeywordTypeguard.ts, 56, 9)) + +class D { a: string; } +>D : Symbol(D, Decl(inKeywordTypeguard.ts, 56, 22)) +>a : Symbol(D.a, Decl(inKeywordTypeguard.ts, 57, 9)) + +function negativeMultipleClassesTest(x: A | B | C | D) { +>negativeMultipleClassesTest : Symbol(negativeMultipleClassesTest, Decl(inKeywordTypeguard.ts, 57, 22)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 59, 37)) +>A : Symbol(A, Decl(inKeywordTypeguard.ts, 0, 0)) +>B : Symbol(B, Decl(inKeywordTypeguard.ts, 0, 22)) +>C : Symbol(C, Decl(inKeywordTypeguard.ts, 54, 1)) +>D : Symbol(D, Decl(inKeywordTypeguard.ts, 56, 22)) + + if ("a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 59, 37)) + + x.b = "1"; +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 59, 37)) + + } else { + x.a = "1"; +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 59, 37)) + } +} + +class ClassWithUnionProp { prop: A | B } +>ClassWithUnionProp : Symbol(ClassWithUnionProp, Decl(inKeywordTypeguard.ts, 65, 1)) +>prop : Symbol(ClassWithUnionProp.prop, Decl(inKeywordTypeguard.ts, 67, 26)) +>A : Symbol(A, Decl(inKeywordTypeguard.ts, 0, 0)) +>B : Symbol(B, Decl(inKeywordTypeguard.ts, 0, 22)) + +function negativePropTest(x: ClassWithUnionProp) { +>negativePropTest : Symbol(negativePropTest, Decl(inKeywordTypeguard.ts, 67, 40)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 69, 26)) +>ClassWithUnionProp : Symbol(ClassWithUnionProp, Decl(inKeywordTypeguard.ts, 65, 1)) + + if ("a" in x.prop) { +>x.prop : Symbol(ClassWithUnionProp.prop, Decl(inKeywordTypeguard.ts, 67, 26)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 69, 26)) +>prop : Symbol(ClassWithUnionProp.prop, Decl(inKeywordTypeguard.ts, 67, 26)) + + let y: string = x.prop.b; +>y : Symbol(y, Decl(inKeywordTypeguard.ts, 71, 11)) +>x.prop : Symbol(ClassWithUnionProp.prop, Decl(inKeywordTypeguard.ts, 67, 26)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 69, 26)) +>prop : Symbol(ClassWithUnionProp.prop, Decl(inKeywordTypeguard.ts, 67, 26)) + + } else { + let z: string = x.prop.a; +>z : Symbol(z, Decl(inKeywordTypeguard.ts, 73, 11)) +>x.prop : Symbol(ClassWithUnionProp.prop, Decl(inKeywordTypeguard.ts, 67, 26)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 69, 26)) +>prop : Symbol(ClassWithUnionProp.prop, Decl(inKeywordTypeguard.ts, 67, 26)) + } +} + +class NegativeClassTest { +>NegativeClassTest : Symbol(NegativeClassTest, Decl(inKeywordTypeguard.ts, 75, 1)) + + protected prop: A | B; +>prop : Symbol(NegativeClassTest.prop, Decl(inKeywordTypeguard.ts, 77, 25)) +>A : Symbol(A, Decl(inKeywordTypeguard.ts, 0, 0)) +>B : Symbol(B, Decl(inKeywordTypeguard.ts, 0, 22)) + + inThis() { +>inThis : Symbol(NegativeClassTest.inThis, Decl(inKeywordTypeguard.ts, 78, 26)) + + if ("a" in this.prop) { +>this.prop : Symbol(NegativeClassTest.prop, Decl(inKeywordTypeguard.ts, 77, 25)) +>this : Symbol(NegativeClassTest, Decl(inKeywordTypeguard.ts, 75, 1)) +>prop : Symbol(NegativeClassTest.prop, Decl(inKeywordTypeguard.ts, 77, 25)) + + let z: number = this.prop.b; +>z : Symbol(z, Decl(inKeywordTypeguard.ts, 81, 15)) +>this.prop : Symbol(NegativeClassTest.prop, Decl(inKeywordTypeguard.ts, 77, 25)) +>this : Symbol(NegativeClassTest, Decl(inKeywordTypeguard.ts, 75, 1)) +>prop : Symbol(NegativeClassTest.prop, Decl(inKeywordTypeguard.ts, 77, 25)) + + } else { + let y: string = this.prop.a; +>y : Symbol(y, Decl(inKeywordTypeguard.ts, 83, 15)) +>this.prop : Symbol(NegativeClassTest.prop, Decl(inKeywordTypeguard.ts, 77, 25)) +>this : Symbol(NegativeClassTest, Decl(inKeywordTypeguard.ts, 75, 1)) +>prop : Symbol(NegativeClassTest.prop, Decl(inKeywordTypeguard.ts, 77, 25)) + } + } +} + +class UnreachableCodeDetection { +>UnreachableCodeDetection : Symbol(UnreachableCodeDetection, Decl(inKeywordTypeguard.ts, 86, 1)) + + a: string; +>a : Symbol(UnreachableCodeDetection.a, Decl(inKeywordTypeguard.ts, 88, 32)) + + inThis() { +>inThis : Symbol(UnreachableCodeDetection.inThis, Decl(inKeywordTypeguard.ts, 89, 14)) + + if ("a" in this) { +>this : Symbol(UnreachableCodeDetection, Decl(inKeywordTypeguard.ts, 86, 1)) + + } else { + let y = this.a; +>y : Symbol(y, Decl(inKeywordTypeguard.ts, 93, 15)) + } + } +} + +function positiveIntersectionTest(x: { a: string } & { b: string }) { +>positiveIntersectionTest : Symbol(positiveIntersectionTest, Decl(inKeywordTypeguard.ts, 96, 1)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 98, 34)) +>a : Symbol(a, Decl(inKeywordTypeguard.ts, 98, 38)) +>b : Symbol(b, Decl(inKeywordTypeguard.ts, 98, 54)) + + if ("a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 98, 34)) + + let s: string = x.a; +>s : Symbol(s, Decl(inKeywordTypeguard.ts, 100, 11)) +>x.a : Symbol(a, Decl(inKeywordTypeguard.ts, 98, 38)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 98, 34)) +>a : Symbol(a, Decl(inKeywordTypeguard.ts, 98, 38)) + + } else { + let n: never = x; +>n : Symbol(n, Decl(inKeywordTypeguard.ts, 102, 11)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 98, 34)) + } +} + +// Repro from #38608 +declare const error: Error; +>error : Symbol(error, Decl(inKeywordTypeguard.ts, 107, 13)) +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + +if ('extra' in error) { +>error : Symbol(error, Decl(inKeywordTypeguard.ts, 107, 13)) + + error // Still Error +>error : Symbol(error, Decl(inKeywordTypeguard.ts, 107, 13)) + +} else { + error // Error +>error : Symbol(error, Decl(inKeywordTypeguard.ts, 107, 13)) +} + +function narrowsToNever(x: { l: number } | { r: number }) { +>narrowsToNever : Symbol(narrowsToNever, Decl(inKeywordTypeguard.ts, 112, 1)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 114, 24)) +>l : Symbol(l, Decl(inKeywordTypeguard.ts, 114, 28)) +>r : Symbol(r, Decl(inKeywordTypeguard.ts, 114, 44)) + + let v: number; +>v : Symbol(v, Decl(inKeywordTypeguard.ts, 115, 7)) + + if ("l" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 114, 24)) + + v = x.l; +>v : Symbol(v, Decl(inKeywordTypeguard.ts, 115, 7)) +>x.l : Symbol(l, Decl(inKeywordTypeguard.ts, 114, 28)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 114, 24)) +>l : Symbol(l, Decl(inKeywordTypeguard.ts, 114, 28)) + } + else if ("r" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 114, 24)) + + v = x.r; +>v : Symbol(v, Decl(inKeywordTypeguard.ts, 115, 7)) +>x.r : Symbol(r, Decl(inKeywordTypeguard.ts, 114, 44)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 114, 24)) +>r : Symbol(r, Decl(inKeywordTypeguard.ts, 114, 44)) + } + else { + v = x +>v : Symbol(v, Decl(inKeywordTypeguard.ts, 115, 7)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 114, 24)) + } + return v; +>v : Symbol(v, Decl(inKeywordTypeguard.ts, 115, 7)) +} + +type AOrB = { aProp: number } | { bProp: number }; +>AOrB : Symbol(AOrB, Decl(inKeywordTypeguard.ts, 126, 1)) +>aProp : Symbol(aProp, Decl(inKeywordTypeguard.ts, 128, 13)) +>bProp : Symbol(bProp, Decl(inKeywordTypeguard.ts, 128, 33)) + +declare function isAOrB(x: unknown): x is AOrB; +>isAOrB : Symbol(isAOrB, Decl(inKeywordTypeguard.ts, 128, 50)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 129, 24)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 129, 24)) +>AOrB : Symbol(AOrB, Decl(inKeywordTypeguard.ts, 126, 1)) + +declare var x: unknown; +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 131, 11)) + +if (isAOrB(x)) { +>isAOrB : Symbol(isAOrB, Decl(inKeywordTypeguard.ts, 128, 50)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 131, 11)) + + if ("aProp" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 131, 11)) + + x.aProp; +>x.aProp : Symbol(aProp, Decl(inKeywordTypeguard.ts, 128, 13)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 131, 11)) +>aProp : Symbol(aProp, Decl(inKeywordTypeguard.ts, 128, 13)) + } + else if ("bProp" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 131, 11)) + + x.bProp; +>x.bProp : Symbol(bProp, Decl(inKeywordTypeguard.ts, 128, 33)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 131, 11)) +>bProp : Symbol(bProp, Decl(inKeywordTypeguard.ts, 128, 33)) + } + // x is never because of the type predicate from unknown + else if ("cProp" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 131, 11)) + + const _never: never = x; +>_never : Symbol(_never, Decl(inKeywordTypeguard.ts, 141, 13)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 131, 11)) + } +} + +function negativeIntersectionTest() { +>negativeIntersectionTest : Symbol(negativeIntersectionTest, Decl(inKeywordTypeguard.ts, 143, 1)) + + if ("ontouchstart" in window) { +>window : Symbol(window, Decl(lib.dom.d.ts, --, --)) + + window.ontouchstart +>window.ontouchstart : Symbol(ontouchstart, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --)) +>window : Symbol(window, Decl(lib.dom.d.ts, --, --)) +>ontouchstart : Symbol(ontouchstart, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --)) + + } else { + window.ontouchstart +>window : Symbol(window, Decl(lib.dom.d.ts, --, --)) + } +} + +function f1(x: unknown) { +>f1 : Symbol(f1, Decl(inKeywordTypeguard.ts, 151, 1)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) + + if ("a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) + + x.a; +>x.a : Symbol(a) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) +>a : Symbol(a) + } + if (x && "a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) + + x.a; +>x.a : Symbol(a) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) +>a : Symbol(a) + } + if (x && typeof x === "object" && "a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) + + x.a; +>x.a : Symbol(a) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) +>a : Symbol(a) + } + if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) + + x.a; +>x.a : Symbol(a) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) +>a : Symbol(a) + + x.b; +>x.b : Symbol(b) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) +>b : Symbol(b) + + x.c; +>x.c : Symbol(c) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12)) +>c : Symbol(c) + } +} + +function f2(x: object) { +>f2 : Symbol(f2, Decl(inKeywordTypeguard.ts, 168, 1)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12)) + + if ("a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12)) + + x.a; +>x.a : Symbol(a) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12)) +>a : Symbol(a) + } + if ("a" in x && "b" in x && "c" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12)) + + x.a; +>x.a : Symbol(a) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12)) +>a : Symbol(a) + + x.b; +>x.b : Symbol(b) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12)) +>b : Symbol(b) + + x.c; +>x.c : Symbol(c) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12)) +>c : Symbol(c) + } +} + +function f3(x: T) { +>f3 : Symbol(f3, Decl(inKeywordTypeguard.ts, 179, 1)) +>T : Symbol(T, Decl(inKeywordTypeguard.ts, 181, 12)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) +>T : Symbol(T, Decl(inKeywordTypeguard.ts, 181, 12)) + + if ("a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) + + x.a; +>x.a : Symbol(a) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) +>a : Symbol(a) + } + if (x && "a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) + + x.a; +>x.a : Symbol(a) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) +>a : Symbol(a) + } + if (x && typeof x === "object" && "a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) + + x.a; +>x.a : Symbol(a) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) +>a : Symbol(a) + } + if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) + + x.a; +>x.a : Symbol(a) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) +>a : Symbol(a) + + x.b; +>x.b : Symbol(b) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) +>b : Symbol(b) + + x.c; +>x.c : Symbol(c) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15)) +>c : Symbol(c) + } +} + +function f4(x: { a: string }) { +>f4 : Symbol(f4, Decl(inKeywordTypeguard.ts, 196, 1)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12)) +>a : Symbol(a, Decl(inKeywordTypeguard.ts, 198, 16)) + + if ("a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12)) + + x.a; +>x.a : Symbol(a, Decl(inKeywordTypeguard.ts, 198, 16)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12)) +>a : Symbol(a, Decl(inKeywordTypeguard.ts, 198, 16)) + } + if ("a" in x && "b" in x && "c" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12)) + + x.a; +>x.a : Symbol(a, Decl(inKeywordTypeguard.ts, 198, 16)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12)) +>a : Symbol(a, Decl(inKeywordTypeguard.ts, 198, 16)) + + x.b; +>x.b : Symbol(b) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12)) +>b : Symbol(b) + + x.c; +>x.c : Symbol(c) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12)) +>c : Symbol(c) + } +} + +function f5(x: { a: string } | { b: string }) { +>f5 : Symbol(f5, Decl(inKeywordTypeguard.ts, 207, 1)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 209, 12)) +>a : Symbol(a, Decl(inKeywordTypeguard.ts, 209, 16)) +>b : Symbol(b, Decl(inKeywordTypeguard.ts, 209, 32)) + + if ("a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 209, 12)) + + x; // { a: string } +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 209, 12)) + } + else if ("b" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 209, 12)) + + x; // { b: string } +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 209, 12)) + } + else { + x; // never +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 209, 12)) + } +} + +function f6(x: { a: string } | { b: string }) { +>f6 : Symbol(f6, Decl(inKeywordTypeguard.ts, 219, 1)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 221, 12)) +>a : Symbol(a, Decl(inKeywordTypeguard.ts, 221, 16)) +>b : Symbol(b, Decl(inKeywordTypeguard.ts, 221, 32)) + + if ("a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 221, 12)) + + x; // { a: string } +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 221, 12)) + } + else if ("a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 221, 12)) + + x; // { b: string } & Record<"a", unknown> +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 221, 12)) + } + else { + x; // { b: string } +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 221, 12)) + } +} + +// Object and corresponding intersection should narrow the same + +function f7(x: { a: string, b: number }, y: { a: string } & { b: number }) { +>f7 : Symbol(f7, Decl(inKeywordTypeguard.ts, 231, 1)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 235, 12)) +>a : Symbol(a, Decl(inKeywordTypeguard.ts, 235, 16)) +>b : Symbol(b, Decl(inKeywordTypeguard.ts, 235, 27)) +>y : Symbol(y, Decl(inKeywordTypeguard.ts, 235, 40)) +>a : Symbol(a, Decl(inKeywordTypeguard.ts, 235, 45)) +>b : Symbol(b, Decl(inKeywordTypeguard.ts, 235, 61)) + + if ("a" in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 235, 12)) + + x; +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 235, 12)) + } + else { + x; // never +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 235, 12)) + } + if ("a" in y) { +>y : Symbol(y, Decl(inKeywordTypeguard.ts, 235, 40)) + + y; +>y : Symbol(y, Decl(inKeywordTypeguard.ts, 235, 40)) + } + else { + y; // never +>y : Symbol(y, Decl(inKeywordTypeguard.ts, 235, 40)) + } +} + +const sym = Symbol(); +>sym : Symbol(sym, Decl(inKeywordTypeguard.ts, 250, 5)) +>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) + +function f8(x: object) { +>f8 : Symbol(f8, Decl(inKeywordTypeguard.ts, 250, 21)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12)) + + if ("a" in x && 1 in x && sym in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12)) +>sym : Symbol(sym, Decl(inKeywordTypeguard.ts, 250, 5)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12)) + + x.a; +>x.a : Symbol(a) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12)) +>a : Symbol(a) + + x["a"]; +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12)) +>"a" : Symbol(a) + + x[1]; +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12)) +>1 : Symbol(1) + + x["1"]; +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12)) +>"1" : Symbol(1) + + x[sym]; +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12)) +>sym : Symbol(sym, Decl(inKeywordTypeguard.ts, 250, 5)) + } +} + +function f9(x: object) { +>f9 : Symbol(f9, Decl(inKeywordTypeguard.ts, 260, 1)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12)) + + if ("a" in x && "1" in x && sym in x) { +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12)) +>sym : Symbol(sym, Decl(inKeywordTypeguard.ts, 250, 5)) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12)) + + x.a; +>x.a : Symbol(a) +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12)) +>a : Symbol(a) + + x["a"]; +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12)) +>"a" : Symbol(a) + + x[1]; +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12)) +>1 : Symbol(1) + + x["1"]; +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12)) +>"1" : Symbol(1) + + x[sym]; +>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12)) +>sym : Symbol(sym, Decl(inKeywordTypeguard.ts, 250, 5)) + } +} + +// Repro from #50639 + +function foo(value: A) { +>foo : Symbol(foo, Decl(inKeywordTypeguard.ts, 270, 1)) +>A : Symbol(A, Decl(inKeywordTypeguard.ts, 274, 13)) +>value : Symbol(value, Decl(inKeywordTypeguard.ts, 274, 16)) +>A : Symbol(A, Decl(inKeywordTypeguard.ts, 274, 13)) + + if (typeof value === "object" && value !== null && "prop" in value) { +>value : Symbol(value, Decl(inKeywordTypeguard.ts, 274, 16)) +>value : Symbol(value, Decl(inKeywordTypeguard.ts, 274, 16)) +>value : Symbol(value, Decl(inKeywordTypeguard.ts, 274, 16)) + + value; // A & object & Record<"prop", unknown> +>value : Symbol(value, Decl(inKeywordTypeguard.ts, 274, 16)) + } +} + diff --git a/tests/baselines/reference/inKeywordTypeguard(strict=true).types b/tests/baselines/reference/inKeywordTypeguard(strict=true).types new file mode 100644 index 0000000000000..8ec2b47df0796 --- /dev/null +++ b/tests/baselines/reference/inKeywordTypeguard(strict=true).types @@ -0,0 +1,914 @@ +=== tests/cases/compiler/inKeywordTypeguard.ts === +class A { a: string; } +>A : A +>a : string + +class B { b: string; } +>B : B +>b : string + +function negativeClassesTest(x: A | B) { +>negativeClassesTest : (x: A | B) => void +>x : A | B + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : A | B + + x.b = "1"; +>x.b = "1" : "1" +>x.b : any +>x : A +>b : any +>"1" : "1" + + } else { + x.a = "1"; +>x.a = "1" : "1" +>x.a : any +>x : B +>a : any +>"1" : "1" + } +} + +function positiveClassesTest(x: A | B) { +>positiveClassesTest : (x: A | B) => void +>x : A | B + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : A | B + + x.b = "1"; +>x.b = "1" : "1" +>x.b : any +>x : A +>b : any +>"1" : "1" + + } else { + x.a = "1"; +>x.a = "1" : "1" +>x.a : any +>x : B +>a : any +>"1" : "1" + } +} + +class AWithOptionalProp { a?: string; } +>AWithOptionalProp : AWithOptionalProp +>a : string | undefined + +class BWithOptionalProp { b?: string; } +>BWithOptionalProp : BWithOptionalProp +>b : string | undefined + +function positiveTestClassesWithOptionalProperties(x: AWithOptionalProp | BWithOptionalProp) { +>positiveTestClassesWithOptionalProperties : (x: AWithOptionalProp | BWithOptionalProp) => void +>x : AWithOptionalProp | BWithOptionalProp + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : AWithOptionalProp | BWithOptionalProp + + x.a = "1"; +>x.a = "1" : "1" +>x.a : string | undefined +>x : AWithOptionalProp +>a : string | undefined +>"1" : "1" + + } else { + x.b = "1"; +>x.b = "1" : "1" +>x.b : any +>x : AWithOptionalProp | BWithOptionalProp +>b : any +>"1" : "1" + } +} + +class AWithMethod { +>AWithMethod : AWithMethod + + a(): string { return ""; } +>a : () => string +>"" : "" +} + +class BWithMethod { +>BWithMethod : BWithMethod + + b(): string { return ""; } +>b : () => string +>"" : "" +} + +function negativeTestClassesWithMembers(x: AWithMethod | BWithMethod) { +>negativeTestClassesWithMembers : (x: AWithMethod | BWithMethod) => void +>x : AWithMethod | BWithMethod + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : AWithMethod | BWithMethod + + x.a(); +>x.a() : string +>x.a : () => string +>x : AWithMethod +>a : () => string + + x.b(); +>x.b() : any +>x.b : any +>x : AWithMethod +>b : any + + } else { + } +} + +function negativeTestClassesWithMemberMissingInBothClasses(x: AWithMethod | BWithMethod) { +>negativeTestClassesWithMemberMissingInBothClasses : (x: AWithMethod | BWithMethod) => void +>x : AWithMethod | BWithMethod + + if ("c" in x) { +>"c" in x : boolean +>"c" : "c" +>x : AWithMethod | BWithMethod + + x.a(); +>x.a() : any +>x.a : any +>x : (AWithMethod | BWithMethod) & Record<"c", unknown> +>a : any + + x.b(); +>x.b() : any +>x.b : any +>x : (AWithMethod | BWithMethod) & Record<"c", unknown> +>b : any + + } else { + x.a(); +>x.a() : any +>x.a : any +>x : AWithMethod | BWithMethod +>a : any + + x.b(); +>x.b() : any +>x.b : any +>x : AWithMethod | BWithMethod +>b : any + } +} + +class C { a: string; } +>C : C +>a : string + +class D { a: string; } +>D : D +>a : string + +function negativeMultipleClassesTest(x: A | B | C | D) { +>negativeMultipleClassesTest : (x: A | B | C | D) => void +>x : A | B | C | D + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : A | B | C | D + + x.b = "1"; +>x.b = "1" : "1" +>x.b : any +>x : A | C | D +>b : any +>"1" : "1" + + } else { + x.a = "1"; +>x.a = "1" : "1" +>x.a : any +>x : B +>a : any +>"1" : "1" + } +} + +class ClassWithUnionProp { prop: A | B } +>ClassWithUnionProp : ClassWithUnionProp +>prop : A | B + +function negativePropTest(x: ClassWithUnionProp) { +>negativePropTest : (x: ClassWithUnionProp) => void +>x : ClassWithUnionProp + + if ("a" in x.prop) { +>"a" in x.prop : boolean +>"a" : "a" +>x.prop : A | B +>x : ClassWithUnionProp +>prop : A | B + + let y: string = x.prop.b; +>y : string +>x.prop.b : any +>x.prop : A +>x : ClassWithUnionProp +>prop : A +>b : any + + } else { + let z: string = x.prop.a; +>z : string +>x.prop.a : any +>x.prop : B +>x : ClassWithUnionProp +>prop : B +>a : any + } +} + +class NegativeClassTest { +>NegativeClassTest : NegativeClassTest + + protected prop: A | B; +>prop : A | B + + inThis() { +>inThis : () => void + + if ("a" in this.prop) { +>"a" in this.prop : boolean +>"a" : "a" +>this.prop : A | B +>this : this +>prop : A | B + + let z: number = this.prop.b; +>z : number +>this.prop.b : any +>this.prop : A +>this : this +>prop : A +>b : any + + } else { + let y: string = this.prop.a; +>y : string +>this.prop.a : any +>this.prop : B +>this : this +>prop : B +>a : any + } + } +} + +class UnreachableCodeDetection { +>UnreachableCodeDetection : UnreachableCodeDetection + + a: string; +>a : string + + inThis() { +>inThis : () => void + + if ("a" in this) { +>"a" in this : boolean +>"a" : "a" +>this : this + + } else { + let y = this.a; +>y : any +>this.a : any +>this : never +>a : any + } + } +} + +function positiveIntersectionTest(x: { a: string } & { b: string }) { +>positiveIntersectionTest : (x: { a: string;} & { b: string;}) => void +>x : { a: string; } & { b: string; } +>a : string +>b : string + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : { a: string; } & { b: string; } + + let s: string = x.a; +>s : string +>x.a : string +>x : { a: string; } & { b: string; } +>a : string + + } else { + let n: never = x; +>n : never +>x : never + } +} + +// Repro from #38608 +declare const error: Error; +>error : Error + +if ('extra' in error) { +>'extra' in error : boolean +>'extra' : "extra" +>error : Error + + error // Still Error +>error : Error & Record<"extra", unknown> + +} else { + error // Error +>error : Error +} + +function narrowsToNever(x: { l: number } | { r: number }) { +>narrowsToNever : (x: { l: number;} | { r: number;}) => number +>x : { l: number; } | { r: number; } +>l : number +>r : number + + let v: number; +>v : number + + if ("l" in x) { +>"l" in x : boolean +>"l" : "l" +>x : { l: number; } | { r: number; } + + v = x.l; +>v = x.l : number +>v : number +>x.l : number +>x : { l: number; } +>l : number + } + else if ("r" in x) { +>"r" in x : boolean +>"r" : "r" +>x : { r: number; } + + v = x.r; +>v = x.r : number +>v : number +>x.r : number +>x : { r: number; } +>r : number + } + else { + v = x +>v = x : never +>v : number +>x : never + } + return v; +>v : number +} + +type AOrB = { aProp: number } | { bProp: number }; +>AOrB : { aProp: number; } | { bProp: number; } +>aProp : number +>bProp : number + +declare function isAOrB(x: unknown): x is AOrB; +>isAOrB : (x: unknown) => x is AOrB +>x : unknown + +declare var x: unknown; +>x : unknown + +if (isAOrB(x)) { +>isAOrB(x) : boolean +>isAOrB : (x: unknown) => x is AOrB +>x : unknown + + if ("aProp" in x) { +>"aProp" in x : boolean +>"aProp" : "aProp" +>x : AOrB + + x.aProp; +>x.aProp : number +>x : { aProp: number; } +>aProp : number + } + else if ("bProp" in x) { +>"bProp" in x : boolean +>"bProp" : "bProp" +>x : { bProp: number; } + + x.bProp; +>x.bProp : number +>x : { bProp: number; } +>bProp : number + } + // x is never because of the type predicate from unknown + else if ("cProp" in x) { +>"cProp" in x : boolean +>"cProp" : "cProp" +>x : never + + const _never: never = x; +>_never : never +>x : never + } +} + +function negativeIntersectionTest() { +>negativeIntersectionTest : () => void + + if ("ontouchstart" in window) { +>"ontouchstart" in window : boolean +>"ontouchstart" : "ontouchstart" +>window : Window & typeof globalThis + + window.ontouchstart +>window.ontouchstart : (((this: GlobalEventHandlers, ev: TouchEvent) => any) & ((this: Window, ev: TouchEvent) => any)) | null | undefined +>window : Window & typeof globalThis +>ontouchstart : (((this: GlobalEventHandlers, ev: TouchEvent) => any) & ((this: Window, ev: TouchEvent) => any)) | null | undefined + + } else { + window.ontouchstart +>window.ontouchstart : any +>window : never +>ontouchstart : any + } +} + +function f1(x: unknown) { +>f1 : (x: unknown) => void +>x : unknown + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : unknown + + x.a; +>x.a : unknown +>x : Record<"a", unknown> +>a : unknown + } + if (x && "a" in x) { +>x && "a" in x : unknown +>x : unknown +>"a" in x : boolean +>"a" : "a" +>x : {} + + x.a; +>x.a : unknown +>x : Record<"a", unknown> +>a : unknown + } + if (x && typeof x === "object" && "a" in x) { +>x && typeof x === "object" && "a" in x : unknown +>x && typeof x === "object" : unknown +>x : unknown +>typeof x === "object" : boolean +>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>x : {} +>"object" : "object" +>"a" in x : boolean +>"a" : "a" +>x : object + + x.a; +>x.a : unknown +>x : object & Record<"a", unknown> +>a : unknown + } + if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) { +>x && typeof x === "object" && "a" in x && "b" in x && "c" in x : unknown +>x && typeof x === "object" && "a" in x && "b" in x : unknown +>x && typeof x === "object" && "a" in x : unknown +>x && typeof x === "object" : unknown +>x : unknown +>typeof x === "object" : boolean +>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>x : {} +>"object" : "object" +>"a" in x : boolean +>"a" : "a" +>x : object +>"b" in x : boolean +>"b" : "b" +>x : object & Record<"a", unknown> +>"c" in x : boolean +>"c" : "c" +>x : object & Record<"a", unknown> & Record<"b", unknown> + + x.a; +>x.a : unknown +>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>a : unknown + + x.b; +>x.b : unknown +>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>b : unknown + + x.c; +>x.c : unknown +>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>c : unknown + } +} + +function f2(x: object) { +>f2 : (x: object) => void +>x : object + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : object + + x.a; +>x.a : unknown +>x : object & Record<"a", unknown> +>a : unknown + } + if ("a" in x && "b" in x && "c" in x) { +>"a" in x && "b" in x && "c" in x : boolean +>"a" in x && "b" in x : boolean +>"a" in x : boolean +>"a" : "a" +>x : object +>"b" in x : boolean +>"b" : "b" +>x : object & Record<"a", unknown> +>"c" in x : boolean +>"c" : "c" +>x : object & Record<"a", unknown> & Record<"b", unknown> + + x.a; +>x.a : unknown +>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>a : unknown + + x.b; +>x.b : unknown +>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>b : unknown + + x.c; +>x.c : unknown +>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>c : unknown + } +} + +function f3(x: T) { +>f3 : (x: T) => void +>x : T + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : T + + x.a; +>x.a : unknown +>x : T & Record<"a", unknown> +>a : unknown + } + if (x && "a" in x) { +>x && "a" in x : boolean +>x : T +>"a" in x : boolean +>"a" : "a" +>x : NonNullable + + x.a; +>x.a : unknown +>x : T & Record<"a", unknown> +>a : unknown + } + if (x && typeof x === "object" && "a" in x) { +>x && typeof x === "object" && "a" in x : boolean +>x && typeof x === "object" : boolean +>x : T +>typeof x === "object" : boolean +>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>x : NonNullable +>"object" : "object" +>"a" in x : boolean +>"a" : "a" +>x : T & object + + x.a; +>x.a : unknown +>x : T & object & Record<"a", unknown> +>a : unknown + } + if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) { +>x && typeof x === "object" && "a" in x && "b" in x && "c" in x : boolean +>x && typeof x === "object" && "a" in x && "b" in x : boolean +>x && typeof x === "object" && "a" in x : boolean +>x && typeof x === "object" : boolean +>x : T +>typeof x === "object" : boolean +>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>x : NonNullable +>"object" : "object" +>"a" in x : boolean +>"a" : "a" +>x : T & object +>"b" in x : boolean +>"b" : "b" +>x : T & object & Record<"a", unknown> +>"c" in x : boolean +>"c" : "c" +>x : T & object & Record<"a", unknown> & Record<"b", unknown> + + x.a; +>x.a : unknown +>x : T & object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>a : unknown + + x.b; +>x.b : unknown +>x : T & object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>b : unknown + + x.c; +>x.c : unknown +>x : T & object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>c : unknown + } +} + +function f4(x: { a: string }) { +>f4 : (x: { a: string;}) => void +>x : { a: string; } +>a : string + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : { a: string; } + + x.a; +>x.a : string +>x : { a: string; } +>a : string + } + if ("a" in x && "b" in x && "c" in x) { +>"a" in x && "b" in x && "c" in x : boolean +>"a" in x && "b" in x : boolean +>"a" in x : boolean +>"a" : "a" +>x : { a: string; } +>"b" in x : boolean +>"b" : "b" +>x : { a: string; } +>"c" in x : boolean +>"c" : "c" +>x : { a: string; } & Record<"b", unknown> + + x.a; +>x.a : string +>x : { a: string; } & Record<"b", unknown> & Record<"c", unknown> +>a : string + + x.b; +>x.b : unknown +>x : { a: string; } & Record<"b", unknown> & Record<"c", unknown> +>b : unknown + + x.c; +>x.c : unknown +>x : { a: string; } & Record<"b", unknown> & Record<"c", unknown> +>c : unknown + } +} + +function f5(x: { a: string } | { b: string }) { +>f5 : (x: { a: string;} | { b: string;}) => void +>x : { a: string; } | { b: string; } +>a : string +>b : string + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : { a: string; } | { b: string; } + + x; // { a: string } +>x : { a: string; } + } + else if ("b" in x) { +>"b" in x : boolean +>"b" : "b" +>x : { b: string; } + + x; // { b: string } +>x : { b: string; } + } + else { + x; // never +>x : never + } +} + +function f6(x: { a: string } | { b: string }) { +>f6 : (x: { a: string;} | { b: string;}) => void +>x : { a: string; } | { b: string; } +>a : string +>b : string + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : { a: string; } | { b: string; } + + x; // { a: string } +>x : { a: string; } + } + else if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : { b: string; } + + x; // { b: string } & Record<"a", unknown> +>x : { b: string; } & Record<"a", unknown> + } + else { + x; // { b: string } +>x : { b: string; } + } +} + +// Object and corresponding intersection should narrow the same + +function f7(x: { a: string, b: number }, y: { a: string } & { b: number }) { +>f7 : (x: { a: string; b: number;}, y: { a: string;} & { b: number;}) => void +>x : { a: string; b: number; } +>a : string +>b : number +>y : { a: string; } & { b: number; } +>a : string +>b : number + + if ("a" in x) { +>"a" in x : boolean +>"a" : "a" +>x : { a: string; b: number; } + + x; +>x : { a: string; b: number; } + } + else { + x; // never +>x : never + } + if ("a" in y) { +>"a" in y : boolean +>"a" : "a" +>y : { a: string; } & { b: number; } + + y; +>y : { a: string; } & { b: number; } + } + else { + y; // never +>y : never + } +} + +const sym = Symbol(); +>sym : unique symbol +>Symbol() : unique symbol +>Symbol : SymbolConstructor + +function f8(x: object) { +>f8 : (x: object) => void +>x : object + + if ("a" in x && 1 in x && sym in x) { +>"a" in x && 1 in x && sym in x : boolean +>"a" in x && 1 in x : boolean +>"a" in x : boolean +>"a" : "a" +>x : object +>1 in x : boolean +>1 : 1 +>x : object & Record<"a", unknown> +>sym in x : boolean +>sym : unique symbol +>x : object & Record<"a", unknown> & Record<1, unknown> + + x.a; +>x.a : unknown +>x : object & Record<"a", unknown> & Record<1, unknown> & Record +>a : unknown + + x["a"]; +>x["a"] : unknown +>x : object & Record<"a", unknown> & Record<1, unknown> & Record +>"a" : "a" + + x[1]; +>x[1] : unknown +>x : object & Record<"a", unknown> & Record<1, unknown> & Record +>1 : 1 + + x["1"]; +>x["1"] : unknown +>x : object & Record<"a", unknown> & Record<1, unknown> & Record +>"1" : "1" + + x[sym]; +>x[sym] : unknown +>x : object & Record<"a", unknown> & Record<1, unknown> & Record +>sym : unique symbol + } +} + +function f9(x: object) { +>f9 : (x: object) => void +>x : object + + if ("a" in x && "1" in x && sym in x) { +>"a" in x && "1" in x && sym in x : boolean +>"a" in x && "1" in x : boolean +>"a" in x : boolean +>"a" : "a" +>x : object +>"1" in x : boolean +>"1" : "1" +>x : object & Record<"a", unknown> +>sym in x : boolean +>sym : unique symbol +>x : object & Record<"a", unknown> & Record<"1", unknown> + + x.a; +>x.a : unknown +>x : object & Record<"a", unknown> & Record<"1", unknown> & Record +>a : unknown + + x["a"]; +>x["a"] : unknown +>x : object & Record<"a", unknown> & Record<"1", unknown> & Record +>"a" : "a" + + x[1]; +>x[1] : unknown +>x : object & Record<"a", unknown> & Record<"1", unknown> & Record +>1 : 1 + + x["1"]; +>x["1"] : unknown +>x : object & Record<"a", unknown> & Record<"1", unknown> & Record +>"1" : "1" + + x[sym]; +>x[sym] : unknown +>x : object & Record<"a", unknown> & Record<"1", unknown> & Record +>sym : unique symbol + } +} + +// Repro from #50639 + +function foo(value: A) { +>foo : (value: A) => void +>value : A + + if (typeof value === "object" && value !== null && "prop" in value) { +>typeof value === "object" && value !== null && "prop" in value : boolean +>typeof value === "object" && value !== null : boolean +>typeof value === "object" : boolean +>typeof value : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>value : A +>"object" : "object" +>value !== null : boolean +>value : (A & object) | (A & null) +>null : null +>"prop" in value : boolean +>"prop" : "prop" +>value : A & object + + value; // A & object & Record<"prop", unknown> +>value : A & object & Record<"prop", unknown> + } +} + diff --git a/tests/baselines/reference/inKeywordTypeguard.js b/tests/baselines/reference/inKeywordTypeguard.js deleted file mode 100644 index e91a6ddf6e4d4..0000000000000 --- a/tests/baselines/reference/inKeywordTypeguard.js +++ /dev/null @@ -1,333 +0,0 @@ -//// [inKeywordTypeguard.ts] -class A { a: string; } -class B { b: string; } - -function negativeClassesTest(x: A | B) { - if ("a" in x) { - x.b = "1"; - } else { - x.a = "1"; - } -} - -function positiveClassesTest(x: A | B) { - if ("a" in x) { - x.b = "1"; - } else { - x.a = "1"; - } -} - -class AWithOptionalProp { a?: string; } -class BWithOptionalProp { b?: string; } - -function positiveTestClassesWithOptionalProperties(x: AWithOptionalProp | BWithOptionalProp) { - if ("a" in x) { - x.a = "1"; - } else { - x.b = "1"; - } -} - -class AWithMethod { - a(): string { return ""; } -} - -class BWithMethod { - b(): string { return ""; } -} - -function negativeTestClassesWithMembers(x: AWithMethod | BWithMethod) { - if ("a" in x) { - x.a(); - x.b(); - } else { - } -} - -function negativeTestClassesWithMemberMissingInBothClasses(x: AWithMethod | BWithMethod) { - if ("c" in x) { - x.a(); - x.b(); - } else { - x.a(); - x.b(); - } -} - -class C { a: string; } -class D { a: string; } - -function negativeMultipleClassesTest(x: A | B | C | D) { - if ("a" in x) { - x.b = "1"; - } else { - x.a = "1"; - } -} - -class ClassWithUnionProp { prop: A | B } - -function negativePropTest(x: ClassWithUnionProp) { - if ("a" in x.prop) { - let y: string = x.prop.b; - } else { - let z: string = x.prop.a; - } -} - -class NegativeClassTest { - protected prop: A | B; - inThis() { - if ("a" in this.prop) { - let z: number = this.prop.b; - } else { - let y: string = this.prop.a; - } - } -} - -class UnreachableCodeDetection { - a: string; - inThis() { - if ("a" in this) { - } else { - let y = this.a; - } - } -} - -function positiveIntersectionTest(x: { a: string } & { b: string }) { - if ("a" in x) { - let s: string = x.a; - } else { - let n: never = x; - } -} - -// Repro from #38608 -declare const error: Error; -if ('extra' in error) { - error // Still Error -} else { - error // Error -} - -function narrowsToNever(x: { l: number } | { r: number }) { - let v: number; - if ("l" in x) { - v = x.l; - } - else if ("r" in x) { - v = x.r; - } - else { - v = x - } - return v; -} - -type AOrB = { aProp: number } | { bProp: number }; -declare function isAOrB(x: unknown): x is AOrB; - -declare var x: unknown; -if (isAOrB(x)) { - if ("aProp" in x) { - x.aProp; - } - else if ("bProp" in x) { - x.bProp; - } - // x is never because of the type predicate from unknown - else if ("cProp" in x) { - const _never: never = x; - } -} - -function negativeIntersectionTest() { - if ("ontouchstart" in window) { - window.ontouchstart - } else { - window.ontouchstart - } -} - - -//// [inKeywordTypeguard.js] -var A = /** @class */ (function () { - function A() { - } - return A; -}()); -var B = /** @class */ (function () { - function B() { - } - return B; -}()); -function negativeClassesTest(x) { - if ("a" in x) { - x.b = "1"; - } - else { - x.a = "1"; - } -} -function positiveClassesTest(x) { - if ("a" in x) { - x.b = "1"; - } - else { - x.a = "1"; - } -} -var AWithOptionalProp = /** @class */ (function () { - function AWithOptionalProp() { - } - return AWithOptionalProp; -}()); -var BWithOptionalProp = /** @class */ (function () { - function BWithOptionalProp() { - } - return BWithOptionalProp; -}()); -function positiveTestClassesWithOptionalProperties(x) { - if ("a" in x) { - x.a = "1"; - } - else { - x.b = "1"; - } -} -var AWithMethod = /** @class */ (function () { - function AWithMethod() { - } - AWithMethod.prototype.a = function () { return ""; }; - return AWithMethod; -}()); -var BWithMethod = /** @class */ (function () { - function BWithMethod() { - } - BWithMethod.prototype.b = function () { return ""; }; - return BWithMethod; -}()); -function negativeTestClassesWithMembers(x) { - if ("a" in x) { - x.a(); - x.b(); - } - else { - } -} -function negativeTestClassesWithMemberMissingInBothClasses(x) { - if ("c" in x) { - x.a(); - x.b(); - } - else { - x.a(); - x.b(); - } -} -var C = /** @class */ (function () { - function C() { - } - return C; -}()); -var D = /** @class */ (function () { - function D() { - } - return D; -}()); -function negativeMultipleClassesTest(x) { - if ("a" in x) { - x.b = "1"; - } - else { - x.a = "1"; - } -} -var ClassWithUnionProp = /** @class */ (function () { - function ClassWithUnionProp() { - } - return ClassWithUnionProp; -}()); -function negativePropTest(x) { - if ("a" in x.prop) { - var y = x.prop.b; - } - else { - var z = x.prop.a; - } -} -var NegativeClassTest = /** @class */ (function () { - function NegativeClassTest() { - } - NegativeClassTest.prototype.inThis = function () { - if ("a" in this.prop) { - var z = this.prop.b; - } - else { - var y = this.prop.a; - } - }; - return NegativeClassTest; -}()); -var UnreachableCodeDetection = /** @class */ (function () { - function UnreachableCodeDetection() { - } - UnreachableCodeDetection.prototype.inThis = function () { - if ("a" in this) { - } - else { - var y = this.a; - } - }; - return UnreachableCodeDetection; -}()); -function positiveIntersectionTest(x) { - if ("a" in x) { - var s = x.a; - } - else { - var n = x; - } -} -if ('extra' in error) { - error; // Still Error -} -else { - error; // Error -} -function narrowsToNever(x) { - var v; - if ("l" in x) { - v = x.l; - } - else if ("r" in x) { - v = x.r; - } - else { - v = x; - } - return v; -} -if (isAOrB(x)) { - if ("aProp" in x) { - x.aProp; - } - else if ("bProp" in x) { - x.bProp; - } - // x is never because of the type predicate from unknown - else if ("cProp" in x) { - var _never = x; - } -} -function negativeIntersectionTest() { - if ("ontouchstart" in window) { - window.ontouchstart; - } - else { - window.ontouchstart; - } -} diff --git a/tests/baselines/reference/inKeywordTypeguard.types b/tests/baselines/reference/inKeywordTypeguard.types deleted file mode 100644 index cb0c545088822..0000000000000 --- a/tests/baselines/reference/inKeywordTypeguard.types +++ /dev/null @@ -1,454 +0,0 @@ -=== tests/cases/compiler/inKeywordTypeguard.ts === -class A { a: string; } ->A : A ->a : string - -class B { b: string; } ->B : B ->b : string - -function negativeClassesTest(x: A | B) { ->negativeClassesTest : (x: A | B) => void ->x : A | B - - if ("a" in x) { ->"a" in x : boolean ->"a" : "a" ->x : A | B - - x.b = "1"; ->x.b = "1" : "1" ->x.b : any ->x : A ->b : any ->"1" : "1" - - } else { - x.a = "1"; ->x.a = "1" : "1" ->x.a : any ->x : B ->a : any ->"1" : "1" - } -} - -function positiveClassesTest(x: A | B) { ->positiveClassesTest : (x: A | B) => void ->x : A | B - - if ("a" in x) { ->"a" in x : boolean ->"a" : "a" ->x : A | B - - x.b = "1"; ->x.b = "1" : "1" ->x.b : any ->x : A ->b : any ->"1" : "1" - - } else { - x.a = "1"; ->x.a = "1" : "1" ->x.a : any ->x : B ->a : any ->"1" : "1" - } -} - -class AWithOptionalProp { a?: string; } ->AWithOptionalProp : AWithOptionalProp ->a : string - -class BWithOptionalProp { b?: string; } ->BWithOptionalProp : BWithOptionalProp ->b : string - -function positiveTestClassesWithOptionalProperties(x: AWithOptionalProp | BWithOptionalProp) { ->positiveTestClassesWithOptionalProperties : (x: AWithOptionalProp | BWithOptionalProp) => void ->x : AWithOptionalProp | BWithOptionalProp - - if ("a" in x) { ->"a" in x : boolean ->"a" : "a" ->x : AWithOptionalProp | BWithOptionalProp - - x.a = "1"; ->x.a = "1" : "1" ->x.a : string ->x : AWithOptionalProp ->a : string ->"1" : "1" - - } else { - x.b = "1"; ->x.b = "1" : "1" ->x.b : any ->x : AWithOptionalProp | BWithOptionalProp ->b : any ->"1" : "1" - } -} - -class AWithMethod { ->AWithMethod : AWithMethod - - a(): string { return ""; } ->a : () => string ->"" : "" -} - -class BWithMethod { ->BWithMethod : BWithMethod - - b(): string { return ""; } ->b : () => string ->"" : "" -} - -function negativeTestClassesWithMembers(x: AWithMethod | BWithMethod) { ->negativeTestClassesWithMembers : (x: AWithMethod | BWithMethod) => void ->x : AWithMethod | BWithMethod - - if ("a" in x) { ->"a" in x : boolean ->"a" : "a" ->x : AWithMethod | BWithMethod - - x.a(); ->x.a() : string ->x.a : () => string ->x : AWithMethod ->a : () => string - - x.b(); ->x.b() : any ->x.b : any ->x : AWithMethod ->b : any - - } else { - } -} - -function negativeTestClassesWithMemberMissingInBothClasses(x: AWithMethod | BWithMethod) { ->negativeTestClassesWithMemberMissingInBothClasses : (x: AWithMethod | BWithMethod) => void ->x : AWithMethod | BWithMethod - - if ("c" in x) { ->"c" in x : boolean ->"c" : "c" ->x : AWithMethod | BWithMethod - - x.a(); ->x.a() : any ->x.a : any ->x : never ->a : any - - x.b(); ->x.b() : any ->x.b : any ->x : never ->b : any - - } else { - x.a(); ->x.a() : any ->x.a : any ->x : AWithMethod | BWithMethod ->a : any - - x.b(); ->x.b() : any ->x.b : any ->x : AWithMethod | BWithMethod ->b : any - } -} - -class C { a: string; } ->C : C ->a : string - -class D { a: string; } ->D : D ->a : string - -function negativeMultipleClassesTest(x: A | B | C | D) { ->negativeMultipleClassesTest : (x: A | B | C | D) => void ->x : A | B | C | D - - if ("a" in x) { ->"a" in x : boolean ->"a" : "a" ->x : A | B | C | D - - x.b = "1"; ->x.b = "1" : "1" ->x.b : any ->x : A | C | D ->b : any ->"1" : "1" - - } else { - x.a = "1"; ->x.a = "1" : "1" ->x.a : any ->x : B ->a : any ->"1" : "1" - } -} - -class ClassWithUnionProp { prop: A | B } ->ClassWithUnionProp : ClassWithUnionProp ->prop : A | B - -function negativePropTest(x: ClassWithUnionProp) { ->negativePropTest : (x: ClassWithUnionProp) => void ->x : ClassWithUnionProp - - if ("a" in x.prop) { ->"a" in x.prop : boolean ->"a" : "a" ->x.prop : A | B ->x : ClassWithUnionProp ->prop : A | B - - let y: string = x.prop.b; ->y : string ->x.prop.b : any ->x.prop : A ->x : ClassWithUnionProp ->prop : A ->b : any - - } else { - let z: string = x.prop.a; ->z : string ->x.prop.a : any ->x.prop : B ->x : ClassWithUnionProp ->prop : B ->a : any - } -} - -class NegativeClassTest { ->NegativeClassTest : NegativeClassTest - - protected prop: A | B; ->prop : A | B - - inThis() { ->inThis : () => void - - if ("a" in this.prop) { ->"a" in this.prop : boolean ->"a" : "a" ->this.prop : A | B ->this : this ->prop : A | B - - let z: number = this.prop.b; ->z : number ->this.prop.b : any ->this.prop : A ->this : this ->prop : A ->b : any - - } else { - let y: string = this.prop.a; ->y : string ->this.prop.a : any ->this.prop : B ->this : this ->prop : B ->a : any - } - } -} - -class UnreachableCodeDetection { ->UnreachableCodeDetection : UnreachableCodeDetection - - a: string; ->a : string - - inThis() { ->inThis : () => void - - if ("a" in this) { ->"a" in this : boolean ->"a" : "a" ->this : this - - } else { - let y = this.a; ->y : any ->this.a : any ->this : never ->a : any - } - } -} - -function positiveIntersectionTest(x: { a: string } & { b: string }) { ->positiveIntersectionTest : (x: { a: string;} & { b: string;}) => void ->x : { a: string; } & { b: string; } ->a : string ->b : string - - if ("a" in x) { ->"a" in x : boolean ->"a" : "a" ->x : { a: string; } & { b: string; } - - let s: string = x.a; ->s : string ->x.a : string ->x : { a: string; } & { b: string; } ->a : string - - } else { - let n: never = x; ->n : never ->x : never - } -} - -// Repro from #38608 -declare const error: Error; ->error : Error - -if ('extra' in error) { ->'extra' in error : boolean ->'extra' : "extra" ->error : Error - - error // Still Error ->error : Error - -} else { - error // Error ->error : Error -} - -function narrowsToNever(x: { l: number } | { r: number }) { ->narrowsToNever : (x: { l: number;} | { r: number;}) => number ->x : { l: number; } | { r: number; } ->l : number ->r : number - - let v: number; ->v : number - - if ("l" in x) { ->"l" in x : boolean ->"l" : "l" ->x : { l: number; } | { r: number; } - - v = x.l; ->v = x.l : number ->v : number ->x.l : number ->x : { l: number; } ->l : number - } - else if ("r" in x) { ->"r" in x : boolean ->"r" : "r" ->x : { r: number; } - - v = x.r; ->v = x.r : number ->v : number ->x.r : number ->x : { r: number; } ->r : number - } - else { - v = x ->v = x : never ->v : number ->x : never - } - return v; ->v : number -} - -type AOrB = { aProp: number } | { bProp: number }; ->AOrB : { aProp: number; } | { bProp: number; } ->aProp : number ->bProp : number - -declare function isAOrB(x: unknown): x is AOrB; ->isAOrB : (x: unknown) => x is AOrB ->x : unknown - -declare var x: unknown; ->x : unknown - -if (isAOrB(x)) { ->isAOrB(x) : boolean ->isAOrB : (x: unknown) => x is AOrB ->x : unknown - - if ("aProp" in x) { ->"aProp" in x : boolean ->"aProp" : "aProp" ->x : AOrB - - x.aProp; ->x.aProp : number ->x : { aProp: number; } ->aProp : number - } - else if ("bProp" in x) { ->"bProp" in x : boolean ->"bProp" : "bProp" ->x : { bProp: number; } - - x.bProp; ->x.bProp : number ->x : { bProp: number; } ->bProp : number - } - // x is never because of the type predicate from unknown - else if ("cProp" in x) { ->"cProp" in x : boolean ->"cProp" : "cProp" ->x : never - - const _never: never = x; ->_never : never ->x : never - } -} - -function negativeIntersectionTest() { ->negativeIntersectionTest : () => void - - if ("ontouchstart" in window) { ->"ontouchstart" in window : boolean ->"ontouchstart" : "ontouchstart" ->window : Window & typeof globalThis - - window.ontouchstart ->window.ontouchstart : ((this: GlobalEventHandlers, ev: TouchEvent) => any) & ((this: Window, ev: TouchEvent) => any) ->window : Window & typeof globalThis ->ontouchstart : ((this: GlobalEventHandlers, ev: TouchEvent) => any) & ((this: Window, ev: TouchEvent) => any) - - } else { - window.ontouchstart ->window.ontouchstart : ((this: GlobalEventHandlers, ev: TouchEvent) => any) & ((this: Window, ev: TouchEvent) => any) ->window : Window & typeof globalThis ->ontouchstart : ((this: GlobalEventHandlers, ev: TouchEvent) => any) & ((this: Window, ev: TouchEvent) => any) - } -} - diff --git a/tests/baselines/reference/inOperator.errors.txt b/tests/baselines/reference/inOperator.errors.txt index a3f09a23643d6..a1cdcbc181742 100644 --- a/tests/baselines/reference/inOperator.errors.txt +++ b/tests/baselines/reference/inOperator.errors.txt @@ -1,4 +1,4 @@ -tests/cases/compiler/inOperator.ts(7,15): error TS2361: The right-hand side of an 'in' expression must not be a primitive. +tests/cases/compiler/inOperator.ts(7,15): error TS2322: Type 'number' is not assignable to type 'object'. ==== tests/cases/compiler/inOperator.ts (1 errors) ==== @@ -10,7 +10,7 @@ tests/cases/compiler/inOperator.ts(7,15): error TS2361: The right-hand side of a var b = '' in 0; ~ -!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. +!!! error TS2322: Type 'number' is not assignable to type 'object'. var c: any; var y: number; diff --git a/tests/baselines/reference/inOperatorWithInvalidOperands.errors.txt b/tests/baselines/reference/inOperatorWithInvalidOperands.errors.txt index 6b47a8b16749b..354c2c3c68f0e 100644 --- a/tests/baselines/reference/inOperatorWithInvalidOperands.errors.txt +++ b/tests/baselines/reference/inOperatorWithInvalidOperands.errors.txt @@ -1,24 +1,26 @@ -tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(15,11): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. -tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(16,11): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. -tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(17,11): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. +tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(15,11): error TS2322: Type 'boolean' is not assignable to type 'string | number | symbol'. +tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(16,11): error TS2322: Type 'void' is not assignable to type 'string | number | symbol'. +tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(17,11): error TS2322: Type '{}' is not assignable to type 'string | number | symbol'. tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(19,11): error TS18050: The value 'null' cannot be used here. tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(20,11): error TS18050: The value 'undefined' cannot be used here. -tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(22,11): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. -tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(23,11): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. -tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(24,12): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. -tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(25,12): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. -tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(35,16): error TS2361: The right-hand side of an 'in' expression must not be a primitive. -tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(36,16): error TS2361: The right-hand side of an 'in' expression must not be a primitive. -tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(37,16): error TS2361: The right-hand side of an 'in' expression must not be a primitive. -tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(38,16): error TS2361: The right-hand side of an 'in' expression must not be a primitive. -tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(39,16): error TS2361: The right-hand side of an 'in' expression must not be a primitive. -tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(40,16): error TS2361: The right-hand side of an 'in' expression must not be a primitive. -tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(41,16): error TS2361: The right-hand side of an 'in' expression must not be a primitive. -tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(42,16): error TS2361: The right-hand side of an 'in' expression must not be a primitive. +tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(22,11): error TS2322: Type 'boolean' is not assignable to type 'string | number | symbol'. +tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(23,11): error TS2322: Type '{}' is not assignable to type 'string | number | symbol'. +tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(24,12): error TS2322: Type 'string | Foo' is not assignable to type 'string | number | symbol'. + Type 'Foo' is not assignable to type 'string | number | symbol'. +tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(25,12): error TS2322: Type 'Foo' is not assignable to type 'string | number | symbol'. +tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(35,16): error TS2322: Type 'number' is not assignable to type 'object'. +tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(36,16): error TS2322: Type 'boolean' is not assignable to type 'object'. +tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(37,16): error TS2322: Type 'string' is not assignable to type 'object'. +tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(38,16): error TS2322: Type 'void' is not assignable to type 'object'. +tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(39,16): error TS2322: Type 'string | number' is not assignable to type 'object'. + Type 'string' is not assignable to type 'object'. +tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(40,16): error TS2322: Type 'number' is not assignable to type 'object'. +tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(41,16): error TS2322: Type 'boolean' is not assignable to type 'object'. +tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(42,16): error TS2322: Type 'string' is not assignable to type 'object'. tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(43,16): error TS18050: The value 'null' cannot be used here. tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(44,17): error TS18050: The value 'undefined' cannot be used here. -tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(47,11): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. -tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(47,17): error TS2361: The right-hand side of an 'in' expression must not be a primitive. +tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(47,11): error TS2322: Type '{}' is not assignable to type 'string | number | symbol'. +tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(47,17): error TS2322: Type 'string' is not assignable to type 'object'. ==== tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts (21 errors) ==== @@ -38,13 +40,13 @@ tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInv var ra1 = a1 in x; ~~ -!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. +!!! error TS2322: Type 'boolean' is not assignable to type 'string | number | symbol'. var ra2 = a2 in x; ~~ -!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. +!!! error TS2322: Type 'void' is not assignable to type 'string | number | symbol'. var ra3 = a3 in x; ~~ -!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. +!!! error TS2322: Type '{}' is not assignable to type 'string | number | symbol'. var ra4 = a4 in x; var ra5 = null in x; ~~~~ @@ -55,16 +57,17 @@ tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInv var ra7 = E.a in x; var ra8 = false in x; ~~~~~ -!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. +!!! error TS2322: Type 'boolean' is not assignable to type 'string | number | symbol'. var ra9 = {} in x; ~~ -!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. +!!! error TS2322: Type '{}' is not assignable to type 'string | number | symbol'. var ra10 = a5 in x; ~~ -!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. +!!! error TS2322: Type 'string | Foo' is not assignable to type 'string | number | symbol'. +!!! error TS2322: Type 'Foo' is not assignable to type 'string | number | symbol'. var ra11 = a6 in x; ~~ -!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. +!!! error TS2322: Type 'Foo' is not assignable to type 'string | number | symbol'. // invalid right operands // the right operand is required to be of type Any, an object type, or a type parameter type @@ -76,28 +79,29 @@ tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInv var rb1 = x in b1; ~~ -!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. +!!! error TS2322: Type 'number' is not assignable to type 'object'. var rb2 = x in b2; ~~ -!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. +!!! error TS2322: Type 'boolean' is not assignable to type 'object'. var rb3 = x in b3; ~~ -!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. +!!! error TS2322: Type 'string' is not assignable to type 'object'. var rb4 = x in b4; ~~ -!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. +!!! error TS2322: Type 'void' is not assignable to type 'object'. var rb5 = x in b5; ~~ -!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. +!!! error TS2322: Type 'string | number' is not assignable to type 'object'. +!!! error TS2322: Type 'string' is not assignable to type 'object'. var rb6 = x in 0; ~ -!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. +!!! error TS2322: Type 'number' is not assignable to type 'object'. var rb7 = x in false; ~~~~~ -!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. +!!! error TS2322: Type 'boolean' is not assignable to type 'object'. var rb8 = x in ''; ~~ -!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. +!!! error TS2322: Type 'string' is not assignable to type 'object'. var rb9 = x in null; ~~~~ !!! error TS18050: The value 'null' cannot be used here. @@ -108,6 +112,6 @@ tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInv // both operands are invalid var rc1 = {} in ''; ~~ -!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. +!!! error TS2322: Type '{}' is not assignable to type 'string | number | symbol'. ~~ -!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. \ No newline at end of file +!!! error TS2322: Type 'string' is not assignable to type 'object'. \ No newline at end of file diff --git a/tests/baselines/reference/inOperatorWithValidOperands.errors.txt b/tests/baselines/reference/inOperatorWithValidOperands.errors.txt new file mode 100644 index 0000000000000..51801beeb658e --- /dev/null +++ b/tests/baselines/reference/inOperatorWithValidOperands.errors.txt @@ -0,0 +1,65 @@ +tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithValidOperands.ts(26,20): error TS2322: Type 'T' is not assignable to type 'object'. +tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithValidOperands.ts(30,20): error TS2322: Type 'T | U' is not assignable to type 'object'. + Type 'T' is not assignable to type 'object'. +tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithValidOperands.ts(34,20): error TS2322: Type 'object | T' is not assignable to type 'object'. + Type 'T' is not assignable to type 'object'. + + +==== tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithValidOperands.ts (3 errors) ==== + var x: any; + + // valid left operands + // the left operand is required to be of type Any, the String primitive type, or the Number primitive type + var a1: string; + var a2: number; + var a3: string | number | symbol; + var a4: any; + + var ra1 = x in x; + var ra2 = a1 in x; + var ra3 = a2 in x; + var ra4 = '' in x; + var ra5 = 0 in x; + var ra6 = a3 in x; + var ra7 = a4 in x; + + // valid right operands + // the right operand is required to be of type Any, an object type, or a type parameter type + var b1: {}; + + var rb1 = x in b1; + var rb2 = x in {}; + + function foo(t: T) { + var rb3 = x in t; + ~ +!!! error TS2322: Type 'T' is not assignable to type 'object'. +!!! related TS2208 tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithValidOperands.ts:25:14: This type parameter might need an `extends object` constraint. + } + + function unionCase(t: T | U) { + var rb4 = x in t; + ~ +!!! error TS2322: Type 'T | U' is not assignable to type 'object'. +!!! error TS2322: Type 'T' is not assignable to type 'object'. +!!! related TS2208 tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithValidOperands.ts:29:20: This type parameter might need an `extends object` constraint. + } + + function unionCase2(t: T | object) { + var rb5 = x in t; + ~ +!!! error TS2322: Type 'object | T' is not assignable to type 'object'. +!!! error TS2322: Type 'T' is not assignable to type 'object'. +!!! related TS2208 tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithValidOperands.ts:33:21: This type parameter might need an `extends object` constraint. + } + + interface X { x: number } + interface Y { y: number } + + var c1: X | Y; + var c2: X; + var c3: Y; + + var rc1 = x in c1; + var rc2 = x in (c2 || c3); + \ No newline at end of file diff --git a/tests/baselines/reference/keyofAndIndexedAccess.errors.txt b/tests/baselines/reference/keyofAndIndexedAccess.errors.txt index 8917c5e588822..cbad8ef2699e8 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess.errors.txt +++ b/tests/baselines/reference/keyofAndIndexedAccess.errors.txt @@ -1,3 +1,10 @@ +tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts(205,24): error TS2322: Type 'T[keyof T]' is not assignable to type 'object'. + Type 'T[string] | T[number] | T[symbol]' is not assignable to type 'object'. + Type 'T[string]' is not assignable to type 'object'. +tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts(211,24): error TS2322: Type 'T[K]' is not assignable to type 'object'. + Type 'T[keyof T]' is not assignable to type 'object'. + Type 'T[string] | T[number] | T[symbol]' is not assignable to type 'object'. + Type 'T[string]' is not assignable to type 'object'. tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts(316,5): error TS2322: Type 'T' is not assignable to type '{}'. tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts(317,5): error TS2322: Type 'T[keyof T]' is not assignable to type '{}'. Type 'T[string] | T[number] | T[symbol]' is not assignable to type '{}'. @@ -6,7 +13,7 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts(318,5): error TS232 Type 'T[keyof T]' is not assignable to type '{}'. -==== tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts (3 errors) ==== +==== tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts (5 errors) ==== class Shape { name: string; width: number; @@ -212,12 +219,21 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts(318,5): error TS232 for (let s in obj[key]) { } const b = "foo" in obj[key]; + ~~~~~~~~ +!!! error TS2322: Type 'T[keyof T]' is not assignable to type 'object'. +!!! error TS2322: Type 'T[string] | T[number] | T[symbol]' is not assignable to type 'object'. +!!! error TS2322: Type 'T[string]' is not assignable to type 'object'. } function f55(obj: T, key: K) { for (let s in obj[key]) { } const b = "foo" in obj[key]; + ~~~~~~~~ +!!! error TS2322: Type 'T[K]' is not assignable to type 'object'. +!!! error TS2322: Type 'T[keyof T]' is not assignable to type 'object'. +!!! error TS2322: Type 'T[string] | T[number] | T[symbol]' is not assignable to type 'object'. +!!! error TS2322: Type 'T[string]' is not assignable to type 'object'. } function f60(source: T, target: T) { diff --git a/tests/baselines/reference/mappedTypeProperties.errors.txt b/tests/baselines/reference/mappedTypeProperties.errors.txt index 9f64d7e856358..cab2d32293b31 100644 --- a/tests/baselines/reference/mappedTypeProperties.errors.txt +++ b/tests/baselines/reference/mappedTypeProperties.errors.txt @@ -10,7 +10,7 @@ tests/cases/conformance/types/mapped/mappedTypeProperties.ts(37,5): error TS7061 tests/cases/conformance/types/mapped/mappedTypeProperties.ts(40,5): error TS1166: A computed property name in a class property declaration must have a simple literal type or a 'unique symbol' type. tests/cases/conformance/types/mapped/mappedTypeProperties.ts(40,6): error TS2304: Cannot find name 'P'. tests/cases/conformance/types/mapped/mappedTypeProperties.ts(40,6): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. -tests/cases/conformance/types/mapped/mappedTypeProperties.ts(40,11): error TS2361: The right-hand side of an 'in' expression must not be a primitive. +tests/cases/conformance/types/mapped/mappedTypeProperties.ts(40,11): error TS2322: Type 'string' is not assignable to type 'object'. tests/cases/conformance/types/mapped/mappedTypeProperties.ts(40,17): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. @@ -80,7 +80,7 @@ tests/cases/conformance/types/mapped/mappedTypeProperties.ts(40,17): error TS236 ~~~~~~~~ !!! error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. ~~~ -!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. +!!! error TS2322: Type 'string' is not assignable to type 'object'. ~~~ !!! error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. } diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).errors.txt b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).errors.txt index 1d00c96fac064..56037f22d8b94 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).errors.txt +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).errors.txt @@ -1,5 +1,5 @@ -tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(20,24): error TS2361: The right-hand side of an 'in' expression must not be a primitive. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(24,14): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(20,24): error TS2322: Type 'number' is not assignable to type 'object'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(24,14): error TS2322: Type 'boolean' is not assignable to type 'string | number | symbol'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(29,21): error TS1005: ';' expected. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(30,21): error TS1005: ';' expected. @@ -26,13 +26,13 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTr v << #field in v << v; // Good precedence (SyntaxError): (v << #field) in (v << v) ~~~~~~ -!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. +!!! error TS2322: Type 'number' is not assignable to type 'object'. v << #field in v == v; // Good precedence (SyntaxError): ((v << #field) in v) == v v == #field in v in v; // Good precedence: v == ((#field in v) in v) ~~~~~~~~~~~ -!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. +!!! error TS2322: Type 'boolean' is not assignable to type 'string | number | symbol'. #field in v && #field in v; // Good precedence: (#field in v) && (#field in v) } diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2022).errors.txt b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2022).errors.txt index 1d00c96fac064..56037f22d8b94 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2022).errors.txt +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2022).errors.txt @@ -1,5 +1,5 @@ -tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(20,24): error TS2361: The right-hand side of an 'in' expression must not be a primitive. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(24,14): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(20,24): error TS2322: Type 'number' is not assignable to type 'object'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(24,14): error TS2322: Type 'boolean' is not assignable to type 'string | number | symbol'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(29,21): error TS1005: ';' expected. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(30,21): error TS1005: ';' expected. @@ -26,13 +26,13 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTr v << #field in v << v; // Good precedence (SyntaxError): (v << #field) in (v << v) ~~~~~~ -!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. +!!! error TS2322: Type 'number' is not assignable to type 'object'. v << #field in v == v; // Good precedence (SyntaxError): ((v << #field) in v) == v v == #field in v in v; // Good precedence: v == ((#field in v) in v) ~~~~~~~~~~~ -!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. +!!! error TS2322: Type 'boolean' is not assignable to type 'string | number | symbol'. #field in v && #field in v; // Good precedence: (#field in v) && (#field in v) } diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).errors.txt b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).errors.txt index 1d00c96fac064..56037f22d8b94 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).errors.txt +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).errors.txt @@ -1,5 +1,5 @@ -tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(20,24): error TS2361: The right-hand side of an 'in' expression must not be a primitive. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(24,14): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(20,24): error TS2322: Type 'number' is not assignable to type 'object'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(24,14): error TS2322: Type 'boolean' is not assignable to type 'string | number | symbol'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(29,21): error TS1005: ';' expected. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(30,21): error TS1005: ';' expected. @@ -26,13 +26,13 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTr v << #field in v << v; // Good precedence (SyntaxError): (v << #field) in (v << v) ~~~~~~ -!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. +!!! error TS2322: Type 'number' is not assignable to type 'object'. v << #field in v == v; // Good precedence (SyntaxError): ((v << #field) in v) == v v == #field in v in v; // Good precedence: v == ((#field in v) in v) ~~~~~~~~~~~ -!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. +!!! error TS2322: Type 'boolean' is not assignable to type 'string | number | symbol'. #field in v && #field in v; // Good precedence: (#field in v) && (#field in v) } diff --git a/tests/baselines/reference/symbolType2.errors.txt b/tests/baselines/reference/symbolType2.errors.txt index 4c63d850b7b56..212fd9939b137 100644 --- a/tests/baselines/reference/symbolType2.errors.txt +++ b/tests/baselines/reference/symbolType2.errors.txt @@ -1,8 +1,8 @@ -tests/cases/conformance/es6/Symbols/symbolType2.ts(2,7): error TS2361: The right-hand side of an 'in' expression must not be a primitive. +tests/cases/conformance/es6/Symbols/symbolType2.ts(2,7): error TS2322: Type 'typeof Symbol.toPrimitive' is not assignable to type 'object'. ==== tests/cases/conformance/es6/Symbols/symbolType2.ts (1 errors) ==== Symbol.isConcatSpreadable in {}; "" in Symbol.toPrimitive; ~~~~~~~~~~~~~~~~~~ -!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. \ No newline at end of file +!!! error TS2322: Type 'typeof Symbol.toPrimitive' is not assignable to type 'object'. \ No newline at end of file diff --git a/tests/cases/compiler/inKeywordTypeguard.ts b/tests/cases/compiler/inKeywordTypeguard.ts index e853d1935bee7..c6c697613ba62 100644 --- a/tests/cases/compiler/inKeywordTypeguard.ts +++ b/tests/cases/compiler/inKeywordTypeguard.ts @@ -1,3 +1,6 @@ +// @strict: true, false +// @target: es2015 + class A { a: string; } class B { b: string; } @@ -150,3 +153,130 @@ function negativeIntersectionTest() { window.ontouchstart } } + +function f1(x: unknown) { + if ("a" in x) { + x.a; + } + if (x && "a" in x) { + x.a; + } + if (x && typeof x === "object" && "a" in x) { + x.a; + } + if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) { + x.a; + x.b; + x.c; + } +} + +function f2(x: object) { + if ("a" in x) { + x.a; + } + if ("a" in x && "b" in x && "c" in x) { + x.a; + x.b; + x.c; + } +} + +function f3(x: T) { + if ("a" in x) { + x.a; + } + if (x && "a" in x) { + x.a; + } + if (x && typeof x === "object" && "a" in x) { + x.a; + } + if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) { + x.a; + x.b; + x.c; + } +} + +function f4(x: { a: string }) { + if ("a" in x) { + x.a; + } + if ("a" in x && "b" in x && "c" in x) { + x.a; + x.b; + x.c; + } +} + +function f5(x: { a: string } | { b: string }) { + if ("a" in x) { + x; // { a: string } + } + else if ("b" in x) { + x; // { b: string } + } + else { + x; // never + } +} + +function f6(x: { a: string } | { b: string }) { + if ("a" in x) { + x; // { a: string } + } + else if ("a" in x) { + x; // { b: string } & Record<"a", unknown> + } + else { + x; // { b: string } + } +} + +// Object and corresponding intersection should narrow the same + +function f7(x: { a: string, b: number }, y: { a: string } & { b: number }) { + if ("a" in x) { + x; + } + else { + x; // never + } + if ("a" in y) { + y; + } + else { + y; // never + } +} + +const sym = Symbol(); + +function f8(x: object) { + if ("a" in x && 1 in x && sym in x) { + x.a; + x["a"]; + x[1]; + x["1"]; + x[sym]; + } +} + +function f9(x: object) { + if ("a" in x && "1" in x && sym in x) { + x.a; + x["a"]; + x[1]; + x["1"]; + x[sym]; + } +} + +// Repro from #50639 + +function foo(value: A) { + if (typeof value === "object" && value !== null && "prop" in value) { + value; // A & object & Record<"prop", unknown> + } +}