From 88927b55f6d1b2193a6627631e99637a7582ad74 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 6 Sep 2022 16:58:57 -0700 Subject: [PATCH 01/15] Improve checking of `in` operator --- src/compiler/checker.ts | 68 ++++++++++------------------ src/compiler/diagnosticMessages.json | 8 ---- 2 files changed, 25 insertions(+), 51 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1ac041a3653c2..ef4e0295a738e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -996,6 +996,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 @@ -14287,6 +14288,10 @@ namespace ts { return (deferredGlobalBigIntType ||= getGlobalType("BigInt" as __String, /*arity*/ 0, /*reportErrors*/ false)) || emptyObjectType; } + function getGlobalRecordSymbol() { + return deferredGlobalRecordSymbol ||= getGlobalTypeAliasSymbol("Record" as __String, /*arity*/ 2, /*reportErrors*/ true) || unknownSymbol; + } + /** * Instantiates a global type that is generic with some element type, and returns that instantiation. */ @@ -25142,19 +25147,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 - || isThisTypeParameter(type) - || type.flags & TypeFlags.Intersection && every((type as IntersectionType).types, t => t.symbol !== globalThisSymbol)) { + 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 !== unknownSymbol) { + const record = getTypeAliasInstantiation(recordSymbol, [getStringLiteralType(unescapeLeadingUnderscores(name)), unknownType]); + return mapType(type, t => t.flags & TypeFlags.NonPrimitive ? record : getIntersectionType([t, record])); + } + } return type; } @@ -33765,43 +33778,12 @@ 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); } - 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 type of the right operand must be assignable to 'object'. + checkTypeAssignableTo(checkNonNullType(rightType, right), nonPrimitiveType, right); // 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 6cec02b9e1a41..825570eb1babf 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1836,14 +1836,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 From 9ed6357afeba1280e385e832cdb9ff744884a012 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 6 Sep 2022 16:59:25 -0700 Subject: [PATCH 02/15] Accept new baselines --- ...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/inKeywordTypeguard.errors.txt | 17 +- .../reference/inKeywordTypeguard.symbols | 2 - .../reference/inKeywordTypeguard.types | 12 +- .../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 +- 19 files changed, 381 insertions(+), 96 deletions(-) create mode 100644 tests/baselines/reference/conditionalTypeDoesntSpinForever.errors.txt create mode 100644 tests/baselines/reference/inOperatorWithValidOperands.errors.txt 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..3223c317aa089 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 & Record<"d", unknown>) | (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 & Record<"d", unknown>) | (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/inKeywordTypeguard.errors.txt b/tests/baselines/reference/inKeywordTypeguard.errors.txt index cfb2b090a7af5..d28ec909fec88 100644 --- a/tests/baselines/reference/inKeywordTypeguard.errors.txt +++ b/tests/baselines/reference/inKeywordTypeguard.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 & Record<"c", unknown>) | (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 & Record<"c", unknown>) | (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,10 @@ 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 (17 errors) ==== +==== tests/cases/compiler/inKeywordTypeguard.ts (18 errors) ==== class A { a: string; } class B { b: string; } @@ -85,10 +88,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 & Record<"c", unknown>) | (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 & Record<"c", unknown>) | (BWithMethod & Record<"c", unknown>)'. +!!! error TS2339: Property 'b' does not exist on type 'AWithMethod & Record<"c", unknown>'. } else { x.a(); ~ @@ -210,6 +215,8 @@ 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'. } } \ No newline at end of file diff --git a/tests/baselines/reference/inKeywordTypeguard.symbols b/tests/baselines/reference/inKeywordTypeguard.symbols index 91d9c42597740..7b51014759a1b 100644 --- a/tests/baselines/reference/inKeywordTypeguard.symbols +++ b/tests/baselines/reference/inKeywordTypeguard.symbols @@ -371,9 +371,7 @@ 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, --, --)) } } diff --git a/tests/baselines/reference/inKeywordTypeguard.types b/tests/baselines/reference/inKeywordTypeguard.types index cb0c545088822..1a72e4c95cbef 100644 --- a/tests/baselines/reference/inKeywordTypeguard.types +++ b/tests/baselines/reference/inKeywordTypeguard.types @@ -146,13 +146,13 @@ function negativeTestClassesWithMemberMissingInBothClasses(x: AWithMethod | BWit x.a(); >x.a() : any >x.a : any ->x : never +>x : (AWithMethod & Record<"c", unknown>) | (BWithMethod & Record<"c", unknown>) >a : any x.b(); >x.b() : any >x.b : any ->x : never +>x : (AWithMethod & Record<"c", unknown>) | (BWithMethod & Record<"c", unknown>) >b : any } else { @@ -332,7 +332,7 @@ if ('extra' in error) { >error : Error error // Still Error ->error : Error +>error : Error & Record<"extra", unknown> } else { error // Error @@ -446,9 +446,9 @@ function negativeIntersectionTest() { } 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) +>window.ontouchstart : any +>window : never +>ontouchstart : 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 7984645b6d648..e1be085341f0d 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 TS2531: Object is possibly 'null'. tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(20,11): error TS2532: Object is possibly 'undefined'. -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 TS2531: Object is possibly 'null'. tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(44,17): error TS2532: Object is possibly 'undefined'. -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 TS2531: Object is possibly 'null'. @@ -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 From 335d7c817337121c0a982e0dcff3e8452741ef29 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 6 Sep 2022 17:17:59 -0700 Subject: [PATCH 03/15] Add tests --- ...nKeywordTypeguard(strict=false).errors.txt | 341 ++++++++ .../inKeywordTypeguard(strict=false).js | 533 ++++++++++++ .../inKeywordTypeguard(strict=false).symbols | 657 ++++++++++++++ .../inKeywordTypeguard(strict=false).types | 821 ++++++++++++++++++ ...inKeywordTypeguard(strict=true).errors.txt | 355 ++++++++ .../inKeywordTypeguard(strict=true).js | 534 ++++++++++++ .../inKeywordTypeguard(strict=true).symbols | 657 ++++++++++++++ .../inKeywordTypeguard(strict=true).types | 821 ++++++++++++++++++ tests/cases/compiler/inKeywordTypeguard.ts | 107 +++ 9 files changed, 4826 insertions(+) create mode 100644 tests/baselines/reference/inKeywordTypeguard(strict=false).errors.txt create mode 100644 tests/baselines/reference/inKeywordTypeguard(strict=false).js create mode 100644 tests/baselines/reference/inKeywordTypeguard(strict=false).symbols 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 diff --git a/tests/baselines/reference/inKeywordTypeguard(strict=false).errors.txt b/tests/baselines/reference/inKeywordTypeguard(strict=false).errors.txt new file mode 100644 index 0000000000000..ec3925bb33223 --- /dev/null +++ b/tests/baselines/reference/inKeywordTypeguard(strict=false).errors.txt @@ -0,0 +1,341 @@ +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 & Record<"c", unknown>) | (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 & Record<"c", unknown>) | (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(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(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(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 (22 errors) ==== + class A { a: string; } + class B { b: string; } + + 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 & Record<"c", unknown>) | (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 & Record<"c", unknown>) | (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; } + class D { a: string; } + + 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 } + + 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; + 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; + 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 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 + } + } + + // Repro from #50639 + + function f8(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..297f3c679f52e --- /dev/null +++ b/tests/baselines/reference/inKeywordTypeguard(strict=false).js @@ -0,0 +1,533 @@ +//// [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 + } +} + +// Repro from #50639 + +function f8(value: A) { + if (typeof value === "object" && value !== null && "prop" in value) { + value; // A & object & Record<"prop", unknown> + } +} + + +//// [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; + } +} +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 + } +} +// Repro from #50639 +function f8(value) { + if (typeof value === "object" && value !== null && "prop" in value) { + value; // A & object & Record<"prop", unknown> + } +} diff --git a/tests/baselines/reference/inKeywordTypeguard(strict=false).symbols b/tests/baselines/reference/inKeywordTypeguard(strict=false).symbols new file mode 100644 index 0000000000000..c22686b0c52dd --- /dev/null +++ b/tests/baselines/reference/inKeywordTypeguard(strict=false).symbols @@ -0,0 +1,657 @@ +=== 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)) + } +} + +// Repro from #50639 + +function f8(value: A) { +>f8 : Symbol(f8, Decl(inKeywordTypeguard.ts, 248, 1)) +>A : Symbol(A, Decl(inKeywordTypeguard.ts, 252, 12)) +>value : Symbol(value, Decl(inKeywordTypeguard.ts, 252, 15)) +>A : Symbol(A, Decl(inKeywordTypeguard.ts, 252, 12)) + + if (typeof value === "object" && value !== null && "prop" in value) { +>value : Symbol(value, Decl(inKeywordTypeguard.ts, 252, 15)) +>value : Symbol(value, Decl(inKeywordTypeguard.ts, 252, 15)) +>value : Symbol(value, Decl(inKeywordTypeguard.ts, 252, 15)) + + value; // A & object & Record<"prop", unknown> +>value : Symbol(value, Decl(inKeywordTypeguard.ts, 252, 15)) + } +} + diff --git a/tests/baselines/reference/inKeywordTypeguard(strict=false).types b/tests/baselines/reference/inKeywordTypeguard(strict=false).types new file mode 100644 index 0000000000000..220ea85b8f127 --- /dev/null +++ b/tests/baselines/reference/inKeywordTypeguard(strict=false).types @@ -0,0 +1,821 @@ +=== 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 & Record<"c", unknown>) | (BWithMethod & Record<"c", unknown>) +>a : any + + x.b(); +>x.b() : any +>x.b : any +>x : (AWithMethod & Record<"c", unknown>) | (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 : 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 : Record<"a", unknown> +>"c" in x : boolean +>"c" : "c" +>x : Record<"a", unknown> & Record<"b", unknown> + + x.a; +>x.a : unknown +>x : Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>a : unknown + + x.b; +>x.b : unknown +>x : Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>b : unknown + + x.c; +>x.c : unknown +>x : 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 : 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 : Record<"a", unknown> +>"c" in x : boolean +>"c" : "c" +>x : Record<"a", unknown> & Record<"b", unknown> + + x.a; +>x.a : unknown +>x : Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>a : unknown + + x.b; +>x.b : unknown +>x : Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>b : unknown + + x.c; +>x.c : unknown +>x : 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 + } +} + +// Repro from #50639 + +function f8(value: A) { +>f8 : (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..ebea9fd2a830e --- /dev/null +++ b/tests/baselines/reference/inKeywordTypeguard(strict=true).errors.txt @@ -0,0 +1,355 @@ +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 & Record<"c", unknown>) | (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 & Record<"c", unknown>) | (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 TS2571: Object is of type 'unknown'. +tests/cases/compiler/inKeywordTypeguard.ts(183,16): error TS2322: Type 'T' is not assignable to type 'object'. + + +==== tests/cases/compiler/inKeywordTypeguard.ts (27 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 & Record<"c", unknown>) | (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 & Record<"c", unknown>) | (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 TS2571: Object is of type 'unknown'. + 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) { + ~ +!!! 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) { + 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 + } + } + + // Repro from #50639 + + function f8(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..0d28f7eb91486 --- /dev/null +++ b/tests/baselines/reference/inKeywordTypeguard(strict=true).js @@ -0,0 +1,534 @@ +//// [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 + } +} + +// Repro from #50639 + +function f8(value: A) { + if (typeof value === "object" && value !== null && "prop" in value) { + value; // A & object & Record<"prop", unknown> + } +} + + +//// [inKeywordTypeguard.js] +"use strict"; +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; + } +} +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 + } +} +// Repro from #50639 +function f8(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..c22686b0c52dd --- /dev/null +++ b/tests/baselines/reference/inKeywordTypeguard(strict=true).symbols @@ -0,0 +1,657 @@ +=== 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)) + } +} + +// Repro from #50639 + +function f8(value: A) { +>f8 : Symbol(f8, Decl(inKeywordTypeguard.ts, 248, 1)) +>A : Symbol(A, Decl(inKeywordTypeguard.ts, 252, 12)) +>value : Symbol(value, Decl(inKeywordTypeguard.ts, 252, 15)) +>A : Symbol(A, Decl(inKeywordTypeguard.ts, 252, 12)) + + if (typeof value === "object" && value !== null && "prop" in value) { +>value : Symbol(value, Decl(inKeywordTypeguard.ts, 252, 15)) +>value : Symbol(value, Decl(inKeywordTypeguard.ts, 252, 15)) +>value : Symbol(value, Decl(inKeywordTypeguard.ts, 252, 15)) + + value; // A & object & Record<"prop", unknown> +>value : Symbol(value, Decl(inKeywordTypeguard.ts, 252, 15)) + } +} + diff --git a/tests/baselines/reference/inKeywordTypeguard(strict=true).types b/tests/baselines/reference/inKeywordTypeguard(strict=true).types new file mode 100644 index 0000000000000..d3c3784705756 --- /dev/null +++ b/tests/baselines/reference/inKeywordTypeguard(strict=true).types @@ -0,0 +1,821 @@ +=== 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 & Record<"c", unknown>) | (BWithMethod & Record<"c", unknown>) +>a : any + + x.b(); +>x.b() : any +>x.b : any +>x : (AWithMethod & Record<"c", unknown>) | (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 : 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 : Record<"a", unknown> +>"c" in x : boolean +>"c" : "c" +>x : Record<"a", unknown> & Record<"b", unknown> + + x.a; +>x.a : unknown +>x : Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>a : unknown + + x.b; +>x.b : unknown +>x : Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>b : unknown + + x.c; +>x.c : unknown +>x : 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 : 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 : Record<"a", unknown> +>"c" in x : boolean +>"c" : "c" +>x : Record<"a", unknown> & Record<"b", unknown> + + x.a; +>x.a : unknown +>x : Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>a : unknown + + x.b; +>x.b : unknown +>x : Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>b : unknown + + x.c; +>x.c : unknown +>x : 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 + } +} + +// Repro from #50639 + +function f8(value: A) { +>f8 : (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/cases/compiler/inKeywordTypeguard.ts b/tests/cases/compiler/inKeywordTypeguard.ts index e853d1935bee7..1b47a29888e3f 100644 --- a/tests/cases/compiler/inKeywordTypeguard.ts +++ b/tests/cases/compiler/inKeywordTypeguard.ts @@ -1,3 +1,5 @@ +// @strict: true, false + class A { a: string; } class B { b: string; } @@ -150,3 +152,108 @@ 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 + } +} + +// Repro from #50639 + +function f8(value: A) { + if (typeof value === "object" && value !== null && "prop" in value) { + value; // A & object & Record<"prop", unknown> + } +} From 5d31ba804ac562049e36ca7436cba99f85b2ed57 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 7 Sep 2022 07:00:22 -0700 Subject: [PATCH 04/15] Delete old and accept new baselines --- .../reference/inKeywordAndUnknown.types | 4 +- .../reference/inKeywordTypeguard.errors.txt | 222 --------- .../baselines/reference/inKeywordTypeguard.js | 333 ------------- .../reference/inKeywordTypeguard.symbols | 377 --------------- .../reference/inKeywordTypeguard.types | 454 ------------------ 5 files changed, 2 insertions(+), 1388 deletions(-) delete mode 100644 tests/baselines/reference/inKeywordTypeguard.errors.txt delete mode 100644 tests/baselines/reference/inKeywordTypeguard.js delete mode 100644 tests/baselines/reference/inKeywordTypeguard.symbols delete mode 100644 tests/baselines/reference/inKeywordTypeguard.types 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.errors.txt deleted file mode 100644 index d28ec909fec88..0000000000000 --- a/tests/baselines/reference/inKeywordTypeguard.errors.txt +++ /dev/null @@ -1,222 +0,0 @@ -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 & Record<"c", unknown>) | (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 & Record<"c", unknown>) | (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(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(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(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 (18 errors) ==== - class A { a: string; } - class B { b: string; } - - 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 & Record<"c", unknown>) | (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 & Record<"c", unknown>) | (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; } - class D { a: string; } - - 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 } - - 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; - 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; - 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'. - } - } - \ No newline at end of file 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.symbols b/tests/baselines/reference/inKeywordTypeguard.symbols deleted file mode 100644 index 7b51014759a1b..0000000000000 --- a/tests/baselines/reference/inKeywordTypeguard.symbols +++ /dev/null @@ -1,377 +0,0 @@ -=== 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, --, --)) - } -} - diff --git a/tests/baselines/reference/inKeywordTypeguard.types b/tests/baselines/reference/inKeywordTypeguard.types deleted file mode 100644 index 1a72e4c95cbef..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 : (AWithMethod & Record<"c", unknown>) | (BWithMethod & Record<"c", unknown>) ->a : any - - x.b(); ->x.b() : any ->x.b : any ->x : (AWithMethod & Record<"c", unknown>) | (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 - } -} - From 498d9b544dfa13b6128fbb061929ce335ba42a60 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 8 Sep 2022 07:10:13 -0700 Subject: [PATCH 05/15] Disallow right operand of type '{}' --- src/compiler/checker.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f9914b71405fa..85fac78801be1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -33774,6 +33774,12 @@ namespace ts { return booleanType; } + function hasEmptyAnonymousObjectType(type: Type): boolean { + const t = getBaseConstraintOrType(type); + return isEmptyAnonymousObjectType(t) && !(getObjectFlags(t) & ObjectFlags.FreshLiteral) || + !!(t.flags & TypeFlags.UnionOrIntersection && some((t as UnionType).types, hasEmptyAnonymousObjectType)); + } + function checkInExpression(left: Expression, right: Expression, leftType: Type, rightType: Type): Type { if (leftType === silentNeverType || rightType === silentNeverType) { return silentNeverType; @@ -33794,7 +33800,11 @@ namespace ts { checkTypeAssignableTo(checkNonNullType(leftType, left), stringNumberSymbolType, left); } // The type of the right operand must be assignable to 'object'. - checkTypeAssignableTo(checkNonNullType(rightType, right), nonPrimitiveType, right); + if (checkTypeAssignableTo(checkNonNullType(rightType, right), nonPrimitiveType, right)) { + if (hasEmptyAnonymousObjectType(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)); + } + } // The result is always of the Boolean primitive type. return booleanType; } From 7d1564f96988119e730165323627699e618ad072 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 8 Sep 2022 07:10:22 -0700 Subject: [PATCH 06/15] Accept new baselines --- src/compiler/diagnosticMessages.json | 4 +++ ...controlFlowWithTemplateLiterals.errors.txt | 16 +++++++++ .../reference/inKeywordAndUnknown.errors.txt | 26 ++++++++++++++ ...inKeywordTypeguard(strict=true).errors.txt | 8 ++++- .../inOperatorWithValidOperands.errors.txt | 5 ++- ...meInInExpression(target=es2022).errors.txt | 5 ++- ...meInInExpression(target=esnext).errors.txt | 5 ++- .../typePredicateWithThisParameter.errors.txt | 36 +++++++++++++++++++ 8 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 tests/baselines/reference/controlFlowWithTemplateLiterals.errors.txt create mode 100644 tests/baselines/reference/inKeywordAndUnknown.errors.txt create mode 100644 tests/baselines/reference/typePredicateWithThisParameter.errors.txt diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 825570eb1babf..1e8c55ec0a205 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2829,6 +2829,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/controlFlowWithTemplateLiterals.errors.txt b/tests/baselines/reference/controlFlowWithTemplateLiterals.errors.txt new file mode 100644 index 0000000000000..034c45eb71e25 --- /dev/null +++ b/tests/baselines/reference/controlFlowWithTemplateLiterals.errors.txt @@ -0,0 +1,16 @@ +tests/cases/conformance/controlFlow/controlFlowWithTemplateLiterals.ts(7,15): error TS2638: Type '{} | { test: string; }' may represent a primitive value, which is not permitted as the right operand of the 'in' operator. + + +==== tests/cases/conformance/controlFlow/controlFlowWithTemplateLiterals.ts (1 errors) ==== + declare const envVar: string | undefined; + if (typeof envVar === `string`) { + envVar.slice(0) + } + + declare const obj: {test: string} | {} + if (`test` in obj) { + ~~~ +!!! error TS2638: Type '{} | { test: string; }' may represent a primitive value, which is not permitted as the right operand of the 'in' operator. + obj.test.slice(0) + } + \ 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..0481ec6929f82 --- /dev/null +++ b/tests/baselines/reference/inKeywordAndUnknown.errors.txt @@ -0,0 +1,26 @@ +tests/cases/compiler/inKeywordAndUnknown.ts(4,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(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 (2 errors) ==== + // Repro from #50531 + + function f(x: {}, y: unknown) { + if (!("a" in x)) { + ~ +!!! error TS2638: Type '{}' may represent a primitive value, which is not permitted as the right operand of the 'in' operator. + 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/inKeywordTypeguard(strict=true).errors.txt b/tests/baselines/reference/inKeywordTypeguard(strict=true).errors.txt index ebea9fd2a830e..078cf138efa28 100644 --- a/tests/baselines/reference/inKeywordTypeguard(strict=true).errors.txt +++ b/tests/baselines/reference/inKeywordTypeguard(strict=true).errors.txt @@ -30,10 +30,12 @@ tests/cases/compiler/inKeywordTypeguard.ts(90,5): error TS2564: Property 'a' has 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 TS2571: Object 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 (27 errors) ==== +==== 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. @@ -250,6 +252,8 @@ tests/cases/compiler/inKeywordTypeguard.ts(183,16): error TS2322: Type 'T' is no 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) { @@ -281,6 +285,8 @@ tests/cases/compiler/inKeywordTypeguard.ts(183,16): error TS2322: Type 'T' is no 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) { diff --git a/tests/baselines/reference/inOperatorWithValidOperands.errors.txt b/tests/baselines/reference/inOperatorWithValidOperands.errors.txt index 51801beeb658e..d607a533b6cd6 100644 --- a/tests/baselines/reference/inOperatorWithValidOperands.errors.txt +++ b/tests/baselines/reference/inOperatorWithValidOperands.errors.txt @@ -1,3 +1,4 @@ +tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithValidOperands.ts(22,16): error TS2638: Type '{}' may represent a primitive value, which is not permitted as the right operand of the 'in' operator. 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'. @@ -5,7 +6,7 @@ tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithVal Type 'T' is not assignable to type 'object'. -==== tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithValidOperands.ts (3 errors) ==== +==== tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithValidOperands.ts (4 errors) ==== var x: any; // valid left operands @@ -28,6 +29,8 @@ tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithVal var b1: {}; var rb1 = x in b1; + ~~ +!!! error TS2638: Type '{}' may represent a primitive value, which is not permitted as the right operand of the 'in' operator. var rb2 = x in {}; function foo(t: T) { diff --git a/tests/baselines/reference/privateNameInInExpression(target=es2022).errors.txt b/tests/baselines/reference/privateNameInInExpression(target=es2022).errors.txt index 514d2a85e84a6..480712c6869cf 100644 --- a/tests/baselines/reference/privateNameInInExpression(target=es2022).errors.txt +++ b/tests/baselines/reference/privateNameInInExpression(target=es2022).errors.txt @@ -1,3 +1,4 @@ +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(12,29): error TS2638: Type '{}' may represent a primitive value, which is not permitted as the right operand of the 'in' operator. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(21,29): error TS2571: Object is of type 'unknown'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(23,19): error TS2339: Property '#fiel' does not exist on type 'any'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(25,20): error TS1451: Private identifiers are only allowed in class bodies and may only be used as part of a class member declaration, property access, or on the left-hand-side of an 'in' expression @@ -7,7 +8,7 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(114,12): error TS18016: Private identifiers are not allowed outside class bodies. -==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (7 errors) ==== +==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (8 errors) ==== class Foo { #field = 1; static #staticField = 2; @@ -20,6 +21,8 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t const b = #field in v.p1.p2; const c = #field in (v as {}); + ~~~~~~~~~ +!!! error TS2638: Type '{}' may represent a primitive value, which is not permitted as the right operand of the 'in' operator. const d = #field in (v as Foo); diff --git a/tests/baselines/reference/privateNameInInExpression(target=esnext).errors.txt b/tests/baselines/reference/privateNameInInExpression(target=esnext).errors.txt index 514d2a85e84a6..480712c6869cf 100644 --- a/tests/baselines/reference/privateNameInInExpression(target=esnext).errors.txt +++ b/tests/baselines/reference/privateNameInInExpression(target=esnext).errors.txt @@ -1,3 +1,4 @@ +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(12,29): error TS2638: Type '{}' may represent a primitive value, which is not permitted as the right operand of the 'in' operator. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(21,29): error TS2571: Object is of type 'unknown'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(23,19): error TS2339: Property '#fiel' does not exist on type 'any'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(25,20): error TS1451: Private identifiers are only allowed in class bodies and may only be used as part of a class member declaration, property access, or on the left-hand-side of an 'in' expression @@ -7,7 +8,7 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(114,12): error TS18016: Private identifiers are not allowed outside class bodies. -==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (7 errors) ==== +==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (8 errors) ==== class Foo { #field = 1; static #staticField = 2; @@ -20,6 +21,8 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t const b = #field in v.p1.p2; const c = #field in (v as {}); + ~~~~~~~~~ +!!! error TS2638: Type '{}' may represent a primitive value, which is not permitted as the right operand of the 'in' operator. const d = #field in (v as Foo); diff --git a/tests/baselines/reference/typePredicateWithThisParameter.errors.txt b/tests/baselines/reference/typePredicateWithThisParameter.errors.txt new file mode 100644 index 0000000000000..45bbdc0c6aeac --- /dev/null +++ b/tests/baselines/reference/typePredicateWithThisParameter.errors.txt @@ -0,0 +1,36 @@ +tests/cases/compiler/typePredicateWithThisParameter.ts(11,21): error TS2638: Type '{}' may represent a primitive value, which is not permitted as the right operand of the 'in' operator. +tests/cases/compiler/typePredicateWithThisParameter.ts(15,21): error TS2638: Type '{}' may represent a primitive value, which is not permitted as the right operand of the 'in' operator. + + +==== tests/cases/compiler/typePredicateWithThisParameter.ts (2 errors) ==== + // Repro from #15310 + + interface Foo { + foo: string; + } + interface Bar { + bar: string; + } + + function isFoo1(object: {}): object is Foo { + return 'foo' in object; + ~~~~~~ +!!! error TS2638: Type '{}' may represent a primitive value, which is not permitted as the right operand of the 'in' operator. + } + + function isFoo2(this: void, object: {}): object is Foo { + return 'foo' in object; + ~~~~~~ +!!! error TS2638: Type '{}' may represent a primitive value, which is not permitted as the right operand of the 'in' operator. + } + + declare let test: Foo | Bar; + + if (isFoo1(test)) { + test.foo = 'hi'; + } + + if (isFoo2(test)) { + test.foo = 'hi'; + } + \ No newline at end of file From 4fe1ab37c4a4fd63d0e79203bbea71cae91273e0 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 8 Sep 2022 08:22:33 -0700 Subject: [PATCH 07/15] Support number and symbol literals --- src/compiler/checker.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 85fac78801be1..0f5e31415f308 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -25152,7 +25152,8 @@ namespace ts { !!getApplicableIndexInfoForName(type, propName) || !assumeTrue; } - function narrowByInKeyword(type: Type, name: __String, assumeTrue: boolean) { + 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 @@ -25164,7 +25165,7 @@ namespace ts { // where X is the name of the property. const recordSymbol = getGlobalRecordSymbol(); if (recordSymbol !== unknownSymbol) { - const record = getTypeAliasInstantiation(recordSymbol, [getStringLiteralType(unescapeLeadingUnderscores(name)), unknownType]); + const record = getTypeAliasInstantiation(recordSymbol, [nameType, unknownType]); return mapType(type, t => t.flags & TypeFlags.NonPrimitive ? record : getIntersectionType([t, record])); } } @@ -25227,15 +25228,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; From 0db3935d5e8fc0a1ea88e6ef57d7cea9f749586e Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 8 Sep 2022 08:22:57 -0700 Subject: [PATCH 08/15] Add tests --- ...nKeywordTypeguard(strict=false).errors.txt | 24 ++- .../inKeywordTypeguard(strict=false).js | 150 +++++++++--------- .../inKeywordTypeguard(strict=false).symbols | 88 ++++++++-- .../inKeywordTypeguard(strict=false).types | 97 ++++++++++- ...inKeywordTypeguard(strict=true).errors.txt | 24 ++- .../inKeywordTypeguard(strict=true).js | 150 +++++++++--------- .../inKeywordTypeguard(strict=true).symbols | 88 ++++++++-- .../inKeywordTypeguard(strict=true).types | 97 ++++++++++- tests/cases/compiler/inKeywordTypeguard.ts | 25 ++- 9 files changed, 576 insertions(+), 167 deletions(-) diff --git a/tests/baselines/reference/inKeywordTypeguard(strict=false).errors.txt b/tests/baselines/reference/inKeywordTypeguard(strict=false).errors.txt index ec3925bb33223..d974b9f4ff3c7 100644 --- a/tests/baselines/reference/inKeywordTypeguard(strict=false).errors.txt +++ b/tests/baselines/reference/inKeywordTypeguard(strict=false).errors.txt @@ -331,9 +331,31 @@ tests/cases/compiler/inKeywordTypeguard.ts(186,21): error TS2322: Type 'T' is no } } + 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 f8(value: A) { + function foo(value: A) { if (typeof value === "object" && value !== null && "prop" in value) { value; // A & object & Record<"prop", unknown> } diff --git a/tests/baselines/reference/inKeywordTypeguard(strict=false).js b/tests/baselines/reference/inKeywordTypeguard(strict=false).js index 297f3c679f52e..08e5a870d5ab1 100644 --- a/tests/baselines/reference/inKeywordTypeguard(strict=false).js +++ b/tests/baselines/reference/inKeywordTypeguard(strict=false).js @@ -249,9 +249,31 @@ function f7(x: { a: string, b: number }, y: { a: string } & { b: number }) { } } +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 f8(value: A) { +function foo(value: A) { if (typeof value === "object" && value !== null && "prop" in value) { value; // A & object & Record<"prop", unknown> } @@ -259,16 +281,10 @@ function f8(value: A) { //// [inKeywordTypeguard.js] -var A = /** @class */ (function () { - function A() { - } - return A; -}()); -var B = /** @class */ (function () { - function B() { - } - return B; -}()); +class A { +} +class B { +} function negativeClassesTest(x) { if ("a" in x) { x.b = "1"; @@ -285,16 +301,10 @@ function positiveClassesTest(x) { x.a = "1"; } } -var AWithOptionalProp = /** @class */ (function () { - function AWithOptionalProp() { - } - return AWithOptionalProp; -}()); -var BWithOptionalProp = /** @class */ (function () { - function BWithOptionalProp() { - } - return BWithOptionalProp; -}()); +class AWithOptionalProp { +} +class BWithOptionalProp { +} function positiveTestClassesWithOptionalProperties(x) { if ("a" in x) { x.a = "1"; @@ -303,18 +313,12 @@ function positiveTestClassesWithOptionalProperties(x) { 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; -}()); +class AWithMethod { + a() { return ""; } +} +class BWithMethod { + b() { return ""; } +} function negativeTestClassesWithMembers(x) { if ("a" in x) { x.a(); @@ -333,16 +337,10 @@ function negativeTestClassesWithMemberMissingInBothClasses(x) { x.b(); } } -var C = /** @class */ (function () { - function C() { - } - return C; -}()); -var D = /** @class */ (function () { - function D() { - } - return D; -}()); +class C { +} +class D { +} function negativeMultipleClassesTest(x) { if ("a" in x) { x.b = "1"; @@ -351,50 +349,41 @@ function negativeMultipleClassesTest(x) { x.a = "1"; } } -var ClassWithUnionProp = /** @class */ (function () { - function ClassWithUnionProp() { - } - return ClassWithUnionProp; -}()); +class ClassWithUnionProp { +} function negativePropTest(x) { if ("a" in x.prop) { - var y = x.prop.b; + let y = x.prop.b; } else { - var z = x.prop.a; + let z = x.prop.a; } } -var NegativeClassTest = /** @class */ (function () { - function NegativeClassTest() { - } - NegativeClassTest.prototype.inThis = function () { +class NegativeClassTest { + inThis() { if ("a" in this.prop) { - var z = this.prop.b; + let z = this.prop.b; } else { - var y = this.prop.a; + let y = this.prop.a; } - }; - return NegativeClassTest; -}()); -var UnreachableCodeDetection = /** @class */ (function () { - function UnreachableCodeDetection() { } - UnreachableCodeDetection.prototype.inThis = function () { +} +class UnreachableCodeDetection { + inThis() { if ("a" in this) { } else { - var y = this.a; + let y = this.a; } - }; - return UnreachableCodeDetection; -}()); + } +} function positiveIntersectionTest(x) { if ("a" in x) { - var s = x.a; + let s = x.a; } else { - var n = x; + let n = x; } } if ('extra' in error) { @@ -404,7 +393,7 @@ else { error; // Error } function narrowsToNever(x) { - var v; + let v; if ("l" in x) { v = x.l; } @@ -425,7 +414,7 @@ if (isAOrB(x)) { } // x is never because of the type predicate from unknown else if ("cProp" in x) { - var _never = x; + const _never = x; } } function negativeIntersectionTest() { @@ -525,8 +514,27 @@ function f7(x, y) { 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 f8(value) { +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=false).symbols b/tests/baselines/reference/inKeywordTypeguard(strict=false).symbols index c22686b0c52dd..33ff5a5749fca 100644 --- a/tests/baselines/reference/inKeywordTypeguard(strict=false).symbols +++ b/tests/baselines/reference/inKeywordTypeguard(strict=false).symbols @@ -637,21 +637,91 @@ function f7(x: { a: string, b: number }, y: { a: string } & { b: number }) { } } +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 f8(value: A) { ->f8 : Symbol(f8, Decl(inKeywordTypeguard.ts, 248, 1)) ->A : Symbol(A, Decl(inKeywordTypeguard.ts, 252, 12)) ->value : Symbol(value, Decl(inKeywordTypeguard.ts, 252, 15)) ->A : Symbol(A, Decl(inKeywordTypeguard.ts, 252, 12)) +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, 252, 15)) ->value : Symbol(value, Decl(inKeywordTypeguard.ts, 252, 15)) ->value : Symbol(value, Decl(inKeywordTypeguard.ts, 252, 15)) +>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, 252, 15)) +>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 index 220ea85b8f127..5ff86bd7b8599 100644 --- a/tests/baselines/reference/inKeywordTypeguard(strict=false).types +++ b/tests/baselines/reference/inKeywordTypeguard(strict=false).types @@ -794,10 +794,103 @@ function f7(x: { a: string, b: number }, y: { a: string } & { b: number }) { } } +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 : Record<"a", unknown> +>sym in x : boolean +>sym : unique symbol +>x : Record<"a", unknown> & Record<1, unknown> + + x.a; +>x.a : unknown +>x : Record<"a", unknown> & Record<1, unknown> & Record +>a : unknown + + x["a"]; +>x["a"] : unknown +>x : Record<"a", unknown> & Record<1, unknown> & Record +>"a" : "a" + + x[1]; +>x[1] : unknown +>x : Record<"a", unknown> & Record<1, unknown> & Record +>1 : 1 + + x["1"]; +>x["1"] : unknown +>x : Record<"a", unknown> & Record<1, unknown> & Record +>"1" : "1" + + x[sym]; +>x[sym] : unknown +>x : 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 : Record<"a", unknown> +>sym in x : boolean +>sym : unique symbol +>x : Record<"a", unknown> & Record<"1", unknown> + + x.a; +>x.a : unknown +>x : Record<"a", unknown> & Record<"1", unknown> & Record +>a : unknown + + x["a"]; +>x["a"] : unknown +>x : Record<"a", unknown> & Record<"1", unknown> & Record +>"a" : "a" + + x[1]; +>x[1] : unknown +>x : Record<"a", unknown> & Record<"1", unknown> & Record +>1 : 1 + + x["1"]; +>x["1"] : unknown +>x : Record<"a", unknown> & Record<"1", unknown> & Record +>"1" : "1" + + x[sym]; +>x[sym] : unknown +>x : Record<"a", unknown> & Record<"1", unknown> & Record +>sym : unique symbol + } +} + // Repro from #50639 -function f8(value: A) { ->f8 : (value: A) => void +function foo(value: A) { +>foo : (value: A) => void >value : A if (typeof value === "object" && value !== null && "prop" in value) { diff --git a/tests/baselines/reference/inKeywordTypeguard(strict=true).errors.txt b/tests/baselines/reference/inKeywordTypeguard(strict=true).errors.txt index 078cf138efa28..b780b4485d7c1 100644 --- a/tests/baselines/reference/inKeywordTypeguard(strict=true).errors.txt +++ b/tests/baselines/reference/inKeywordTypeguard(strict=true).errors.txt @@ -351,9 +351,31 @@ tests/cases/compiler/inKeywordTypeguard.ts(186,21): error TS2638: Type 'NonNulla } } + 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 f8(value: A) { + function foo(value: A) { if (typeof value === "object" && value !== null && "prop" in value) { value; // A & object & Record<"prop", unknown> } diff --git a/tests/baselines/reference/inKeywordTypeguard(strict=true).js b/tests/baselines/reference/inKeywordTypeguard(strict=true).js index 0d28f7eb91486..b2a0d92f7b18b 100644 --- a/tests/baselines/reference/inKeywordTypeguard(strict=true).js +++ b/tests/baselines/reference/inKeywordTypeguard(strict=true).js @@ -249,9 +249,31 @@ function f7(x: { a: string, b: number }, y: { a: string } & { b: number }) { } } +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 f8(value: A) { +function foo(value: A) { if (typeof value === "object" && value !== null && "prop" in value) { value; // A & object & Record<"prop", unknown> } @@ -260,16 +282,10 @@ function f8(value: A) { //// [inKeywordTypeguard.js] "use strict"; -var A = /** @class */ (function () { - function A() { - } - return A; -}()); -var B = /** @class */ (function () { - function B() { - } - return B; -}()); +class A { +} +class B { +} function negativeClassesTest(x) { if ("a" in x) { x.b = "1"; @@ -286,16 +302,10 @@ function positiveClassesTest(x) { x.a = "1"; } } -var AWithOptionalProp = /** @class */ (function () { - function AWithOptionalProp() { - } - return AWithOptionalProp; -}()); -var BWithOptionalProp = /** @class */ (function () { - function BWithOptionalProp() { - } - return BWithOptionalProp; -}()); +class AWithOptionalProp { +} +class BWithOptionalProp { +} function positiveTestClassesWithOptionalProperties(x) { if ("a" in x) { x.a = "1"; @@ -304,18 +314,12 @@ function positiveTestClassesWithOptionalProperties(x) { 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; -}()); +class AWithMethod { + a() { return ""; } +} +class BWithMethod { + b() { return ""; } +} function negativeTestClassesWithMembers(x) { if ("a" in x) { x.a(); @@ -334,16 +338,10 @@ function negativeTestClassesWithMemberMissingInBothClasses(x) { x.b(); } } -var C = /** @class */ (function () { - function C() { - } - return C; -}()); -var D = /** @class */ (function () { - function D() { - } - return D; -}()); +class C { +} +class D { +} function negativeMultipleClassesTest(x) { if ("a" in x) { x.b = "1"; @@ -352,50 +350,41 @@ function negativeMultipleClassesTest(x) { x.a = "1"; } } -var ClassWithUnionProp = /** @class */ (function () { - function ClassWithUnionProp() { - } - return ClassWithUnionProp; -}()); +class ClassWithUnionProp { +} function negativePropTest(x) { if ("a" in x.prop) { - var y = x.prop.b; + let y = x.prop.b; } else { - var z = x.prop.a; + let z = x.prop.a; } } -var NegativeClassTest = /** @class */ (function () { - function NegativeClassTest() { - } - NegativeClassTest.prototype.inThis = function () { +class NegativeClassTest { + inThis() { if ("a" in this.prop) { - var z = this.prop.b; + let z = this.prop.b; } else { - var y = this.prop.a; + let y = this.prop.a; } - }; - return NegativeClassTest; -}()); -var UnreachableCodeDetection = /** @class */ (function () { - function UnreachableCodeDetection() { } - UnreachableCodeDetection.prototype.inThis = function () { +} +class UnreachableCodeDetection { + inThis() { if ("a" in this) { } else { - var y = this.a; + let y = this.a; } - }; - return UnreachableCodeDetection; -}()); + } +} function positiveIntersectionTest(x) { if ("a" in x) { - var s = x.a; + let s = x.a; } else { - var n = x; + let n = x; } } if ('extra' in error) { @@ -405,7 +394,7 @@ else { error; // Error } function narrowsToNever(x) { - var v; + let v; if ("l" in x) { v = x.l; } @@ -426,7 +415,7 @@ if (isAOrB(x)) { } // x is never because of the type predicate from unknown else if ("cProp" in x) { - var _never = x; + const _never = x; } } function negativeIntersectionTest() { @@ -526,8 +515,27 @@ function f7(x, y) { 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 f8(value) { +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 index c22686b0c52dd..33ff5a5749fca 100644 --- a/tests/baselines/reference/inKeywordTypeguard(strict=true).symbols +++ b/tests/baselines/reference/inKeywordTypeguard(strict=true).symbols @@ -637,21 +637,91 @@ function f7(x: { a: string, b: number }, y: { a: string } & { b: number }) { } } +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 f8(value: A) { ->f8 : Symbol(f8, Decl(inKeywordTypeguard.ts, 248, 1)) ->A : Symbol(A, Decl(inKeywordTypeguard.ts, 252, 12)) ->value : Symbol(value, Decl(inKeywordTypeguard.ts, 252, 15)) ->A : Symbol(A, Decl(inKeywordTypeguard.ts, 252, 12)) +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, 252, 15)) ->value : Symbol(value, Decl(inKeywordTypeguard.ts, 252, 15)) ->value : Symbol(value, Decl(inKeywordTypeguard.ts, 252, 15)) +>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, 252, 15)) +>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 index d3c3784705756..6aabe074a1e87 100644 --- a/tests/baselines/reference/inKeywordTypeguard(strict=true).types +++ b/tests/baselines/reference/inKeywordTypeguard(strict=true).types @@ -794,10 +794,103 @@ function f7(x: { a: string, b: number }, y: { a: string } & { b: number }) { } } +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 : Record<"a", unknown> +>sym in x : boolean +>sym : unique symbol +>x : Record<"a", unknown> & Record<1, unknown> + + x.a; +>x.a : unknown +>x : Record<"a", unknown> & Record<1, unknown> & Record +>a : unknown + + x["a"]; +>x["a"] : unknown +>x : Record<"a", unknown> & Record<1, unknown> & Record +>"a" : "a" + + x[1]; +>x[1] : unknown +>x : Record<"a", unknown> & Record<1, unknown> & Record +>1 : 1 + + x["1"]; +>x["1"] : unknown +>x : Record<"a", unknown> & Record<1, unknown> & Record +>"1" : "1" + + x[sym]; +>x[sym] : unknown +>x : 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 : Record<"a", unknown> +>sym in x : boolean +>sym : unique symbol +>x : Record<"a", unknown> & Record<"1", unknown> + + x.a; +>x.a : unknown +>x : Record<"a", unknown> & Record<"1", unknown> & Record +>a : unknown + + x["a"]; +>x["a"] : unknown +>x : Record<"a", unknown> & Record<"1", unknown> & Record +>"a" : "a" + + x[1]; +>x[1] : unknown +>x : Record<"a", unknown> & Record<"1", unknown> & Record +>1 : 1 + + x["1"]; +>x["1"] : unknown +>x : Record<"a", unknown> & Record<"1", unknown> & Record +>"1" : "1" + + x[sym]; +>x[sym] : unknown +>x : Record<"a", unknown> & Record<"1", unknown> & Record +>sym : unique symbol + } +} + // Repro from #50639 -function f8(value: A) { ->f8 : (value: A) => void +function foo(value: A) { +>foo : (value: A) => void >value : A if (typeof value === "object" && value !== null && "prop" in value) { diff --git a/tests/cases/compiler/inKeywordTypeguard.ts b/tests/cases/compiler/inKeywordTypeguard.ts index 1b47a29888e3f..c6c697613ba62 100644 --- a/tests/cases/compiler/inKeywordTypeguard.ts +++ b/tests/cases/compiler/inKeywordTypeguard.ts @@ -1,4 +1,5 @@ // @strict: true, false +// @target: es2015 class A { a: string; } class B { b: string; } @@ -250,9 +251,31 @@ function f7(x: { a: string, b: number }, y: { a: string } & { b: number }) { } } +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 f8(value: A) { +function foo(value: A) { if (typeof value === "object" && value !== null && "prop" in value) { value; // A & object & Record<"prop", unknown> } From a6818786701d706fa485d37976217114ee753e67 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 9 Sep 2022 07:01:47 -0700 Subject: [PATCH 09/15] Disallow {} typed right operand only in strictNullChecks mode --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0f5e31415f308..80e381364c034 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -33801,7 +33801,7 @@ namespace ts { } // The type of the right operand must be assignable to 'object'. if (checkTypeAssignableTo(checkNonNullType(rightType, right), nonPrimitiveType, right)) { - if (hasEmptyAnonymousObjectType(rightType)) { + if (strictNullChecks && hasEmptyAnonymousObjectType(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)); } } From 00b32d50040ae0ad6f41a305b8fafbf4cef5630f Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 9 Sep 2022 07:01:57 -0700 Subject: [PATCH 10/15] Accept new baselines --- .../inOperatorWithValidOperands.errors.txt | 5 +-- .../typePredicateWithThisParameter.errors.txt | 36 ------------------- 2 files changed, 1 insertion(+), 40 deletions(-) delete mode 100644 tests/baselines/reference/typePredicateWithThisParameter.errors.txt diff --git a/tests/baselines/reference/inOperatorWithValidOperands.errors.txt b/tests/baselines/reference/inOperatorWithValidOperands.errors.txt index d607a533b6cd6..51801beeb658e 100644 --- a/tests/baselines/reference/inOperatorWithValidOperands.errors.txt +++ b/tests/baselines/reference/inOperatorWithValidOperands.errors.txt @@ -1,4 +1,3 @@ -tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithValidOperands.ts(22,16): error TS2638: Type '{}' may represent a primitive value, which is not permitted as the right operand of the 'in' operator. 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'. @@ -6,7 +5,7 @@ tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithVal Type 'T' is not assignable to type 'object'. -==== tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithValidOperands.ts (4 errors) ==== +==== tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithValidOperands.ts (3 errors) ==== var x: any; // valid left operands @@ -29,8 +28,6 @@ tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithVal var b1: {}; var rb1 = x in b1; - ~~ -!!! error TS2638: Type '{}' may represent a primitive value, which is not permitted as the right operand of the 'in' operator. var rb2 = x in {}; function foo(t: T) { diff --git a/tests/baselines/reference/typePredicateWithThisParameter.errors.txt b/tests/baselines/reference/typePredicateWithThisParameter.errors.txt deleted file mode 100644 index 45bbdc0c6aeac..0000000000000 --- a/tests/baselines/reference/typePredicateWithThisParameter.errors.txt +++ /dev/null @@ -1,36 +0,0 @@ -tests/cases/compiler/typePredicateWithThisParameter.ts(11,21): error TS2638: Type '{}' may represent a primitive value, which is not permitted as the right operand of the 'in' operator. -tests/cases/compiler/typePredicateWithThisParameter.ts(15,21): error TS2638: Type '{}' may represent a primitive value, which is not permitted as the right operand of the 'in' operator. - - -==== tests/cases/compiler/typePredicateWithThisParameter.ts (2 errors) ==== - // Repro from #15310 - - interface Foo { - foo: string; - } - interface Bar { - bar: string; - } - - function isFoo1(object: {}): object is Foo { - return 'foo' in object; - ~~~~~~ -!!! error TS2638: Type '{}' may represent a primitive value, which is not permitted as the right operand of the 'in' operator. - } - - function isFoo2(this: void, object: {}): object is Foo { - return 'foo' in object; - ~~~~~~ -!!! error TS2638: Type '{}' may represent a primitive value, which is not permitted as the right operand of the 'in' operator. - } - - declare let test: Foo | Bar; - - if (isFoo1(test)) { - test.foo = 'hi'; - } - - if (isFoo2(test)) { - test.foo = 'hi'; - } - \ No newline at end of file From cba7203aec200a0cdd3a0a4d17562ccd2c1de61f Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 14 Sep 2022 12:02:33 -0700 Subject: [PATCH 11/15] Detect {} resulting from intersections --- src/compiler/checker.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 80e381364c034..cea06df729661 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -856,7 +856,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(); @@ -33774,10 +33775,8 @@ namespace ts { return booleanType; } - function hasEmptyAnonymousObjectType(type: Type): boolean { - const t = getBaseConstraintOrType(type); - return isEmptyAnonymousObjectType(t) && !(getObjectFlags(t) & ObjectFlags.FreshLiteral) || - !!(t.flags & TypeFlags.UnionOrIntersection && some((t as UnionType).types, hasEmptyAnonymousObjectType)); + 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 { @@ -33801,7 +33800,11 @@ namespace ts { } // The type of the right operand must be assignable to 'object'. if (checkTypeAssignableTo(checkNonNullType(rightType, right), nonPrimitiveType, right)) { - if (strictNullChecks && hasEmptyAnonymousObjectType(rightType)) { + // 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)); } } From 1f673d6a839982f594c5aba4122a7c603e490496 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 14 Sep 2022 12:02:40 -0700 Subject: [PATCH 12/15] Accept new baselines --- .../controlFlowWithTemplateLiterals.errors.txt | 16 ---------------- .../reference/inKeywordAndUnknown.errors.txt | 5 +---- ...eNameInInExpression(target=es2022).errors.txt | 5 +---- ...eNameInInExpression(target=esnext).errors.txt | 5 +---- 4 files changed, 3 insertions(+), 28 deletions(-) delete mode 100644 tests/baselines/reference/controlFlowWithTemplateLiterals.errors.txt diff --git a/tests/baselines/reference/controlFlowWithTemplateLiterals.errors.txt b/tests/baselines/reference/controlFlowWithTemplateLiterals.errors.txt deleted file mode 100644 index 034c45eb71e25..0000000000000 --- a/tests/baselines/reference/controlFlowWithTemplateLiterals.errors.txt +++ /dev/null @@ -1,16 +0,0 @@ -tests/cases/conformance/controlFlow/controlFlowWithTemplateLiterals.ts(7,15): error TS2638: Type '{} | { test: string; }' may represent a primitive value, which is not permitted as the right operand of the 'in' operator. - - -==== tests/cases/conformance/controlFlow/controlFlowWithTemplateLiterals.ts (1 errors) ==== - declare const envVar: string | undefined; - if (typeof envVar === `string`) { - envVar.slice(0) - } - - declare const obj: {test: string} | {} - if (`test` in obj) { - ~~~ -!!! error TS2638: Type '{} | { test: string; }' may represent a primitive value, which is not permitted as the right operand of the 'in' operator. - obj.test.slice(0) - } - \ No newline at end of file diff --git a/tests/baselines/reference/inKeywordAndUnknown.errors.txt b/tests/baselines/reference/inKeywordAndUnknown.errors.txt index 0481ec6929f82..61c2cd2a2a364 100644 --- a/tests/baselines/reference/inKeywordAndUnknown.errors.txt +++ b/tests/baselines/reference/inKeywordAndUnknown.errors.txt @@ -1,14 +1,11 @@ -tests/cases/compiler/inKeywordAndUnknown.ts(4,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(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 (2 errors) ==== +==== tests/cases/compiler/inKeywordAndUnknown.ts (1 errors) ==== // Repro from #50531 function f(x: {}, y: unknown) { if (!("a" in x)) { - ~ -!!! error TS2638: Type '{}' may represent a primitive value, which is not permitted as the right operand of the 'in' operator. return; } x; // {} diff --git a/tests/baselines/reference/privateNameInInExpression(target=es2022).errors.txt b/tests/baselines/reference/privateNameInInExpression(target=es2022).errors.txt index 480712c6869cf..514d2a85e84a6 100644 --- a/tests/baselines/reference/privateNameInInExpression(target=es2022).errors.txt +++ b/tests/baselines/reference/privateNameInInExpression(target=es2022).errors.txt @@ -1,4 +1,3 @@ -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(12,29): error TS2638: Type '{}' may represent a primitive value, which is not permitted as the right operand of the 'in' operator. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(21,29): error TS2571: Object is of type 'unknown'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(23,19): error TS2339: Property '#fiel' does not exist on type 'any'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(25,20): error TS1451: Private identifiers are only allowed in class bodies and may only be used as part of a class member declaration, property access, or on the left-hand-side of an 'in' expression @@ -8,7 +7,7 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(114,12): error TS18016: Private identifiers are not allowed outside class bodies. -==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (8 errors) ==== +==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (7 errors) ==== class Foo { #field = 1; static #staticField = 2; @@ -21,8 +20,6 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t const b = #field in v.p1.p2; const c = #field in (v as {}); - ~~~~~~~~~ -!!! error TS2638: Type '{}' may represent a primitive value, which is not permitted as the right operand of the 'in' operator. const d = #field in (v as Foo); diff --git a/tests/baselines/reference/privateNameInInExpression(target=esnext).errors.txt b/tests/baselines/reference/privateNameInInExpression(target=esnext).errors.txt index 480712c6869cf..514d2a85e84a6 100644 --- a/tests/baselines/reference/privateNameInInExpression(target=esnext).errors.txt +++ b/tests/baselines/reference/privateNameInInExpression(target=esnext).errors.txt @@ -1,4 +1,3 @@ -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(12,29): error TS2638: Type '{}' may represent a primitive value, which is not permitted as the right operand of the 'in' operator. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(21,29): error TS2571: Object is of type 'unknown'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(23,19): error TS2339: Property '#fiel' does not exist on type 'any'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(25,20): error TS1451: Private identifiers are only allowed in class bodies and may only be used as part of a class member declaration, property access, or on the left-hand-side of an 'in' expression @@ -8,7 +7,7 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(114,12): error TS18016: Private identifiers are not allowed outside class bodies. -==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (8 errors) ==== +==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (7 errors) ==== class Foo { #field = 1; static #staticField = 2; @@ -21,8 +20,6 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t const b = #field in v.p1.p2; const c = #field in (v as {}); - ~~~~~~~~~ -!!! error TS2638: Type '{}' may represent a primitive value, which is not permitted as the right operand of the 'in' operator. const d = #field in (v as Foo); From 3dea326d6b9cc270d853c74c9db943caf63cc4e6 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 17 Sep 2022 07:48:44 -0700 Subject: [PATCH 13/15] Don't attempt to reduce intersections with Record --- src/compiler/checker.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 21cd309fb27ce..4052b160a95c9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -25194,8 +25194,7 @@ namespace ts { // where X is the name of the property. const recordSymbol = getGlobalRecordSymbol(); if (recordSymbol !== unknownSymbol) { - const record = getTypeAliasInstantiation(recordSymbol, [nameType, unknownType]); - return mapType(type, t => t.flags & TypeFlags.NonPrimitive ? record : getIntersectionType([t, record])); + return getIntersectionType([type, getTypeAliasInstantiation(recordSymbol, [nameType, unknownType])]); } } return type; From 3efc2d6d49322a960427bee55dfaead631710e4e Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 17 Sep 2022 07:49:56 -0700 Subject: [PATCH 14/15] Accept new baselines --- .../reference/controlFlowInOperator.types | 4 +- ...nKeywordTypeguard(strict=false).errors.txt | 8 +-- .../inKeywordTypeguard(strict=false).types | 56 +++++++++---------- ...inKeywordTypeguard(strict=true).errors.txt | 8 +-- .../inKeywordTypeguard(strict=true).types | 56 +++++++++---------- 5 files changed, 66 insertions(+), 66 deletions(-) diff --git a/tests/baselines/reference/controlFlowInOperator.types b/tests/baselines/reference/controlFlowInOperator.types index 3223c317aa089..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 : (A & Record<"d", unknown>) | (B & Record<"d", unknown>) +>c : (A | B) & Record<"d", unknown> } if (a in c) { @@ -67,6 +67,6 @@ if (d in c) { >c : A | B c; // never ->c : (A & Record<"d", unknown>) | (B & Record<"d", unknown>) +>c : (A | B) & Record<"d", unknown> } diff --git a/tests/baselines/reference/inKeywordTypeguard(strict=false).errors.txt b/tests/baselines/reference/inKeywordTypeguard(strict=false).errors.txt index d974b9f4ff3c7..6feabe9f5c65e 100644 --- a/tests/baselines/reference/inKeywordTypeguard(strict=false).errors.txt +++ b/tests/baselines/reference/inKeywordTypeguard(strict=false).errors.txt @@ -5,9 +5,9 @@ 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 '(AWithMethod & Record<"c", unknown>) | (BWithMethod & Record<"c", unknown>)'. +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 & Record<"c", unknown>) | (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'. @@ -92,11 +92,11 @@ tests/cases/compiler/inKeywordTypeguard.ts(186,21): error TS2322: Type 'T' is no if ("c" in x) { x.a(); ~ -!!! error TS2339: Property 'a' does not exist on type '(AWithMethod & Record<"c", unknown>) | (BWithMethod & Record<"c", unknown>)'. +!!! 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 & Record<"c", unknown>) | (BWithMethod & Record<"c", unknown>)'. +!!! 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(); diff --git a/tests/baselines/reference/inKeywordTypeguard(strict=false).types b/tests/baselines/reference/inKeywordTypeguard(strict=false).types index 5ff86bd7b8599..e8e414520d3cf 100644 --- a/tests/baselines/reference/inKeywordTypeguard(strict=false).types +++ b/tests/baselines/reference/inKeywordTypeguard(strict=false).types @@ -146,13 +146,13 @@ function negativeTestClassesWithMemberMissingInBothClasses(x: AWithMethod | BWit x.a(); >x.a() : any >x.a : any ->x : (AWithMethod & Record<"c", unknown>) | (BWithMethod & Record<"c", unknown>) +>x : (AWithMethod | BWithMethod) & Record<"c", unknown> >a : any x.b(); >x.b() : any >x.b : any ->x : (AWithMethod & Record<"c", unknown>) | (BWithMethod & Record<"c", unknown>) +>x : (AWithMethod | BWithMethod) & Record<"c", unknown> >b : any } else { @@ -492,7 +492,7 @@ function f1(x: unknown) { x.a; >x.a : unknown ->x : Record<"a", unknown> +>x : object & Record<"a", unknown> >a : unknown } if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) { @@ -510,24 +510,24 @@ function f1(x: unknown) { >x : object >"b" in x : boolean >"b" : "b" ->x : Record<"a", unknown> +>x : object & Record<"a", unknown> >"c" in x : boolean >"c" : "c" ->x : Record<"a", unknown> & Record<"b", unknown> +>x : object & Record<"a", unknown> & Record<"b", unknown> x.a; >x.a : unknown ->x : Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> >a : unknown x.b; >x.b : unknown ->x : Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> >b : unknown x.c; >x.c : unknown ->x : Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> >c : unknown } } @@ -543,7 +543,7 @@ function f2(x: object) { x.a; >x.a : unknown ->x : Record<"a", unknown> +>x : object & Record<"a", unknown> >a : unknown } if ("a" in x && "b" in x && "c" in x) { @@ -554,24 +554,24 @@ function f2(x: object) { >x : object >"b" in x : boolean >"b" : "b" ->x : Record<"a", unknown> +>x : object & Record<"a", unknown> >"c" in x : boolean >"c" : "c" ->x : Record<"a", unknown> & Record<"b", unknown> +>x : object & Record<"a", unknown> & Record<"b", unknown> x.a; >x.a : unknown ->x : Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> >a : unknown x.b; >x.b : unknown ->x : Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> >b : unknown x.c; >x.c : unknown ->x : Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> >c : unknown } } @@ -811,34 +811,34 @@ function f8(x: object) { >x : object >1 in x : boolean >1 : 1 ->x : Record<"a", unknown> +>x : object & Record<"a", unknown> >sym in x : boolean >sym : unique symbol ->x : Record<"a", unknown> & Record<1, unknown> +>x : object & Record<"a", unknown> & Record<1, unknown> x.a; >x.a : unknown ->x : Record<"a", unknown> & Record<1, unknown> & Record +>x : object & Record<"a", unknown> & Record<1, unknown> & Record >a : unknown x["a"]; >x["a"] : unknown ->x : Record<"a", unknown> & Record<1, unknown> & Record +>x : object & Record<"a", unknown> & Record<1, unknown> & Record >"a" : "a" x[1]; >x[1] : unknown ->x : Record<"a", unknown> & Record<1, unknown> & Record +>x : object & Record<"a", unknown> & Record<1, unknown> & Record >1 : 1 x["1"]; >x["1"] : unknown ->x : Record<"a", unknown> & Record<1, unknown> & Record +>x : object & Record<"a", unknown> & Record<1, unknown> & Record >"1" : "1" x[sym]; >x[sym] : unknown ->x : Record<"a", unknown> & Record<1, unknown> & Record +>x : object & Record<"a", unknown> & Record<1, unknown> & Record >sym : unique symbol } } @@ -855,34 +855,34 @@ function f9(x: object) { >x : object >"1" in x : boolean >"1" : "1" ->x : Record<"a", unknown> +>x : object & Record<"a", unknown> >sym in x : boolean >sym : unique symbol ->x : Record<"a", unknown> & Record<"1", unknown> +>x : object & Record<"a", unknown> & Record<"1", unknown> x.a; >x.a : unknown ->x : Record<"a", unknown> & Record<"1", unknown> & Record +>x : object & Record<"a", unknown> & Record<"1", unknown> & Record >a : unknown x["a"]; >x["a"] : unknown ->x : Record<"a", unknown> & Record<"1", unknown> & Record +>x : object & Record<"a", unknown> & Record<"1", unknown> & Record >"a" : "a" x[1]; >x[1] : unknown ->x : Record<"a", unknown> & Record<"1", unknown> & Record +>x : object & Record<"a", unknown> & Record<"1", unknown> & Record >1 : 1 x["1"]; >x["1"] : unknown ->x : Record<"a", unknown> & Record<"1", unknown> & Record +>x : object & Record<"a", unknown> & Record<"1", unknown> & Record >"1" : "1" x[sym]; >x[sym] : unknown ->x : Record<"a", unknown> & Record<"1", unknown> & Record +>x : object & Record<"a", unknown> & Record<"1", unknown> & Record >sym : unique symbol } } diff --git a/tests/baselines/reference/inKeywordTypeguard(strict=true).errors.txt b/tests/baselines/reference/inKeywordTypeguard(strict=true).errors.txt index 4f5220605807a..dbae773cac10c 100644 --- a/tests/baselines/reference/inKeywordTypeguard(strict=true).errors.txt +++ b/tests/baselines/reference/inKeywordTypeguard(strict=true).errors.txt @@ -7,9 +7,9 @@ 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 '(AWithMethod & Record<"c", unknown>) | (BWithMethod & Record<"c", unknown>)'. +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 & Record<"c", unknown>) | (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'. @@ -103,11 +103,11 @@ tests/cases/compiler/inKeywordTypeguard.ts(186,21): error TS2638: Type 'NonNulla if ("c" in x) { x.a(); ~ -!!! error TS2339: Property 'a' does not exist on type '(AWithMethod & Record<"c", unknown>) | (BWithMethod & Record<"c", unknown>)'. +!!! 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 & Record<"c", unknown>) | (BWithMethod & Record<"c", unknown>)'. +!!! 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(); diff --git a/tests/baselines/reference/inKeywordTypeguard(strict=true).types b/tests/baselines/reference/inKeywordTypeguard(strict=true).types index 6aabe074a1e87..8ec2b47df0796 100644 --- a/tests/baselines/reference/inKeywordTypeguard(strict=true).types +++ b/tests/baselines/reference/inKeywordTypeguard(strict=true).types @@ -146,13 +146,13 @@ function negativeTestClassesWithMemberMissingInBothClasses(x: AWithMethod | BWit x.a(); >x.a() : any >x.a : any ->x : (AWithMethod & Record<"c", unknown>) | (BWithMethod & Record<"c", unknown>) +>x : (AWithMethod | BWithMethod) & Record<"c", unknown> >a : any x.b(); >x.b() : any >x.b : any ->x : (AWithMethod & Record<"c", unknown>) | (BWithMethod & Record<"c", unknown>) +>x : (AWithMethod | BWithMethod) & Record<"c", unknown> >b : any } else { @@ -492,7 +492,7 @@ function f1(x: unknown) { x.a; >x.a : unknown ->x : Record<"a", unknown> +>x : object & Record<"a", unknown> >a : unknown } if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) { @@ -510,24 +510,24 @@ function f1(x: unknown) { >x : object >"b" in x : boolean >"b" : "b" ->x : Record<"a", unknown> +>x : object & Record<"a", unknown> >"c" in x : boolean >"c" : "c" ->x : Record<"a", unknown> & Record<"b", unknown> +>x : object & Record<"a", unknown> & Record<"b", unknown> x.a; >x.a : unknown ->x : Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> >a : unknown x.b; >x.b : unknown ->x : Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> >b : unknown x.c; >x.c : unknown ->x : Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> >c : unknown } } @@ -543,7 +543,7 @@ function f2(x: object) { x.a; >x.a : unknown ->x : Record<"a", unknown> +>x : object & Record<"a", unknown> >a : unknown } if ("a" in x && "b" in x && "c" in x) { @@ -554,24 +554,24 @@ function f2(x: object) { >x : object >"b" in x : boolean >"b" : "b" ->x : Record<"a", unknown> +>x : object & Record<"a", unknown> >"c" in x : boolean >"c" : "c" ->x : Record<"a", unknown> & Record<"b", unknown> +>x : object & Record<"a", unknown> & Record<"b", unknown> x.a; >x.a : unknown ->x : Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> >a : unknown x.b; >x.b : unknown ->x : Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> >b : unknown x.c; >x.c : unknown ->x : Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> +>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown> >c : unknown } } @@ -811,34 +811,34 @@ function f8(x: object) { >x : object >1 in x : boolean >1 : 1 ->x : Record<"a", unknown> +>x : object & Record<"a", unknown> >sym in x : boolean >sym : unique symbol ->x : Record<"a", unknown> & Record<1, unknown> +>x : object & Record<"a", unknown> & Record<1, unknown> x.a; >x.a : unknown ->x : Record<"a", unknown> & Record<1, unknown> & Record +>x : object & Record<"a", unknown> & Record<1, unknown> & Record >a : unknown x["a"]; >x["a"] : unknown ->x : Record<"a", unknown> & Record<1, unknown> & Record +>x : object & Record<"a", unknown> & Record<1, unknown> & Record >"a" : "a" x[1]; >x[1] : unknown ->x : Record<"a", unknown> & Record<1, unknown> & Record +>x : object & Record<"a", unknown> & Record<1, unknown> & Record >1 : 1 x["1"]; >x["1"] : unknown ->x : Record<"a", unknown> & Record<1, unknown> & Record +>x : object & Record<"a", unknown> & Record<1, unknown> & Record >"1" : "1" x[sym]; >x[sym] : unknown ->x : Record<"a", unknown> & Record<1, unknown> & Record +>x : object & Record<"a", unknown> & Record<1, unknown> & Record >sym : unique symbol } } @@ -855,34 +855,34 @@ function f9(x: object) { >x : object >"1" in x : boolean >"1" : "1" ->x : Record<"a", unknown> +>x : object & Record<"a", unknown> >sym in x : boolean >sym : unique symbol ->x : Record<"a", unknown> & Record<"1", unknown> +>x : object & Record<"a", unknown> & Record<"1", unknown> x.a; >x.a : unknown ->x : Record<"a", unknown> & Record<"1", unknown> & Record +>x : object & Record<"a", unknown> & Record<"1", unknown> & Record >a : unknown x["a"]; >x["a"] : unknown ->x : Record<"a", unknown> & Record<"1", unknown> & Record +>x : object & Record<"a", unknown> & Record<"1", unknown> & Record >"a" : "a" x[1]; >x[1] : unknown ->x : Record<"a", unknown> & Record<"1", unknown> & Record +>x : object & Record<"a", unknown> & Record<"1", unknown> & Record >1 : 1 x["1"]; >x["1"] : unknown ->x : Record<"a", unknown> & Record<"1", unknown> & Record +>x : object & Record<"a", unknown> & Record<"1", unknown> & Record >"1" : "1" x[sym]; >x[sym] : unknown ->x : Record<"a", unknown> & Record<"1", unknown> & Record +>x : object & Record<"a", unknown> & Record<"1", unknown> & Record >sym : unique symbol } } From 5bc9f95b8b476bf3e86b3d3ed253c57682884e8b Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 17 Sep 2022 08:24:26 -0700 Subject: [PATCH 15/15] Return undefined instead of unknownSymbol from getGlobalRecordSymbol() --- src/compiler/checker.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4052b160a95c9..31198760e26ba 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14310,8 +14310,9 @@ namespace ts { return (deferredGlobalBigIntType ||= getGlobalType("BigInt" as __String, /*arity*/ 0, /*reportErrors*/ false)) || emptyObjectType; } - function getGlobalRecordSymbol() { - return deferredGlobalRecordSymbol ||= getGlobalTypeAliasSymbol("Record" as __String, /*arity*/ 2, /*reportErrors*/ true) || unknownSymbol; + function getGlobalRecordSymbol(): Symbol | undefined { + deferredGlobalRecordSymbol ||= getGlobalTypeAliasSymbol("Record" as __String, /*arity*/ 2, /*reportErrors*/ true) || unknownSymbol; + return deferredGlobalRecordSymbol === unknownSymbol ? undefined : deferredGlobalRecordSymbol; } /** @@ -25193,7 +25194,7 @@ namespace ts { // 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 !== unknownSymbol) { + if (recordSymbol) { return getIntersectionType([type, getTypeAliasInstantiation(recordSymbol, [nameType, unknownType])]); } }