Improve checking of in operator (#50666)

* Improve checking of `in` operator

* Accept new baselines

* Add tests

* Delete old and accept new baselines

* Disallow right operand of type '{}'

* Accept new baselines

* Support number and symbol literals

* Add tests

* Disallow {} typed right operand only in strictNullChecks mode

* Accept new baselines

* Detect {} resulting from intersections

* Accept new baselines

* Don't attempt to reduce intersections with Record<K, unknown>

* Accept new baselines

* Return undefined instead of unknownSymbol from getGlobalRecordSymbol()
This commit is contained in:
Anders Hejlsberg 2022-09-19 14:16:01 -07:00 committed by GitHub
parent 67f2b62ed2
commit a11c41621b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 5090 additions and 936 deletions

View File

@ -858,7 +858,8 @@ namespace ts {
emptyTypeLiteralSymbol.members = createSymbolTable();
const emptyTypeLiteralType = createAnonymousType(emptyTypeLiteralSymbol, emptySymbols, emptyArray, emptyArray, emptyArray);
const unknownUnionType = strictNullChecks ? getUnionType([undefinedType, nullType, createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, emptyArray)]) : unknownType;
const unknownEmptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, emptyArray);
const unknownUnionType = strictNullChecks ? getUnionType([undefinedType, nullType, unknownEmptyObjectType]) : unknownType;
const emptyGenericType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, emptyArray) as ObjectType as GenericType;
emptyGenericType.instantiations = new Map<string, TypeReference>();
@ -998,6 +999,7 @@ namespace ts {
let deferredGlobalOmitSymbol: Symbol | undefined;
let deferredGlobalAwaitedSymbol: Symbol | undefined;
let deferredGlobalBigIntType: ObjectType | undefined;
let deferredGlobalRecordSymbol: Symbol | undefined;
const allPotentiallyUnusedIdentifiers = new Map<Path, PotentiallyUnusedIdentifier[]>(); // key is file name
@ -14314,6 +14316,11 @@ namespace ts {
return (deferredGlobalBigIntType ||= getGlobalType("BigInt" as __String, /*arity*/ 0, /*reportErrors*/ false)) || emptyObjectType;
}
function getGlobalRecordSymbol(): Symbol | undefined {
deferredGlobalRecordSymbol ||= getGlobalTypeAliasSymbol("Record" as __String, /*arity*/ 2, /*reportErrors*/ true) || unknownSymbol;
return deferredGlobalRecordSymbol === unknownSymbol ? undefined : deferredGlobalRecordSymbol;
}
/**
* Instantiates a global type that is generic with some element type, and returns that instantiation.
*/
@ -25199,19 +25206,27 @@ namespace ts {
function isTypePresencePossible(type: Type, propName: __String, assumeTrue: boolean) {
const prop = getPropertyOfType(type, propName);
if (prop) {
return prop.flags & SymbolFlags.Optional ? true : assumeTrue;
}
return getApplicableIndexInfoForName(type, propName) ? true : !assumeTrue;
return prop ?
!!(prop.flags & SymbolFlags.Optional) || assumeTrue :
!!getApplicableIndexInfoForName(type, propName) || !assumeTrue;
}
function narrowByInKeyword(type: Type, name: __String, assumeTrue: boolean) {
if (type.flags & TypeFlags.Union
|| type.flags & TypeFlags.Object && declaredType !== type && !(declaredType === unknownType && isEmptyAnonymousObjectType(type))
|| isThisTypeParameter(type)
|| type.flags & TypeFlags.Intersection && every((type as IntersectionType).types, t => t.symbol !== globalThisSymbol)) {
function narrowByInKeyword(type: Type, nameType: StringLiteralType | NumberLiteralType | UniqueESSymbolType, assumeTrue: boolean) {
const name = getPropertyNameFromType(nameType);
const isKnownProperty = someType(type, t => isTypePresencePossible(t, name, /*assumeTrue*/ true));
if (isKnownProperty) {
// If the check is for a known property (i.e. a property declared in some constituent of
// the target type), we filter the target type by presence of absence of the property.
return filterType(type, t => isTypePresencePossible(t, name, assumeTrue));
}
if (assumeTrue) {
// If the check is for an unknown property, we intersect the target type with `Record<X, unknown>`,
// where X is the name of the property.
const recordSymbol = getGlobalRecordSymbol();
if (recordSymbol) {
return getIntersectionType([type, getTypeAliasInstantiation(recordSymbol, [nameType, unknownType])]);
}
}
return type;
}
@ -25271,15 +25286,14 @@ namespace ts {
return narrowTypeByPrivateIdentifierInInExpression(type, expr, assumeTrue);
}
const target = getReferenceCandidate(expr.right);
const leftType = getTypeOfNode(expr.left);
if (leftType.flags & TypeFlags.StringLiteral) {
const name = escapeLeadingUnderscores((leftType as StringLiteralType).value);
const leftType = getTypeOfExpression(expr.left);
if (leftType.flags & TypeFlags.StringOrNumberLiteralOrUnique) {
if (containsMissingType(type) && isAccessExpression(reference) && isMatchingReference(reference.expression, target) &&
getAccessedPropertyName(reference) === name) {
getAccessedPropertyName(reference) === getPropertyNameFromType(leftType as StringLiteralType | NumberLiteralType | UniqueESSymbolType)) {
return getTypeWithFacts(type, assumeTrue ? TypeFacts.NEUndefined : TypeFacts.EQUndefined);
}
if (isMatchingReference(reference, target)) {
return narrowByInKeyword(type, name, assumeTrue);
return narrowByInKeyword(type, leftType as StringLiteralType | NumberLiteralType | UniqueESSymbolType, assumeTrue);
}
}
break;
@ -33848,6 +33862,10 @@ namespace ts {
return booleanType;
}
function hasEmptyObjectIntersection(type: Type): boolean {
return someType(type, t => t === unknownEmptyObjectType || !!(t.flags & TypeFlags.Intersection) && some((t as IntersectionType).types, isEmptyAnonymousObjectType));
}
function checkInExpression(left: Expression, right: Expression, leftType: Type, rightType: Type): Type {
if (leftType === silentNeverType || rightType === silentNeverType) {
return silentNeverType;
@ -33864,43 +33882,20 @@ namespace ts {
}
}
else {
leftType = checkNonNullType(leftType, left);
// TypeScript 1.0 spec (April 2014): 4.15.5
// Require the left operand to be of type Any, the String primitive type, or the Number primitive type.
if (!(allTypesAssignableToKind(leftType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbolLike) ||
isTypeAssignableToKind(leftType, TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping | TypeFlags.TypeParameter))) {
error(left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_a_private_identifier_or_of_type_any_string_number_or_symbol);
// The type of the lef operand must be assignable to string, number, or symbol.
checkTypeAssignableTo(checkNonNullType(leftType, left), stringNumberSymbolType, left);
}
// The type of the right operand must be assignable to 'object'.
if (checkTypeAssignableTo(checkNonNullType(rightType, right), nonPrimitiveType, right)) {
// The {} type is assignable to the object type, yet {} might represent a primitive type. Here we
// detect and error on {} that results from narrowing the unknown type, as well as intersections
// that include {} (we know that the other types in such intersections are assignable to object
// since we already checked for that).
if (hasEmptyObjectIntersection(rightType)) {
error(right, Diagnostics.Type_0_may_represent_a_primitive_value_which_is_not_permitted_as_the_right_operand_of_the_in_operator, typeToString(rightType));
}
}
rightType = checkNonNullType(rightType, right);
// TypeScript 1.0 spec (April 2014): 4.15.5
// The in operator requires the right operand to be
//
// 1. assignable to the non-primitive type,
// 2. an unconstrained type parameter,
// 3. a union or intersection including one or more type parameters, whose constituents are all assignable to the
// the non-primitive type, or are unconstrainted type parameters, or have constraints assignable to the
// non-primitive type, or
// 4. a type parameter whose constraint is
// i. an object type,
// ii. the non-primitive type, or
// iii. a union or intersection with at least one constituent assignable to an object or non-primitive type.
//
// The divergent behavior for type parameters and unions containing type parameters is a workaround for type
// parameters not being narrowable. If the right operand is a concrete type, we can error if there is any chance
// it is a primitive. But if the operand is a type parameter, it cannot be narrowed, so we don't issue an error
// unless *all* instantiations would result in an error.
//
// The result is always of the Boolean primitive type.
const rightTypeConstraint = getConstraintOfType(rightType);
if (!allTypesAssignableToKind(rightType, TypeFlags.NonPrimitive | TypeFlags.InstantiableNonPrimitive) ||
rightTypeConstraint && (
isTypeAssignableToKind(rightType, TypeFlags.UnionOrIntersection) && !allTypesAssignableToKind(rightTypeConstraint, TypeFlags.NonPrimitive | TypeFlags.InstantiableNonPrimitive) ||
!maybeTypeOfKind(rightTypeConstraint, TypeFlags.NonPrimitive | TypeFlags.InstantiableNonPrimitive | TypeFlags.Object)
)
) {
error(right, Diagnostics.The_right_hand_side_of_an_in_expression_must_not_be_a_primitive);
}
return booleanType;
}

View File

@ -1844,14 +1844,6 @@
"category": "Error",
"code": 2359
},
"The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.": {
"category": "Error",
"code": 2360
},
"The right-hand side of an 'in' expression must not be a primitive.": {
"category": "Error",
"code": 2361
},
"The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.": {
"category": "Error",
"code": 2362
@ -2845,6 +2837,10 @@
"category": "Error",
"code": 2637
},
"Type '{0}' may represent a primitive value, which is not permitted as the right operand of the 'in' operator.": {
"category": "Error",
"code": 2638
},
"Cannot augment module '{0}' with value exports because it resolves to a non-module entity.": {
"category": "Error",

View File

@ -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 extends string, RECORD, IDENTIFIER extends Partial<RECORD>> {
name: NAME;
record: RECORD;
identifier: IDENTIFIER;
storedAs: PubSubRecordIsStoredInRedisAsA;
maxMsToWaitBeforePublishing: number;
}
type NameFieldConstructor<SO_FAR> =
SO_FAR extends {name: any} ? {} : {
name: <TYPE>(t?: TYPE) => BuildPubSubRecordType<SO_FAR & {name: TYPE}>
}
const buildNameFieldConstructor = <SO_FAR>(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: <TYPE>(instance: TYPE = undefined) =>
buildPubSubRecordType(Object.assign({}, soFar, {name: instance as TYPE}) as SO_FAR & {name: TYPE}) as BuildPubSubRecordType<SO_FAR & {name: TYPE}>
}
);
type StoredAsConstructor<SO_FAR> =
SO_FAR extends {storedAs: any} ? {} : {
storedAsJsonEncodedRedisString: () => BuildPubSubRecordType<SO_FAR & {storedAs: PubSubRecordIsStoredInRedisAsA.jsonEncodedRedisString}>;
storedRedisHash: () => BuildPubSubRecordType<SO_FAR & {storedAs: PubSubRecordIsStoredInRedisAsA.redisHash}>;
}
const buildStoredAsConstructor = <SO_FAR>(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<SO_FAR & {storedAs: PubSubRecordIsStoredInRedisAsA.jsonEncodedRedisString}>,
storedAsRedisHash: () =>
buildPubSubRecordType(Object.assign({}, soFar, {storedAs: PubSubRecordIsStoredInRedisAsA.redisHash})) as
BuildPubSubRecordType<SO_FAR & {storedAs: PubSubRecordIsStoredInRedisAsA.redisHash}>,
}
);
type IdentifierFieldConstructor<SO_FAR> =
SO_FAR extends {identifier: any} ? {} :
SO_FAR extends {record: any} ? {
identifier: <TYPE extends Partial<SO_FAR["record"]>>(t?: TYPE) => BuildPubSubRecordType<SO_FAR & {identifier: TYPE}>
} : {}
const buildIdentifierFieldConstructor = <SO_FAR>(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: <TYPE>(instance: TYPE = undefined) =>
buildPubSubRecordType(Object.assign({}, soFar, {identifier: instance as TYPE}) as SO_FAR & {identifier: TYPE}) as BuildPubSubRecordType<SO_FAR & {identifier: TYPE}>
}
);
type RecordFieldConstructor<SO_FAR> =
SO_FAR extends {record: any} ? {} : {
record: <TYPE>(t?: TYPE) => BuildPubSubRecordType<SO_FAR & {record: TYPE}>
}
const buildRecordFieldConstructor = <SO_FAR>(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: <TYPE>(instance: TYPE = undefined) =>
buildPubSubRecordType(Object.assign({}, soFar, {record: instance as TYPE}) as SO_FAR & {record: TYPE}) as BuildPubSubRecordType<SO_FAR & {record: TYPE}>
}
);
type MaxMsToWaitBeforePublishingFieldConstructor<SO_FAR> =
SO_FAR extends {maxMsToWaitBeforePublishing: any} ? {} : {
maxMsToWaitBeforePublishing: (t: number) => BuildPubSubRecordType<SO_FAR & {maxMsToWaitBeforePublishing: number}>,
neverDelayPublishing: () => BuildPubSubRecordType<SO_FAR & {maxMsToWaitBeforePublishing: 0}>,
}
const buildMaxMsToWaitBeforePublishingFieldConstructor = <SO_FAR>(soFar: SO_FAR): MaxMsToWaitBeforePublishingFieldConstructor<SO_FAR> => (
"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<SO_FAR & {maxMsToWaitBeforePublishing: number}>,
neverDelayPublishing: () =>
buildPubSubRecordType(Object.assign({}, soFar, {maxMsToWaitBeforePublishing: 0})) as BuildPubSubRecordType<SO_FAR & {maxMsToWaitBeforePublishing: 0}>,
}
) as MaxMsToWaitBeforePublishingFieldConstructor<SO_FAR>;
type TypeConstructor<SO_FAR> =
SO_FAR extends {identifier: any, record: any, maxMsToWaitBeforePublishing: number, storedAs: PubSubRecordIsStoredInRedisAsA} ? {
type: SO_FAR,
fields: Set<keyof SO_FAR>,
hasField: (fieldName: string | number | symbol) => fieldName is keyof SO_FAR
} : {}
const buildType = <SO_FAR>(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<SO_FAR> =
NameFieldConstructor<SO_FAR> &
IdentifierFieldConstructor<SO_FAR> &
RecordFieldConstructor<SO_FAR> &
StoredAsConstructor<SO_FAR> & // infinite loop goes away when you comment out this line
MaxMsToWaitBeforePublishingFieldConstructor<SO_FAR> &
TypeConstructor<SO_FAR>
const buildPubSubRecordType = <SO_FAR>(soFar: SO_FAR) => Object.assign(
{},
buildNameFieldConstructor(soFar),
buildIdentifierFieldConstructor(soFar),
buildRecordFieldConstructor(soFar),
buildStoredAsConstructor(soFar),
buildMaxMsToWaitBeforePublishingFieldConstructor(soFar),
buildType(soFar)
) as BuildPubSubRecordType<SO_FAR>;
const PubSubRecordType = buildPubSubRecordType({});

View File

@ -212,12 +212,12 @@ export enum PubSubRecordIsStoredInRedisAsA {
>buildPubSubRecordType(Object.assign({}, soFar, {identifier: instance as TYPE}) as SO_FAR & {identifier: TYPE}) : BuildPubSubRecordType<SO_FAR & { identifier: TYPE; }>
>buildPubSubRecordType : <SO_FAR>(soFar: SO_FAR) => BuildPubSubRecordType<SO_FAR>
>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 : { <T extends {}, U>(target: T, source: U): T & U; <T extends {}, U, V>(target: T, source1: U, source2: V): T & U & V; <T extends {}, U, V, W>(target: T, source1: U, source2: V, source3: W): T & U & V & W; (target: object, ...sources: any[]): any; }
>Object : ObjectConstructor
>assign : { <T extends {}, U>(target: T, source: U): T & U; <T extends {}, U, V>(target: T, source1: U, source2: V): T & U & V; <T extends {}, U, V, W>(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<keyof SO_FAR>; hasField: (fieldName: string | number | symbol) => boolean; }

View File

@ -44,7 +44,7 @@ if ('d' in c) {
>c : A | B
c; // never
>c : never
>c : (A | B) & Record<"d", unknown>
}
if (a in c) {
@ -67,6 +67,6 @@ if (d in c) {
>c : A | B
c; // never
>c : never
>c : (A | B) & Record<"d", unknown>
}

View File

@ -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();

View File

@ -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))

View File

@ -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

View File

@ -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 = <T extends object>(
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<T extends string | number>(
@ -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<T extends string | number, U extends boolean>(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<T extends object, U extends string | number>(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<T>(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<T extends object | "hello">(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<T extends object | string, U extends object | number>(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<T extends number, U extends 0 | 1 | 2>(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<T>(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'.
}

View File

@ -0,0 +1,23 @@
tests/cases/compiler/inKeywordAndUnknown.ts(12,18): error TS2638: Type '{}' may represent a primitive value, which is not permitted as the right operand of the 'in' operator.
==== tests/cases/compiler/inKeywordAndUnknown.ts (1 errors) ====
// Repro from #50531
function f(x: {}, y: unknown) {
if (!("a" in x)) {
return;
}
x; // {}
if (!y) {
return;
}
y; // {}
if (!("a" in y)) {
~
!!! error TS2638: Type '{}' may represent a primitive value, which is not permitted as the right operand of the 'in' operator.
return;
}
y; // {}
}

View File

@ -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>
}

View File

@ -5,8 +5,10 @@ tests/cases/compiler/inKeywordTypeguard.ts(16,11): error TS2339: Property 'a' do
tests/cases/compiler/inKeywordTypeguard.ts(27,11): error TS2339: Property 'b' does not exist on type 'AWithOptionalProp | BWithOptionalProp'.
Property 'b' does not exist on type 'AWithOptionalProp'.
tests/cases/compiler/inKeywordTypeguard.ts(42,11): error TS2339: Property 'b' does not exist on type 'AWithMethod'.
tests/cases/compiler/inKeywordTypeguard.ts(49,11): error TS2339: Property 'a' does not exist on type 'never'.
tests/cases/compiler/inKeywordTypeguard.ts(50,11): error TS2339: Property 'b' does not exist on type 'never'.
tests/cases/compiler/inKeywordTypeguard.ts(49,11): error TS2339: Property 'a' does not exist on type '(AWithMethod | BWithMethod) & Record<"c", unknown>'.
Property 'a' does not exist on type 'BWithMethod & Record<"c", unknown>'.
tests/cases/compiler/inKeywordTypeguard.ts(50,11): error TS2339: Property 'b' does not exist on type '(AWithMethod | BWithMethod) & Record<"c", unknown>'.
Property 'b' does not exist on type 'AWithMethod & Record<"c", unknown>'.
tests/cases/compiler/inKeywordTypeguard.ts(52,11): error TS2339: Property 'a' does not exist on type 'AWithMethod | BWithMethod'.
Property 'a' does not exist on type 'BWithMethod'.
tests/cases/compiler/inKeywordTypeguard.ts(53,11): error TS2339: Property 'b' does not exist on type 'AWithMethod | BWithMethod'.
@ -19,9 +21,14 @@ tests/cases/compiler/inKeywordTypeguard.ts(74,32): error TS2339: Property 'a' do
tests/cases/compiler/inKeywordTypeguard.ts(82,39): error TS2339: Property 'b' does not exist on type 'A'.
tests/cases/compiler/inKeywordTypeguard.ts(84,39): error TS2339: Property 'a' does not exist on type 'B'.
tests/cases/compiler/inKeywordTypeguard.ts(94,26): error TS2339: Property 'a' does not exist on type 'never'.
tests/cases/compiler/inKeywordTypeguard.ts(150,16): error TS2339: Property 'ontouchstart' does not exist on type 'never'.
tests/cases/compiler/inKeywordTypeguard.ts(155,16): error TS2322: Type 'unknown' is not assignable to type 'object'.
tests/cases/compiler/inKeywordTypeguard.ts(158,21): error TS2322: Type 'unknown' is not assignable to type 'object'.
tests/cases/compiler/inKeywordTypeguard.ts(183,16): error TS2322: Type 'T' is not assignable to type 'object'.
tests/cases/compiler/inKeywordTypeguard.ts(186,21): error TS2322: Type 'T' is not assignable to type 'object'.
==== tests/cases/compiler/inKeywordTypeguard.ts (17 errors) ====
==== tests/cases/compiler/inKeywordTypeguard.ts (22 errors) ====
class A { a: string; }
class B { b: string; }
@ -85,10 +92,12 @@ tests/cases/compiler/inKeywordTypeguard.ts(94,26): error TS2339: Property 'a' do
if ("c" in x) {
x.a();
~
!!! error TS2339: Property 'a' does not exist on type 'never'.
!!! error TS2339: Property 'a' does not exist on type '(AWithMethod | BWithMethod) & Record<"c", unknown>'.
!!! error TS2339: Property 'a' does not exist on type 'BWithMethod & Record<"c", unknown>'.
x.b();
~
!!! error TS2339: Property 'b' does not exist on type 'never'.
!!! error TS2339: Property 'b' does not exist on type '(AWithMethod | BWithMethod) & Record<"c", unknown>'.
!!! error TS2339: Property 'b' does not exist on type 'AWithMethod & Record<"c", unknown>'.
} else {
x.a();
~
@ -210,6 +219,145 @@ tests/cases/compiler/inKeywordTypeguard.ts(94,26): error TS2339: Property 'a' do
window.ontouchstart
} else {
window.ontouchstart
~~~~~~~~~~~~
!!! error TS2339: Property 'ontouchstart' does not exist on type 'never'.
}
}
function f1(x: unknown) {
if ("a" in x) {
~
!!! error TS2322: Type 'unknown' is not assignable to type 'object'.
x.a;
}
if (x && "a" in x) {
~
!!! error TS2322: Type 'unknown' is not assignable to type 'object'.
x.a;
}
if (x && typeof x === "object" && "a" in x) {
x.a;
}
if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) {
x.a;
x.b;
x.c;
}
}
function f2(x: object) {
if ("a" in x) {
x.a;
}
if ("a" in x && "b" in x && "c" in x) {
x.a;
x.b;
x.c;
}
}
function f3<T>(x: T) {
if ("a" in x) {
~
!!! error TS2322: Type 'T' is not assignable to type 'object'.
!!! related TS2208 tests/cases/compiler/inKeywordTypeguard.ts:182:13: This type parameter might need an `extends object` constraint.
x.a;
}
if (x && "a" in x) {
~
!!! error TS2322: Type 'T' is not assignable to type 'object'.
!!! related TS2208 tests/cases/compiler/inKeywordTypeguard.ts:182:13: This type parameter might need an `extends object` constraint.
x.a;
}
if (x && typeof x === "object" && "a" in x) {
x.a;
}
if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) {
x.a;
x.b;
x.c;
}
}
function f4(x: { a: string }) {
if ("a" in x) {
x.a;
}
if ("a" in x && "b" in x && "c" in x) {
x.a;
x.b;
x.c;
}
}
function f5(x: { a: string } | { b: string }) {
if ("a" in x) {
x; // { a: string }
}
else if ("b" in x) {
x; // { b: string }
}
else {
x; // never
}
}
function f6(x: { a: string } | { b: string }) {
if ("a" in x) {
x; // { a: string }
}
else if ("a" in x) {
x; // { b: string } & Record<"a", unknown>
}
else {
x; // { b: string }
}
}
// Object and corresponding intersection should narrow the same
function f7(x: { a: string, b: number }, y: { a: string } & { b: number }) {
if ("a" in x) {
x;
}
else {
x; // never
}
if ("a" in y) {
y;
}
else {
y; // never
}
}
const sym = Symbol();
function f8(x: object) {
if ("a" in x && 1 in x && sym in x) {
x.a;
x["a"];
x[1];
x["1"];
x[sym];
}
}
function f9(x: object) {
if ("a" in x && "1" in x && sym in x) {
x.a;
x["a"];
x[1];
x["1"];
x[sym];
}
}
// Repro from #50639
function foo<A>(value: A) {
if (typeof value === "object" && value !== null && "prop" in value) {
value; // A & object & Record<"prop", unknown>
}
}

View File

@ -0,0 +1,541 @@
//// [inKeywordTypeguard.ts]
class A { a: string; }
class B { b: string; }
function negativeClassesTest(x: A | B) {
if ("a" in x) {
x.b = "1";
} else {
x.a = "1";
}
}
function positiveClassesTest(x: A | B) {
if ("a" in x) {
x.b = "1";
} else {
x.a = "1";
}
}
class AWithOptionalProp { a?: string; }
class BWithOptionalProp { b?: string; }
function positiveTestClassesWithOptionalProperties(x: AWithOptionalProp | BWithOptionalProp) {
if ("a" in x) {
x.a = "1";
} else {
x.b = "1";
}
}
class AWithMethod {
a(): string { return ""; }
}
class BWithMethod {
b(): string { return ""; }
}
function negativeTestClassesWithMembers(x: AWithMethod | BWithMethod) {
if ("a" in x) {
x.a();
x.b();
} else {
}
}
function negativeTestClassesWithMemberMissingInBothClasses(x: AWithMethod | BWithMethod) {
if ("c" in x) {
x.a();
x.b();
} else {
x.a();
x.b();
}
}
class C { a: string; }
class D { a: string; }
function negativeMultipleClassesTest(x: A | B | C | D) {
if ("a" in x) {
x.b = "1";
} else {
x.a = "1";
}
}
class ClassWithUnionProp { prop: A | B }
function negativePropTest(x: ClassWithUnionProp) {
if ("a" in x.prop) {
let y: string = x.prop.b;
} else {
let z: string = x.prop.a;
}
}
class NegativeClassTest {
protected prop: A | B;
inThis() {
if ("a" in this.prop) {
let z: number = this.prop.b;
} else {
let y: string = this.prop.a;
}
}
}
class UnreachableCodeDetection {
a: string;
inThis() {
if ("a" in this) {
} else {
let y = this.a;
}
}
}
function positiveIntersectionTest(x: { a: string } & { b: string }) {
if ("a" in x) {
let s: string = x.a;
} else {
let n: never = x;
}
}
// Repro from #38608
declare const error: Error;
if ('extra' in error) {
error // Still Error
} else {
error // Error
}
function narrowsToNever(x: { l: number } | { r: number }) {
let v: number;
if ("l" in x) {
v = x.l;
}
else if ("r" in x) {
v = x.r;
}
else {
v = x
}
return v;
}
type AOrB = { aProp: number } | { bProp: number };
declare function isAOrB(x: unknown): x is AOrB;
declare var x: unknown;
if (isAOrB(x)) {
if ("aProp" in x) {
x.aProp;
}
else if ("bProp" in x) {
x.bProp;
}
// x is never because of the type predicate from unknown
else if ("cProp" in x) {
const _never: never = x;
}
}
function negativeIntersectionTest() {
if ("ontouchstart" in window) {
window.ontouchstart
} else {
window.ontouchstart
}
}
function f1(x: unknown) {
if ("a" in x) {
x.a;
}
if (x && "a" in x) {
x.a;
}
if (x && typeof x === "object" && "a" in x) {
x.a;
}
if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) {
x.a;
x.b;
x.c;
}
}
function f2(x: object) {
if ("a" in x) {
x.a;
}
if ("a" in x && "b" in x && "c" in x) {
x.a;
x.b;
x.c;
}
}
function f3<T>(x: T) {
if ("a" in x) {
x.a;
}
if (x && "a" in x) {
x.a;
}
if (x && typeof x === "object" && "a" in x) {
x.a;
}
if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) {
x.a;
x.b;
x.c;
}
}
function f4(x: { a: string }) {
if ("a" in x) {
x.a;
}
if ("a" in x && "b" in x && "c" in x) {
x.a;
x.b;
x.c;
}
}
function f5(x: { a: string } | { b: string }) {
if ("a" in x) {
x; // { a: string }
}
else if ("b" in x) {
x; // { b: string }
}
else {
x; // never
}
}
function f6(x: { a: string } | { b: string }) {
if ("a" in x) {
x; // { a: string }
}
else if ("a" in x) {
x; // { b: string } & Record<"a", unknown>
}
else {
x; // { b: string }
}
}
// Object and corresponding intersection should narrow the same
function f7(x: { a: string, b: number }, y: { a: string } & { b: number }) {
if ("a" in x) {
x;
}
else {
x; // never
}
if ("a" in y) {
y;
}
else {
y; // never
}
}
const sym = Symbol();
function f8(x: object) {
if ("a" in x && 1 in x && sym in x) {
x.a;
x["a"];
x[1];
x["1"];
x[sym];
}
}
function f9(x: object) {
if ("a" in x && "1" in x && sym in x) {
x.a;
x["a"];
x[1];
x["1"];
x[sym];
}
}
// Repro from #50639
function foo<A>(value: A) {
if (typeof value === "object" && value !== null && "prop" in value) {
value; // A & object & Record<"prop", unknown>
}
}
//// [inKeywordTypeguard.js]
class A {
}
class B {
}
function negativeClassesTest(x) {
if ("a" in x) {
x.b = "1";
}
else {
x.a = "1";
}
}
function positiveClassesTest(x) {
if ("a" in x) {
x.b = "1";
}
else {
x.a = "1";
}
}
class AWithOptionalProp {
}
class BWithOptionalProp {
}
function positiveTestClassesWithOptionalProperties(x) {
if ("a" in x) {
x.a = "1";
}
else {
x.b = "1";
}
}
class AWithMethod {
a() { return ""; }
}
class BWithMethod {
b() { return ""; }
}
function negativeTestClassesWithMembers(x) {
if ("a" in x) {
x.a();
x.b();
}
else {
}
}
function negativeTestClassesWithMemberMissingInBothClasses(x) {
if ("c" in x) {
x.a();
x.b();
}
else {
x.a();
x.b();
}
}
class C {
}
class D {
}
function negativeMultipleClassesTest(x) {
if ("a" in x) {
x.b = "1";
}
else {
x.a = "1";
}
}
class ClassWithUnionProp {
}
function negativePropTest(x) {
if ("a" in x.prop) {
let y = x.prop.b;
}
else {
let z = x.prop.a;
}
}
class NegativeClassTest {
inThis() {
if ("a" in this.prop) {
let z = this.prop.b;
}
else {
let y = this.prop.a;
}
}
}
class UnreachableCodeDetection {
inThis() {
if ("a" in this) {
}
else {
let y = this.a;
}
}
}
function positiveIntersectionTest(x) {
if ("a" in x) {
let s = x.a;
}
else {
let n = x;
}
}
if ('extra' in error) {
error; // Still Error
}
else {
error; // Error
}
function narrowsToNever(x) {
let v;
if ("l" in x) {
v = x.l;
}
else if ("r" in x) {
v = x.r;
}
else {
v = x;
}
return v;
}
if (isAOrB(x)) {
if ("aProp" in x) {
x.aProp;
}
else if ("bProp" in x) {
x.bProp;
}
// x is never because of the type predicate from unknown
else if ("cProp" in x) {
const _never = x;
}
}
function negativeIntersectionTest() {
if ("ontouchstart" in window) {
window.ontouchstart;
}
else {
window.ontouchstart;
}
}
function f1(x) {
if ("a" in x) {
x.a;
}
if (x && "a" in x) {
x.a;
}
if (x && typeof x === "object" && "a" in x) {
x.a;
}
if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) {
x.a;
x.b;
x.c;
}
}
function f2(x) {
if ("a" in x) {
x.a;
}
if ("a" in x && "b" in x && "c" in x) {
x.a;
x.b;
x.c;
}
}
function f3(x) {
if ("a" in x) {
x.a;
}
if (x && "a" in x) {
x.a;
}
if (x && typeof x === "object" && "a" in x) {
x.a;
}
if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) {
x.a;
x.b;
x.c;
}
}
function f4(x) {
if ("a" in x) {
x.a;
}
if ("a" in x && "b" in x && "c" in x) {
x.a;
x.b;
x.c;
}
}
function f5(x) {
if ("a" in x) {
x; // { a: string }
}
else if ("b" in x) {
x; // { b: string }
}
else {
x; // never
}
}
function f6(x) {
if ("a" in x) {
x; // { a: string }
}
else if ("a" in x) {
x; // { b: string } & Record<"a", unknown>
}
else {
x; // { b: string }
}
}
// Object and corresponding intersection should narrow the same
function f7(x, y) {
if ("a" in x) {
x;
}
else {
x; // never
}
if ("a" in y) {
y;
}
else {
y; // never
}
}
const sym = Symbol();
function f8(x) {
if ("a" in x && 1 in x && sym in x) {
x.a;
x["a"];
x[1];
x["1"];
x[sym];
}
}
function f9(x) {
if ("a" in x && "1" in x && sym in x) {
x.a;
x["a"];
x[1];
x["1"];
x[sym];
}
}
// Repro from #50639
function foo(value) {
if (typeof value === "object" && value !== null && "prop" in value) {
value; // A & object & Record<"prop", unknown>
}
}

View File

@ -371,9 +371,357 @@ function negativeIntersectionTest() {
} else {
window.ontouchstart
>window.ontouchstart : Symbol(ontouchstart, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))
>window : Symbol(window, Decl(lib.dom.d.ts, --, --))
>ontouchstart : Symbol(ontouchstart, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))
}
}
function f1(x: unknown) {
>f1 : Symbol(f1, Decl(inKeywordTypeguard.ts, 151, 1))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
if ("a" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
x.a;
>x.a : Symbol(a)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
>a : Symbol(a)
}
if (x && "a" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
x.a;
>x.a : Symbol(a)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
>a : Symbol(a)
}
if (x && typeof x === "object" && "a" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
x.a;
>x.a : Symbol(a)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
>a : Symbol(a)
}
if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
x.a;
>x.a : Symbol(a)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
>a : Symbol(a)
x.b;
>x.b : Symbol(b)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
>b : Symbol(b)
x.c;
>x.c : Symbol(c)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
>c : Symbol(c)
}
}
function f2(x: object) {
>f2 : Symbol(f2, Decl(inKeywordTypeguard.ts, 168, 1))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12))
if ("a" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12))
x.a;
>x.a : Symbol(a)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12))
>a : Symbol(a)
}
if ("a" in x && "b" in x && "c" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12))
x.a;
>x.a : Symbol(a)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12))
>a : Symbol(a)
x.b;
>x.b : Symbol(b)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12))
>b : Symbol(b)
x.c;
>x.c : Symbol(c)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12))
>c : Symbol(c)
}
}
function f3<T>(x: T) {
>f3 : Symbol(f3, Decl(inKeywordTypeguard.ts, 179, 1))
>T : Symbol(T, Decl(inKeywordTypeguard.ts, 181, 12))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
>T : Symbol(T, Decl(inKeywordTypeguard.ts, 181, 12))
if ("a" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
x.a;
>x.a : Symbol(a)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
>a : Symbol(a)
}
if (x && "a" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
x.a;
>x.a : Symbol(a)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
>a : Symbol(a)
}
if (x && typeof x === "object" && "a" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
x.a;
>x.a : Symbol(a)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
>a : Symbol(a)
}
if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
x.a;
>x.a : Symbol(a)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
>a : Symbol(a)
x.b;
>x.b : Symbol(b)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
>b : Symbol(b)
x.c;
>x.c : Symbol(c)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
>c : Symbol(c)
}
}
function f4(x: { a: string }) {
>f4 : Symbol(f4, Decl(inKeywordTypeguard.ts, 196, 1))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12))
>a : Symbol(a, Decl(inKeywordTypeguard.ts, 198, 16))
if ("a" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12))
x.a;
>x.a : Symbol(a, Decl(inKeywordTypeguard.ts, 198, 16))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12))
>a : Symbol(a, Decl(inKeywordTypeguard.ts, 198, 16))
}
if ("a" in x && "b" in x && "c" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12))
x.a;
>x.a : Symbol(a, Decl(inKeywordTypeguard.ts, 198, 16))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12))
>a : Symbol(a, Decl(inKeywordTypeguard.ts, 198, 16))
x.b;
>x.b : Symbol(b)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12))
>b : Symbol(b)
x.c;
>x.c : Symbol(c)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12))
>c : Symbol(c)
}
}
function f5(x: { a: string } | { b: string }) {
>f5 : Symbol(f5, Decl(inKeywordTypeguard.ts, 207, 1))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 209, 12))
>a : Symbol(a, Decl(inKeywordTypeguard.ts, 209, 16))
>b : Symbol(b, Decl(inKeywordTypeguard.ts, 209, 32))
if ("a" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 209, 12))
x; // { a: string }
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 209, 12))
}
else if ("b" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 209, 12))
x; // { b: string }
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 209, 12))
}
else {
x; // never
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 209, 12))
}
}
function f6(x: { a: string } | { b: string }) {
>f6 : Symbol(f6, Decl(inKeywordTypeguard.ts, 219, 1))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 221, 12))
>a : Symbol(a, Decl(inKeywordTypeguard.ts, 221, 16))
>b : Symbol(b, Decl(inKeywordTypeguard.ts, 221, 32))
if ("a" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 221, 12))
x; // { a: string }
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 221, 12))
}
else if ("a" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 221, 12))
x; // { b: string } & Record<"a", unknown>
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 221, 12))
}
else {
x; // { b: string }
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 221, 12))
}
}
// Object and corresponding intersection should narrow the same
function f7(x: { a: string, b: number }, y: { a: string } & { b: number }) {
>f7 : Symbol(f7, Decl(inKeywordTypeguard.ts, 231, 1))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 235, 12))
>a : Symbol(a, Decl(inKeywordTypeguard.ts, 235, 16))
>b : Symbol(b, Decl(inKeywordTypeguard.ts, 235, 27))
>y : Symbol(y, Decl(inKeywordTypeguard.ts, 235, 40))
>a : Symbol(a, Decl(inKeywordTypeguard.ts, 235, 45))
>b : Symbol(b, Decl(inKeywordTypeguard.ts, 235, 61))
if ("a" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 235, 12))
x;
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 235, 12))
}
else {
x; // never
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 235, 12))
}
if ("a" in y) {
>y : Symbol(y, Decl(inKeywordTypeguard.ts, 235, 40))
y;
>y : Symbol(y, Decl(inKeywordTypeguard.ts, 235, 40))
}
else {
y; // never
>y : Symbol(y, Decl(inKeywordTypeguard.ts, 235, 40))
}
}
const sym = Symbol();
>sym : Symbol(sym, Decl(inKeywordTypeguard.ts, 250, 5))
>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
function f8(x: object) {
>f8 : Symbol(f8, Decl(inKeywordTypeguard.ts, 250, 21))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12))
if ("a" in x && 1 in x && sym in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12))
>sym : Symbol(sym, Decl(inKeywordTypeguard.ts, 250, 5))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12))
x.a;
>x.a : Symbol(a)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12))
>a : Symbol(a)
x["a"];
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12))
>"a" : Symbol(a)
x[1];
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12))
>1 : Symbol(1)
x["1"];
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12))
>"1" : Symbol(1)
x[sym];
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12))
>sym : Symbol(sym, Decl(inKeywordTypeguard.ts, 250, 5))
}
}
function f9(x: object) {
>f9 : Symbol(f9, Decl(inKeywordTypeguard.ts, 260, 1))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12))
if ("a" in x && "1" in x && sym in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12))
>sym : Symbol(sym, Decl(inKeywordTypeguard.ts, 250, 5))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12))
x.a;
>x.a : Symbol(a)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12))
>a : Symbol(a)
x["a"];
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12))
>"a" : Symbol(a)
x[1];
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12))
>1 : Symbol(1)
x["1"];
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12))
>"1" : Symbol(1)
x[sym];
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12))
>sym : Symbol(sym, Decl(inKeywordTypeguard.ts, 250, 5))
}
}
// Repro from #50639
function foo<A>(value: A) {
>foo : Symbol(foo, Decl(inKeywordTypeguard.ts, 270, 1))
>A : Symbol(A, Decl(inKeywordTypeguard.ts, 274, 13))
>value : Symbol(value, Decl(inKeywordTypeguard.ts, 274, 16))
>A : Symbol(A, Decl(inKeywordTypeguard.ts, 274, 13))
if (typeof value === "object" && value !== null && "prop" in value) {
>value : Symbol(value, Decl(inKeywordTypeguard.ts, 274, 16))
>value : Symbol(value, Decl(inKeywordTypeguard.ts, 274, 16))
>value : Symbol(value, Decl(inKeywordTypeguard.ts, 274, 16))
value; // A & object & Record<"prop", unknown>
>value : Symbol(value, Decl(inKeywordTypeguard.ts, 274, 16))
}
}

View File

@ -0,0 +1,914 @@
=== tests/cases/compiler/inKeywordTypeguard.ts ===
class A { a: string; }
>A : A
>a : string
class B { b: string; }
>B : B
>b : string
function negativeClassesTest(x: A | B) {
>negativeClassesTest : (x: A | B) => void
>x : A | B
if ("a" in x) {
>"a" in x : boolean
>"a" : "a"
>x : A | B
x.b = "1";
>x.b = "1" : "1"
>x.b : any
>x : A
>b : any
>"1" : "1"
} else {
x.a = "1";
>x.a = "1" : "1"
>x.a : any
>x : B
>a : any
>"1" : "1"
}
}
function positiveClassesTest(x: A | B) {
>positiveClassesTest : (x: A | B) => void
>x : A | B
if ("a" in x) {
>"a" in x : boolean
>"a" : "a"
>x : A | B
x.b = "1";
>x.b = "1" : "1"
>x.b : any
>x : A
>b : any
>"1" : "1"
} else {
x.a = "1";
>x.a = "1" : "1"
>x.a : any
>x : B
>a : any
>"1" : "1"
}
}
class AWithOptionalProp { a?: string; }
>AWithOptionalProp : AWithOptionalProp
>a : string
class BWithOptionalProp { b?: string; }
>BWithOptionalProp : BWithOptionalProp
>b : string
function positiveTestClassesWithOptionalProperties(x: AWithOptionalProp | BWithOptionalProp) {
>positiveTestClassesWithOptionalProperties : (x: AWithOptionalProp | BWithOptionalProp) => void
>x : AWithOptionalProp | BWithOptionalProp
if ("a" in x) {
>"a" in x : boolean
>"a" : "a"
>x : AWithOptionalProp | BWithOptionalProp
x.a = "1";
>x.a = "1" : "1"
>x.a : string
>x : AWithOptionalProp
>a : string
>"1" : "1"
} else {
x.b = "1";
>x.b = "1" : "1"
>x.b : any
>x : AWithOptionalProp | BWithOptionalProp
>b : any
>"1" : "1"
}
}
class AWithMethod {
>AWithMethod : AWithMethod
a(): string { return ""; }
>a : () => string
>"" : ""
}
class BWithMethod {
>BWithMethod : BWithMethod
b(): string { return ""; }
>b : () => string
>"" : ""
}
function negativeTestClassesWithMembers(x: AWithMethod | BWithMethod) {
>negativeTestClassesWithMembers : (x: AWithMethod | BWithMethod) => void
>x : AWithMethod | BWithMethod
if ("a" in x) {
>"a" in x : boolean
>"a" : "a"
>x : AWithMethod | BWithMethod
x.a();
>x.a() : string
>x.a : () => string
>x : AWithMethod
>a : () => string
x.b();
>x.b() : any
>x.b : any
>x : AWithMethod
>b : any
} else {
}
}
function negativeTestClassesWithMemberMissingInBothClasses(x: AWithMethod | BWithMethod) {
>negativeTestClassesWithMemberMissingInBothClasses : (x: AWithMethod | BWithMethod) => void
>x : AWithMethod | BWithMethod
if ("c" in x) {
>"c" in x : boolean
>"c" : "c"
>x : AWithMethod | BWithMethod
x.a();
>x.a() : any
>x.a : any
>x : (AWithMethod | BWithMethod) & Record<"c", unknown>
>a : any
x.b();
>x.b() : any
>x.b : any
>x : (AWithMethod | BWithMethod) & Record<"c", unknown>
>b : any
} else {
x.a();
>x.a() : any
>x.a : any
>x : AWithMethod | BWithMethod
>a : any
x.b();
>x.b() : any
>x.b : any
>x : AWithMethod | BWithMethod
>b : any
}
}
class C { a: string; }
>C : C
>a : string
class D { a: string; }
>D : D
>a : string
function negativeMultipleClassesTest(x: A | B | C | D) {
>negativeMultipleClassesTest : (x: A | B | C | D) => void
>x : A | B | C | D
if ("a" in x) {
>"a" in x : boolean
>"a" : "a"
>x : A | B | C | D
x.b = "1";
>x.b = "1" : "1"
>x.b : any
>x : A | C | D
>b : any
>"1" : "1"
} else {
x.a = "1";
>x.a = "1" : "1"
>x.a : any
>x : B
>a : any
>"1" : "1"
}
}
class ClassWithUnionProp { prop: A | B }
>ClassWithUnionProp : ClassWithUnionProp
>prop : A | B
function negativePropTest(x: ClassWithUnionProp) {
>negativePropTest : (x: ClassWithUnionProp) => void
>x : ClassWithUnionProp
if ("a" in x.prop) {
>"a" in x.prop : boolean
>"a" : "a"
>x.prop : A | B
>x : ClassWithUnionProp
>prop : A | B
let y: string = x.prop.b;
>y : string
>x.prop.b : any
>x.prop : A
>x : ClassWithUnionProp
>prop : A
>b : any
} else {
let z: string = x.prop.a;
>z : string
>x.prop.a : any
>x.prop : B
>x : ClassWithUnionProp
>prop : B
>a : any
}
}
class NegativeClassTest {
>NegativeClassTest : NegativeClassTest
protected prop: A | B;
>prop : A | B
inThis() {
>inThis : () => void
if ("a" in this.prop) {
>"a" in this.prop : boolean
>"a" : "a"
>this.prop : A | B
>this : this
>prop : A | B
let z: number = this.prop.b;
>z : number
>this.prop.b : any
>this.prop : A
>this : this
>prop : A
>b : any
} else {
let y: string = this.prop.a;
>y : string
>this.prop.a : any
>this.prop : B
>this : this
>prop : B
>a : any
}
}
}
class UnreachableCodeDetection {
>UnreachableCodeDetection : UnreachableCodeDetection
a: string;
>a : string
inThis() {
>inThis : () => void
if ("a" in this) {
>"a" in this : boolean
>"a" : "a"
>this : this
} else {
let y = this.a;
>y : any
>this.a : any
>this : never
>a : any
}
}
}
function positiveIntersectionTest(x: { a: string } & { b: string }) {
>positiveIntersectionTest : (x: { a: string;} & { b: string;}) => void
>x : { a: string; } & { b: string; }
>a : string
>b : string
if ("a" in x) {
>"a" in x : boolean
>"a" : "a"
>x : { a: string; } & { b: string; }
let s: string = x.a;
>s : string
>x.a : string
>x : { a: string; } & { b: string; }
>a : string
} else {
let n: never = x;
>n : never
>x : never
}
}
// Repro from #38608
declare const error: Error;
>error : Error
if ('extra' in error) {
>'extra' in error : boolean
>'extra' : "extra"
>error : Error
error // Still Error
>error : Error & Record<"extra", unknown>
} else {
error // Error
>error : Error
}
function narrowsToNever(x: { l: number } | { r: number }) {
>narrowsToNever : (x: { l: number;} | { r: number;}) => number
>x : { l: number; } | { r: number; }
>l : number
>r : number
let v: number;
>v : number
if ("l" in x) {
>"l" in x : boolean
>"l" : "l"
>x : { l: number; } | { r: number; }
v = x.l;
>v = x.l : number
>v : number
>x.l : number
>x : { l: number; }
>l : number
}
else if ("r" in x) {
>"r" in x : boolean
>"r" : "r"
>x : { r: number; }
v = x.r;
>v = x.r : number
>v : number
>x.r : number
>x : { r: number; }
>r : number
}
else {
v = x
>v = x : never
>v : number
>x : never
}
return v;
>v : number
}
type AOrB = { aProp: number } | { bProp: number };
>AOrB : { aProp: number; } | { bProp: number; }
>aProp : number
>bProp : number
declare function isAOrB(x: unknown): x is AOrB;
>isAOrB : (x: unknown) => x is AOrB
>x : unknown
declare var x: unknown;
>x : unknown
if (isAOrB(x)) {
>isAOrB(x) : boolean
>isAOrB : (x: unknown) => x is AOrB
>x : unknown
if ("aProp" in x) {
>"aProp" in x : boolean
>"aProp" : "aProp"
>x : AOrB
x.aProp;
>x.aProp : number
>x : { aProp: number; }
>aProp : number
}
else if ("bProp" in x) {
>"bProp" in x : boolean
>"bProp" : "bProp"
>x : { bProp: number; }
x.bProp;
>x.bProp : number
>x : { bProp: number; }
>bProp : number
}
// x is never because of the type predicate from unknown
else if ("cProp" in x) {
>"cProp" in x : boolean
>"cProp" : "cProp"
>x : never
const _never: never = x;
>_never : never
>x : never
}
}
function negativeIntersectionTest() {
>negativeIntersectionTest : () => void
if ("ontouchstart" in window) {
>"ontouchstart" in window : boolean
>"ontouchstart" : "ontouchstart"
>window : Window & typeof globalThis
window.ontouchstart
>window.ontouchstart : ((this: GlobalEventHandlers, ev: TouchEvent) => any) & ((this: Window, ev: TouchEvent) => any)
>window : Window & typeof globalThis
>ontouchstart : ((this: GlobalEventHandlers, ev: TouchEvent) => any) & ((this: Window, ev: TouchEvent) => any)
} else {
window.ontouchstart
>window.ontouchstart : any
>window : never
>ontouchstart : any
}
}
function f1(x: unknown) {
>f1 : (x: unknown) => void
>x : unknown
if ("a" in x) {
>"a" in x : boolean
>"a" : "a"
>x : unknown
x.a;
>x.a : unknown
>x : Record<"a", unknown>
>a : unknown
}
if (x && "a" in x) {
>x && "a" in x : boolean
>x : unknown
>"a" in x : boolean
>"a" : "a"
>x : unknown
x.a;
>x.a : unknown
>x : Record<"a", unknown>
>a : unknown
}
if (x && typeof x === "object" && "a" in x) {
>x && typeof x === "object" && "a" in x : boolean
>x && typeof x === "object" : boolean
>x : unknown
>typeof x === "object" : boolean
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : unknown
>"object" : "object"
>"a" in x : boolean
>"a" : "a"
>x : object
x.a;
>x.a : unknown
>x : object & Record<"a", unknown>
>a : unknown
}
if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) {
>x && typeof x === "object" && "a" in x && "b" in x && "c" in x : boolean
>x && typeof x === "object" && "a" in x && "b" in x : boolean
>x && typeof x === "object" && "a" in x : boolean
>x && typeof x === "object" : boolean
>x : unknown
>typeof x === "object" : boolean
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : unknown
>"object" : "object"
>"a" in x : boolean
>"a" : "a"
>x : object
>"b" in x : boolean
>"b" : "b"
>x : object & Record<"a", unknown>
>"c" in x : boolean
>"c" : "c"
>x : object & Record<"a", unknown> & Record<"b", unknown>
x.a;
>x.a : unknown
>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown>
>a : unknown
x.b;
>x.b : unknown
>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown>
>b : unknown
x.c;
>x.c : unknown
>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown>
>c : unknown
}
}
function f2(x: object) {
>f2 : (x: object) => void
>x : object
if ("a" in x) {
>"a" in x : boolean
>"a" : "a"
>x : object
x.a;
>x.a : unknown
>x : object & Record<"a", unknown>
>a : unknown
}
if ("a" in x && "b" in x && "c" in x) {
>"a" in x && "b" in x && "c" in x : boolean
>"a" in x && "b" in x : boolean
>"a" in x : boolean
>"a" : "a"
>x : object
>"b" in x : boolean
>"b" : "b"
>x : object & Record<"a", unknown>
>"c" in x : boolean
>"c" : "c"
>x : object & Record<"a", unknown> & Record<"b", unknown>
x.a;
>x.a : unknown
>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown>
>a : unknown
x.b;
>x.b : unknown
>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown>
>b : unknown
x.c;
>x.c : unknown
>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown>
>c : unknown
}
}
function f3<T>(x: T) {
>f3 : <T>(x: T) => void
>x : T
if ("a" in x) {
>"a" in x : boolean
>"a" : "a"
>x : T
x.a;
>x.a : unknown
>x : T & Record<"a", unknown>
>a : unknown
}
if (x && "a" in x) {
>x && "a" in x : boolean
>x : T
>"a" in x : boolean
>"a" : "a"
>x : T
x.a;
>x.a : unknown
>x : T & Record<"a", unknown>
>a : unknown
}
if (x && typeof x === "object" && "a" in x) {
>x && typeof x === "object" && "a" in x : boolean
>x && typeof x === "object" : boolean
>x : T
>typeof x === "object" : boolean
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : T
>"object" : "object"
>"a" in x : boolean
>"a" : "a"
>x : T & object
x.a;
>x.a : unknown
>x : T & object & Record<"a", unknown>
>a : unknown
}
if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) {
>x && typeof x === "object" && "a" in x && "b" in x && "c" in x : boolean
>x && typeof x === "object" && "a" in x && "b" in x : boolean
>x && typeof x === "object" && "a" in x : boolean
>x && typeof x === "object" : boolean
>x : T
>typeof x === "object" : boolean
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : T
>"object" : "object"
>"a" in x : boolean
>"a" : "a"
>x : T & object
>"b" in x : boolean
>"b" : "b"
>x : T & object & Record<"a", unknown>
>"c" in x : boolean
>"c" : "c"
>x : T & object & Record<"a", unknown> & Record<"b", unknown>
x.a;
>x.a : unknown
>x : T & object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown>
>a : unknown
x.b;
>x.b : unknown
>x : T & object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown>
>b : unknown
x.c;
>x.c : unknown
>x : T & object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown>
>c : unknown
}
}
function f4(x: { a: string }) {
>f4 : (x: { a: string;}) => void
>x : { a: string; }
>a : string
if ("a" in x) {
>"a" in x : boolean
>"a" : "a"
>x : { a: string; }
x.a;
>x.a : string
>x : { a: string; }
>a : string
}
if ("a" in x && "b" in x && "c" in x) {
>"a" in x && "b" in x && "c" in x : boolean
>"a" in x && "b" in x : boolean
>"a" in x : boolean
>"a" : "a"
>x : { a: string; }
>"b" in x : boolean
>"b" : "b"
>x : { a: string; }
>"c" in x : boolean
>"c" : "c"
>x : { a: string; } & Record<"b", unknown>
x.a;
>x.a : string
>x : { a: string; } & Record<"b", unknown> & Record<"c", unknown>
>a : string
x.b;
>x.b : unknown
>x : { a: string; } & Record<"b", unknown> & Record<"c", unknown>
>b : unknown
x.c;
>x.c : unknown
>x : { a: string; } & Record<"b", unknown> & Record<"c", unknown>
>c : unknown
}
}
function f5(x: { a: string } | { b: string }) {
>f5 : (x: { a: string;} | { b: string;}) => void
>x : { a: string; } | { b: string; }
>a : string
>b : string
if ("a" in x) {
>"a" in x : boolean
>"a" : "a"
>x : { a: string; } | { b: string; }
x; // { a: string }
>x : { a: string; }
}
else if ("b" in x) {
>"b" in x : boolean
>"b" : "b"
>x : { b: string; }
x; // { b: string }
>x : { b: string; }
}
else {
x; // never
>x : never
}
}
function f6(x: { a: string } | { b: string }) {
>f6 : (x: { a: string;} | { b: string;}) => void
>x : { a: string; } | { b: string; }
>a : string
>b : string
if ("a" in x) {
>"a" in x : boolean
>"a" : "a"
>x : { a: string; } | { b: string; }
x; // { a: string }
>x : { a: string; }
}
else if ("a" in x) {
>"a" in x : boolean
>"a" : "a"
>x : { b: string; }
x; // { b: string } & Record<"a", unknown>
>x : { b: string; } & Record<"a", unknown>
}
else {
x; // { b: string }
>x : { b: string; }
}
}
// Object and corresponding intersection should narrow the same
function f7(x: { a: string, b: number }, y: { a: string } & { b: number }) {
>f7 : (x: { a: string; b: number;}, y: { a: string;} & { b: number;}) => void
>x : { a: string; b: number; }
>a : string
>b : number
>y : { a: string; } & { b: number; }
>a : string
>b : number
if ("a" in x) {
>"a" in x : boolean
>"a" : "a"
>x : { a: string; b: number; }
x;
>x : { a: string; b: number; }
}
else {
x; // never
>x : never
}
if ("a" in y) {
>"a" in y : boolean
>"a" : "a"
>y : { a: string; } & { b: number; }
y;
>y : { a: string; } & { b: number; }
}
else {
y; // never
>y : never
}
}
const sym = Symbol();
>sym : unique symbol
>Symbol() : unique symbol
>Symbol : SymbolConstructor
function f8(x: object) {
>f8 : (x: object) => void
>x : object
if ("a" in x && 1 in x && sym in x) {
>"a" in x && 1 in x && sym in x : boolean
>"a" in x && 1 in x : boolean
>"a" in x : boolean
>"a" : "a"
>x : object
>1 in x : boolean
>1 : 1
>x : object & Record<"a", unknown>
>sym in x : boolean
>sym : unique symbol
>x : object & Record<"a", unknown> & Record<1, unknown>
x.a;
>x.a : unknown
>x : object & Record<"a", unknown> & Record<1, unknown> & Record<unique symbol, unknown>
>a : unknown
x["a"];
>x["a"] : unknown
>x : object & Record<"a", unknown> & Record<1, unknown> & Record<unique symbol, unknown>
>"a" : "a"
x[1];
>x[1] : unknown
>x : object & Record<"a", unknown> & Record<1, unknown> & Record<unique symbol, unknown>
>1 : 1
x["1"];
>x["1"] : unknown
>x : object & Record<"a", unknown> & Record<1, unknown> & Record<unique symbol, unknown>
>"1" : "1"
x[sym];
>x[sym] : unknown
>x : object & Record<"a", unknown> & Record<1, unknown> & Record<unique symbol, unknown>
>sym : unique symbol
}
}
function f9(x: object) {
>f9 : (x: object) => void
>x : object
if ("a" in x && "1" in x && sym in x) {
>"a" in x && "1" in x && sym in x : boolean
>"a" in x && "1" in x : boolean
>"a" in x : boolean
>"a" : "a"
>x : object
>"1" in x : boolean
>"1" : "1"
>x : object & Record<"a", unknown>
>sym in x : boolean
>sym : unique symbol
>x : object & Record<"a", unknown> & Record<"1", unknown>
x.a;
>x.a : unknown
>x : object & Record<"a", unknown> & Record<"1", unknown> & Record<unique symbol, unknown>
>a : unknown
x["a"];
>x["a"] : unknown
>x : object & Record<"a", unknown> & Record<"1", unknown> & Record<unique symbol, unknown>
>"a" : "a"
x[1];
>x[1] : unknown
>x : object & Record<"a", unknown> & Record<"1", unknown> & Record<unique symbol, unknown>
>1 : 1
x["1"];
>x["1"] : unknown
>x : object & Record<"a", unknown> & Record<"1", unknown> & Record<unique symbol, unknown>
>"1" : "1"
x[sym];
>x[sym] : unknown
>x : object & Record<"a", unknown> & Record<"1", unknown> & Record<unique symbol, unknown>
>sym : unique symbol
}
}
// Repro from #50639
function foo<A>(value: A) {
>foo : <A>(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>
}
}

View File

@ -0,0 +1,383 @@
tests/cases/compiler/inKeywordTypeguard.ts(1,11): error TS2564: Property 'a' has no initializer and is not definitely assigned in the constructor.
tests/cases/compiler/inKeywordTypeguard.ts(2,11): error TS2564: Property 'b' has no initializer and is not definitely assigned in the constructor.
tests/cases/compiler/inKeywordTypeguard.ts(6,11): error TS2339: Property 'b' does not exist on type 'A'.
tests/cases/compiler/inKeywordTypeguard.ts(8,11): error TS2339: Property 'a' does not exist on type 'B'.
tests/cases/compiler/inKeywordTypeguard.ts(14,11): error TS2339: Property 'b' does not exist on type 'A'.
tests/cases/compiler/inKeywordTypeguard.ts(16,11): error TS2339: Property 'a' does not exist on type 'B'.
tests/cases/compiler/inKeywordTypeguard.ts(27,11): error TS2339: Property 'b' does not exist on type 'AWithOptionalProp | BWithOptionalProp'.
Property 'b' does not exist on type 'AWithOptionalProp'.
tests/cases/compiler/inKeywordTypeguard.ts(42,11): error TS2339: Property 'b' does not exist on type 'AWithMethod'.
tests/cases/compiler/inKeywordTypeguard.ts(49,11): error TS2339: Property 'a' does not exist on type '(AWithMethod | BWithMethod) & Record<"c", unknown>'.
Property 'a' does not exist on type 'BWithMethod & Record<"c", unknown>'.
tests/cases/compiler/inKeywordTypeguard.ts(50,11): error TS2339: Property 'b' does not exist on type '(AWithMethod | BWithMethod) & Record<"c", unknown>'.
Property 'b' does not exist on type 'AWithMethod & Record<"c", unknown>'.
tests/cases/compiler/inKeywordTypeguard.ts(52,11): error TS2339: Property 'a' does not exist on type 'AWithMethod | BWithMethod'.
Property 'a' does not exist on type 'BWithMethod'.
tests/cases/compiler/inKeywordTypeguard.ts(53,11): error TS2339: Property 'b' does not exist on type 'AWithMethod | BWithMethod'.
Property 'b' does not exist on type 'AWithMethod'.
tests/cases/compiler/inKeywordTypeguard.ts(57,11): error TS2564: Property 'a' has no initializer and is not definitely assigned in the constructor.
tests/cases/compiler/inKeywordTypeguard.ts(58,11): error TS2564: Property 'a' has no initializer and is not definitely assigned in the constructor.
tests/cases/compiler/inKeywordTypeguard.ts(62,11): error TS2339: Property 'b' does not exist on type 'A | C | D'.
Property 'b' does not exist on type 'A'.
tests/cases/compiler/inKeywordTypeguard.ts(64,11): error TS2339: Property 'a' does not exist on type 'B'.
tests/cases/compiler/inKeywordTypeguard.ts(68,28): error TS2564: Property 'prop' has no initializer and is not definitely assigned in the constructor.
tests/cases/compiler/inKeywordTypeguard.ts(72,32): error TS2339: Property 'b' does not exist on type 'A'.
tests/cases/compiler/inKeywordTypeguard.ts(74,32): error TS2339: Property 'a' does not exist on type 'B'.
tests/cases/compiler/inKeywordTypeguard.ts(79,15): error TS2564: Property 'prop' has no initializer and is not definitely assigned in the constructor.
tests/cases/compiler/inKeywordTypeguard.ts(82,39): error TS2339: Property 'b' does not exist on type 'A'.
tests/cases/compiler/inKeywordTypeguard.ts(84,39): error TS2339: Property 'a' does not exist on type 'B'.
tests/cases/compiler/inKeywordTypeguard.ts(90,5): error TS2564: Property 'a' has no initializer and is not definitely assigned in the constructor.
tests/cases/compiler/inKeywordTypeguard.ts(94,26): error TS2339: Property 'a' does not exist on type 'never'.
tests/cases/compiler/inKeywordTypeguard.ts(150,16): error TS2339: Property 'ontouchstart' does not exist on type 'never'.
tests/cases/compiler/inKeywordTypeguard.ts(155,16): error TS18046: 'x' is of type 'unknown'.
tests/cases/compiler/inKeywordTypeguard.ts(158,21): error TS2638: Type '{}' may represent a primitive value, which is not permitted as the right operand of the 'in' operator.
tests/cases/compiler/inKeywordTypeguard.ts(183,16): error TS2322: Type 'T' is not assignable to type 'object'.
tests/cases/compiler/inKeywordTypeguard.ts(186,21): error TS2638: Type 'NonNullable<T>' may represent a primitive value, which is not permitted as the right operand of the 'in' operator.
==== tests/cases/compiler/inKeywordTypeguard.ts (29 errors) ====
class A { a: string; }
~
!!! error TS2564: Property 'a' has no initializer and is not definitely assigned in the constructor.
class B { b: string; }
~
!!! error TS2564: Property 'b' has no initializer and is not definitely assigned in the constructor.
function negativeClassesTest(x: A | B) {
if ("a" in x) {
x.b = "1";
~
!!! error TS2339: Property 'b' does not exist on type 'A'.
} else {
x.a = "1";
~
!!! error TS2339: Property 'a' does not exist on type 'B'.
}
}
function positiveClassesTest(x: A | B) {
if ("a" in x) {
x.b = "1";
~
!!! error TS2339: Property 'b' does not exist on type 'A'.
} else {
x.a = "1";
~
!!! error TS2339: Property 'a' does not exist on type 'B'.
}
}
class AWithOptionalProp { a?: string; }
class BWithOptionalProp { b?: string; }
function positiveTestClassesWithOptionalProperties(x: AWithOptionalProp | BWithOptionalProp) {
if ("a" in x) {
x.a = "1";
} else {
x.b = "1";
~
!!! error TS2339: Property 'b' does not exist on type 'AWithOptionalProp | BWithOptionalProp'.
!!! error TS2339: Property 'b' does not exist on type 'AWithOptionalProp'.
}
}
class AWithMethod {
a(): string { return ""; }
}
class BWithMethod {
b(): string { return ""; }
}
function negativeTestClassesWithMembers(x: AWithMethod | BWithMethod) {
if ("a" in x) {
x.a();
x.b();
~
!!! error TS2339: Property 'b' does not exist on type 'AWithMethod'.
} else {
}
}
function negativeTestClassesWithMemberMissingInBothClasses(x: AWithMethod | BWithMethod) {
if ("c" in x) {
x.a();
~
!!! error TS2339: Property 'a' does not exist on type '(AWithMethod | BWithMethod) & Record<"c", unknown>'.
!!! error TS2339: Property 'a' does not exist on type 'BWithMethod & Record<"c", unknown>'.
x.b();
~
!!! error TS2339: Property 'b' does not exist on type '(AWithMethod | BWithMethod) & Record<"c", unknown>'.
!!! error TS2339: Property 'b' does not exist on type 'AWithMethod & Record<"c", unknown>'.
} else {
x.a();
~
!!! error TS2339: Property 'a' does not exist on type 'AWithMethod | BWithMethod'.
!!! error TS2339: Property 'a' does not exist on type 'BWithMethod'.
x.b();
~
!!! error TS2339: Property 'b' does not exist on type 'AWithMethod | BWithMethod'.
!!! error TS2339: Property 'b' does not exist on type 'AWithMethod'.
}
}
class C { a: string; }
~
!!! error TS2564: Property 'a' has no initializer and is not definitely assigned in the constructor.
class D { a: string; }
~
!!! error TS2564: Property 'a' has no initializer and is not definitely assigned in the constructor.
function negativeMultipleClassesTest(x: A | B | C | D) {
if ("a" in x) {
x.b = "1";
~
!!! error TS2339: Property 'b' does not exist on type 'A | C | D'.
!!! error TS2339: Property 'b' does not exist on type 'A'.
} else {
x.a = "1";
~
!!! error TS2339: Property 'a' does not exist on type 'B'.
}
}
class ClassWithUnionProp { prop: A | B }
~~~~
!!! error TS2564: Property 'prop' has no initializer and is not definitely assigned in the constructor.
function negativePropTest(x: ClassWithUnionProp) {
if ("a" in x.prop) {
let y: string = x.prop.b;
~
!!! error TS2339: Property 'b' does not exist on type 'A'.
} else {
let z: string = x.prop.a;
~
!!! error TS2339: Property 'a' does not exist on type 'B'.
}
}
class NegativeClassTest {
protected prop: A | B;
~~~~
!!! error TS2564: Property 'prop' has no initializer and is not definitely assigned in the constructor.
inThis() {
if ("a" in this.prop) {
let z: number = this.prop.b;
~
!!! error TS2339: Property 'b' does not exist on type 'A'.
} else {
let y: string = this.prop.a;
~
!!! error TS2339: Property 'a' does not exist on type 'B'.
}
}
}
class UnreachableCodeDetection {
a: string;
~
!!! error TS2564: Property 'a' has no initializer and is not definitely assigned in the constructor.
inThis() {
if ("a" in this) {
} else {
let y = this.a;
~
!!! error TS2339: Property 'a' does not exist on type 'never'.
}
}
}
function positiveIntersectionTest(x: { a: string } & { b: string }) {
if ("a" in x) {
let s: string = x.a;
} else {
let n: never = x;
}
}
// Repro from #38608
declare const error: Error;
if ('extra' in error) {
error // Still Error
} else {
error // Error
}
function narrowsToNever(x: { l: number } | { r: number }) {
let v: number;
if ("l" in x) {
v = x.l;
}
else if ("r" in x) {
v = x.r;
}
else {
v = x
}
return v;
}
type AOrB = { aProp: number } | { bProp: number };
declare function isAOrB(x: unknown): x is AOrB;
declare var x: unknown;
if (isAOrB(x)) {
if ("aProp" in x) {
x.aProp;
}
else if ("bProp" in x) {
x.bProp;
}
// x is never because of the type predicate from unknown
else if ("cProp" in x) {
const _never: never = x;
}
}
function negativeIntersectionTest() {
if ("ontouchstart" in window) {
window.ontouchstart
} else {
window.ontouchstart
~~~~~~~~~~~~
!!! error TS2339: Property 'ontouchstart' does not exist on type 'never'.
}
}
function f1(x: unknown) {
if ("a" in x) {
~
!!! error TS18046: 'x' is of type 'unknown'.
x.a;
}
if (x && "a" in x) {
~
!!! error TS2638: Type '{}' may represent a primitive value, which is not permitted as the right operand of the 'in' operator.
x.a;
}
if (x && typeof x === "object" && "a" in x) {
x.a;
}
if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) {
x.a;
x.b;
x.c;
}
}
function f2(x: object) {
if ("a" in x) {
x.a;
}
if ("a" in x && "b" in x && "c" in x) {
x.a;
x.b;
x.c;
}
}
function f3<T>(x: T) {
if ("a" in x) {
~
!!! error TS2322: Type 'T' is not assignable to type 'object'.
!!! related TS2208 tests/cases/compiler/inKeywordTypeguard.ts:182:13: This type parameter might need an `extends object` constraint.
x.a;
}
if (x && "a" in x) {
~
!!! error TS2638: Type 'NonNullable<T>' may represent a primitive value, which is not permitted as the right operand of the 'in' operator.
x.a;
}
if (x && typeof x === "object" && "a" in x) {
x.a;
}
if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) {
x.a;
x.b;
x.c;
}
}
function f4(x: { a: string }) {
if ("a" in x) {
x.a;
}
if ("a" in x && "b" in x && "c" in x) {
x.a;
x.b;
x.c;
}
}
function f5(x: { a: string } | { b: string }) {
if ("a" in x) {
x; // { a: string }
}
else if ("b" in x) {
x; // { b: string }
}
else {
x; // never
}
}
function f6(x: { a: string } | { b: string }) {
if ("a" in x) {
x; // { a: string }
}
else if ("a" in x) {
x; // { b: string } & Record<"a", unknown>
}
else {
x; // { b: string }
}
}
// Object and corresponding intersection should narrow the same
function f7(x: { a: string, b: number }, y: { a: string } & { b: number }) {
if ("a" in x) {
x;
}
else {
x; // never
}
if ("a" in y) {
y;
}
else {
y; // never
}
}
const sym = Symbol();
function f8(x: object) {
if ("a" in x && 1 in x && sym in x) {
x.a;
x["a"];
x[1];
x["1"];
x[sym];
}
}
function f9(x: object) {
if ("a" in x && "1" in x && sym in x) {
x.a;
x["a"];
x[1];
x["1"];
x[sym];
}
}
// Repro from #50639
function foo<A>(value: A) {
if (typeof value === "object" && value !== null && "prop" in value) {
value; // A & object & Record<"prop", unknown>
}
}

View File

@ -0,0 +1,542 @@
//// [inKeywordTypeguard.ts]
class A { a: string; }
class B { b: string; }
function negativeClassesTest(x: A | B) {
if ("a" in x) {
x.b = "1";
} else {
x.a = "1";
}
}
function positiveClassesTest(x: A | B) {
if ("a" in x) {
x.b = "1";
} else {
x.a = "1";
}
}
class AWithOptionalProp { a?: string; }
class BWithOptionalProp { b?: string; }
function positiveTestClassesWithOptionalProperties(x: AWithOptionalProp | BWithOptionalProp) {
if ("a" in x) {
x.a = "1";
} else {
x.b = "1";
}
}
class AWithMethod {
a(): string { return ""; }
}
class BWithMethod {
b(): string { return ""; }
}
function negativeTestClassesWithMembers(x: AWithMethod | BWithMethod) {
if ("a" in x) {
x.a();
x.b();
} else {
}
}
function negativeTestClassesWithMemberMissingInBothClasses(x: AWithMethod | BWithMethod) {
if ("c" in x) {
x.a();
x.b();
} else {
x.a();
x.b();
}
}
class C { a: string; }
class D { a: string; }
function negativeMultipleClassesTest(x: A | B | C | D) {
if ("a" in x) {
x.b = "1";
} else {
x.a = "1";
}
}
class ClassWithUnionProp { prop: A | B }
function negativePropTest(x: ClassWithUnionProp) {
if ("a" in x.prop) {
let y: string = x.prop.b;
} else {
let z: string = x.prop.a;
}
}
class NegativeClassTest {
protected prop: A | B;
inThis() {
if ("a" in this.prop) {
let z: number = this.prop.b;
} else {
let y: string = this.prop.a;
}
}
}
class UnreachableCodeDetection {
a: string;
inThis() {
if ("a" in this) {
} else {
let y = this.a;
}
}
}
function positiveIntersectionTest(x: { a: string } & { b: string }) {
if ("a" in x) {
let s: string = x.a;
} else {
let n: never = x;
}
}
// Repro from #38608
declare const error: Error;
if ('extra' in error) {
error // Still Error
} else {
error // Error
}
function narrowsToNever(x: { l: number } | { r: number }) {
let v: number;
if ("l" in x) {
v = x.l;
}
else if ("r" in x) {
v = x.r;
}
else {
v = x
}
return v;
}
type AOrB = { aProp: number } | { bProp: number };
declare function isAOrB(x: unknown): x is AOrB;
declare var x: unknown;
if (isAOrB(x)) {
if ("aProp" in x) {
x.aProp;
}
else if ("bProp" in x) {
x.bProp;
}
// x is never because of the type predicate from unknown
else if ("cProp" in x) {
const _never: never = x;
}
}
function negativeIntersectionTest() {
if ("ontouchstart" in window) {
window.ontouchstart
} else {
window.ontouchstart
}
}
function f1(x: unknown) {
if ("a" in x) {
x.a;
}
if (x && "a" in x) {
x.a;
}
if (x && typeof x === "object" && "a" in x) {
x.a;
}
if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) {
x.a;
x.b;
x.c;
}
}
function f2(x: object) {
if ("a" in x) {
x.a;
}
if ("a" in x && "b" in x && "c" in x) {
x.a;
x.b;
x.c;
}
}
function f3<T>(x: T) {
if ("a" in x) {
x.a;
}
if (x && "a" in x) {
x.a;
}
if (x && typeof x === "object" && "a" in x) {
x.a;
}
if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) {
x.a;
x.b;
x.c;
}
}
function f4(x: { a: string }) {
if ("a" in x) {
x.a;
}
if ("a" in x && "b" in x && "c" in x) {
x.a;
x.b;
x.c;
}
}
function f5(x: { a: string } | { b: string }) {
if ("a" in x) {
x; // { a: string }
}
else if ("b" in x) {
x; // { b: string }
}
else {
x; // never
}
}
function f6(x: { a: string } | { b: string }) {
if ("a" in x) {
x; // { a: string }
}
else if ("a" in x) {
x; // { b: string } & Record<"a", unknown>
}
else {
x; // { b: string }
}
}
// Object and corresponding intersection should narrow the same
function f7(x: { a: string, b: number }, y: { a: string } & { b: number }) {
if ("a" in x) {
x;
}
else {
x; // never
}
if ("a" in y) {
y;
}
else {
y; // never
}
}
const sym = Symbol();
function f8(x: object) {
if ("a" in x && 1 in x && sym in x) {
x.a;
x["a"];
x[1];
x["1"];
x[sym];
}
}
function f9(x: object) {
if ("a" in x && "1" in x && sym in x) {
x.a;
x["a"];
x[1];
x["1"];
x[sym];
}
}
// Repro from #50639
function foo<A>(value: A) {
if (typeof value === "object" && value !== null && "prop" in value) {
value; // A & object & Record<"prop", unknown>
}
}
//// [inKeywordTypeguard.js]
"use strict";
class A {
}
class B {
}
function negativeClassesTest(x) {
if ("a" in x) {
x.b = "1";
}
else {
x.a = "1";
}
}
function positiveClassesTest(x) {
if ("a" in x) {
x.b = "1";
}
else {
x.a = "1";
}
}
class AWithOptionalProp {
}
class BWithOptionalProp {
}
function positiveTestClassesWithOptionalProperties(x) {
if ("a" in x) {
x.a = "1";
}
else {
x.b = "1";
}
}
class AWithMethod {
a() { return ""; }
}
class BWithMethod {
b() { return ""; }
}
function negativeTestClassesWithMembers(x) {
if ("a" in x) {
x.a();
x.b();
}
else {
}
}
function negativeTestClassesWithMemberMissingInBothClasses(x) {
if ("c" in x) {
x.a();
x.b();
}
else {
x.a();
x.b();
}
}
class C {
}
class D {
}
function negativeMultipleClassesTest(x) {
if ("a" in x) {
x.b = "1";
}
else {
x.a = "1";
}
}
class ClassWithUnionProp {
}
function negativePropTest(x) {
if ("a" in x.prop) {
let y = x.prop.b;
}
else {
let z = x.prop.a;
}
}
class NegativeClassTest {
inThis() {
if ("a" in this.prop) {
let z = this.prop.b;
}
else {
let y = this.prop.a;
}
}
}
class UnreachableCodeDetection {
inThis() {
if ("a" in this) {
}
else {
let y = this.a;
}
}
}
function positiveIntersectionTest(x) {
if ("a" in x) {
let s = x.a;
}
else {
let n = x;
}
}
if ('extra' in error) {
error; // Still Error
}
else {
error; // Error
}
function narrowsToNever(x) {
let v;
if ("l" in x) {
v = x.l;
}
else if ("r" in x) {
v = x.r;
}
else {
v = x;
}
return v;
}
if (isAOrB(x)) {
if ("aProp" in x) {
x.aProp;
}
else if ("bProp" in x) {
x.bProp;
}
// x is never because of the type predicate from unknown
else if ("cProp" in x) {
const _never = x;
}
}
function negativeIntersectionTest() {
if ("ontouchstart" in window) {
window.ontouchstart;
}
else {
window.ontouchstart;
}
}
function f1(x) {
if ("a" in x) {
x.a;
}
if (x && "a" in x) {
x.a;
}
if (x && typeof x === "object" && "a" in x) {
x.a;
}
if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) {
x.a;
x.b;
x.c;
}
}
function f2(x) {
if ("a" in x) {
x.a;
}
if ("a" in x && "b" in x && "c" in x) {
x.a;
x.b;
x.c;
}
}
function f3(x) {
if ("a" in x) {
x.a;
}
if (x && "a" in x) {
x.a;
}
if (x && typeof x === "object" && "a" in x) {
x.a;
}
if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) {
x.a;
x.b;
x.c;
}
}
function f4(x) {
if ("a" in x) {
x.a;
}
if ("a" in x && "b" in x && "c" in x) {
x.a;
x.b;
x.c;
}
}
function f5(x) {
if ("a" in x) {
x; // { a: string }
}
else if ("b" in x) {
x; // { b: string }
}
else {
x; // never
}
}
function f6(x) {
if ("a" in x) {
x; // { a: string }
}
else if ("a" in x) {
x; // { b: string } & Record<"a", unknown>
}
else {
x; // { b: string }
}
}
// Object and corresponding intersection should narrow the same
function f7(x, y) {
if ("a" in x) {
x;
}
else {
x; // never
}
if ("a" in y) {
y;
}
else {
y; // never
}
}
const sym = Symbol();
function f8(x) {
if ("a" in x && 1 in x && sym in x) {
x.a;
x["a"];
x[1];
x["1"];
x[sym];
}
}
function f9(x) {
if ("a" in x && "1" in x && sym in x) {
x.a;
x["a"];
x[1];
x["1"];
x[sym];
}
}
// Repro from #50639
function foo(value) {
if (typeof value === "object" && value !== null && "prop" in value) {
value; // A & object & Record<"prop", unknown>
}
}

View File

@ -0,0 +1,727 @@
=== tests/cases/compiler/inKeywordTypeguard.ts ===
class A { a: string; }
>A : Symbol(A, Decl(inKeywordTypeguard.ts, 0, 0))
>a : Symbol(A.a, Decl(inKeywordTypeguard.ts, 0, 9))
class B { b: string; }
>B : Symbol(B, Decl(inKeywordTypeguard.ts, 0, 22))
>b : Symbol(B.b, Decl(inKeywordTypeguard.ts, 1, 9))
function negativeClassesTest(x: A | B) {
>negativeClassesTest : Symbol(negativeClassesTest, Decl(inKeywordTypeguard.ts, 1, 22))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 3, 29))
>A : Symbol(A, Decl(inKeywordTypeguard.ts, 0, 0))
>B : Symbol(B, Decl(inKeywordTypeguard.ts, 0, 22))
if ("a" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 3, 29))
x.b = "1";
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 3, 29))
} else {
x.a = "1";
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 3, 29))
}
}
function positiveClassesTest(x: A | B) {
>positiveClassesTest : Symbol(positiveClassesTest, Decl(inKeywordTypeguard.ts, 9, 1))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 11, 29))
>A : Symbol(A, Decl(inKeywordTypeguard.ts, 0, 0))
>B : Symbol(B, Decl(inKeywordTypeguard.ts, 0, 22))
if ("a" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 11, 29))
x.b = "1";
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 11, 29))
} else {
x.a = "1";
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 11, 29))
}
}
class AWithOptionalProp { a?: string; }
>AWithOptionalProp : Symbol(AWithOptionalProp, Decl(inKeywordTypeguard.ts, 17, 1))
>a : Symbol(AWithOptionalProp.a, Decl(inKeywordTypeguard.ts, 19, 25))
class BWithOptionalProp { b?: string; }
>BWithOptionalProp : Symbol(BWithOptionalProp, Decl(inKeywordTypeguard.ts, 19, 39))
>b : Symbol(BWithOptionalProp.b, Decl(inKeywordTypeguard.ts, 20, 25))
function positiveTestClassesWithOptionalProperties(x: AWithOptionalProp | BWithOptionalProp) {
>positiveTestClassesWithOptionalProperties : Symbol(positiveTestClassesWithOptionalProperties, Decl(inKeywordTypeguard.ts, 20, 39))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 22, 51))
>AWithOptionalProp : Symbol(AWithOptionalProp, Decl(inKeywordTypeguard.ts, 17, 1))
>BWithOptionalProp : Symbol(BWithOptionalProp, Decl(inKeywordTypeguard.ts, 19, 39))
if ("a" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 22, 51))
x.a = "1";
>x.a : Symbol(AWithOptionalProp.a, Decl(inKeywordTypeguard.ts, 19, 25))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 22, 51))
>a : Symbol(AWithOptionalProp.a, Decl(inKeywordTypeguard.ts, 19, 25))
} else {
x.b = "1";
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 22, 51))
}
}
class AWithMethod {
>AWithMethod : Symbol(AWithMethod, Decl(inKeywordTypeguard.ts, 28, 1))
a(): string { return ""; }
>a : Symbol(AWithMethod.a, Decl(inKeywordTypeguard.ts, 30, 19))
}
class BWithMethod {
>BWithMethod : Symbol(BWithMethod, Decl(inKeywordTypeguard.ts, 32, 1))
b(): string { return ""; }
>b : Symbol(BWithMethod.b, Decl(inKeywordTypeguard.ts, 34, 19))
}
function negativeTestClassesWithMembers(x: AWithMethod | BWithMethod) {
>negativeTestClassesWithMembers : Symbol(negativeTestClassesWithMembers, Decl(inKeywordTypeguard.ts, 36, 1))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 38, 40))
>AWithMethod : Symbol(AWithMethod, Decl(inKeywordTypeguard.ts, 28, 1))
>BWithMethod : Symbol(BWithMethod, Decl(inKeywordTypeguard.ts, 32, 1))
if ("a" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 38, 40))
x.a();
>x.a : Symbol(AWithMethod.a, Decl(inKeywordTypeguard.ts, 30, 19))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 38, 40))
>a : Symbol(AWithMethod.a, Decl(inKeywordTypeguard.ts, 30, 19))
x.b();
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 38, 40))
} else {
}
}
function negativeTestClassesWithMemberMissingInBothClasses(x: AWithMethod | BWithMethod) {
>negativeTestClassesWithMemberMissingInBothClasses : Symbol(negativeTestClassesWithMemberMissingInBothClasses, Decl(inKeywordTypeguard.ts, 44, 1))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 46, 59))
>AWithMethod : Symbol(AWithMethod, Decl(inKeywordTypeguard.ts, 28, 1))
>BWithMethod : Symbol(BWithMethod, Decl(inKeywordTypeguard.ts, 32, 1))
if ("c" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 46, 59))
x.a();
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 46, 59))
x.b();
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 46, 59))
} else {
x.a();
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 46, 59))
x.b();
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 46, 59))
}
}
class C { a: string; }
>C : Symbol(C, Decl(inKeywordTypeguard.ts, 54, 1))
>a : Symbol(C.a, Decl(inKeywordTypeguard.ts, 56, 9))
class D { a: string; }
>D : Symbol(D, Decl(inKeywordTypeguard.ts, 56, 22))
>a : Symbol(D.a, Decl(inKeywordTypeguard.ts, 57, 9))
function negativeMultipleClassesTest(x: A | B | C | D) {
>negativeMultipleClassesTest : Symbol(negativeMultipleClassesTest, Decl(inKeywordTypeguard.ts, 57, 22))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 59, 37))
>A : Symbol(A, Decl(inKeywordTypeguard.ts, 0, 0))
>B : Symbol(B, Decl(inKeywordTypeguard.ts, 0, 22))
>C : Symbol(C, Decl(inKeywordTypeguard.ts, 54, 1))
>D : Symbol(D, Decl(inKeywordTypeguard.ts, 56, 22))
if ("a" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 59, 37))
x.b = "1";
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 59, 37))
} else {
x.a = "1";
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 59, 37))
}
}
class ClassWithUnionProp { prop: A | B }
>ClassWithUnionProp : Symbol(ClassWithUnionProp, Decl(inKeywordTypeguard.ts, 65, 1))
>prop : Symbol(ClassWithUnionProp.prop, Decl(inKeywordTypeguard.ts, 67, 26))
>A : Symbol(A, Decl(inKeywordTypeguard.ts, 0, 0))
>B : Symbol(B, Decl(inKeywordTypeguard.ts, 0, 22))
function negativePropTest(x: ClassWithUnionProp) {
>negativePropTest : Symbol(negativePropTest, Decl(inKeywordTypeguard.ts, 67, 40))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 69, 26))
>ClassWithUnionProp : Symbol(ClassWithUnionProp, Decl(inKeywordTypeguard.ts, 65, 1))
if ("a" in x.prop) {
>x.prop : Symbol(ClassWithUnionProp.prop, Decl(inKeywordTypeguard.ts, 67, 26))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 69, 26))
>prop : Symbol(ClassWithUnionProp.prop, Decl(inKeywordTypeguard.ts, 67, 26))
let y: string = x.prop.b;
>y : Symbol(y, Decl(inKeywordTypeguard.ts, 71, 11))
>x.prop : Symbol(ClassWithUnionProp.prop, Decl(inKeywordTypeguard.ts, 67, 26))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 69, 26))
>prop : Symbol(ClassWithUnionProp.prop, Decl(inKeywordTypeguard.ts, 67, 26))
} else {
let z: string = x.prop.a;
>z : Symbol(z, Decl(inKeywordTypeguard.ts, 73, 11))
>x.prop : Symbol(ClassWithUnionProp.prop, Decl(inKeywordTypeguard.ts, 67, 26))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 69, 26))
>prop : Symbol(ClassWithUnionProp.prop, Decl(inKeywordTypeguard.ts, 67, 26))
}
}
class NegativeClassTest {
>NegativeClassTest : Symbol(NegativeClassTest, Decl(inKeywordTypeguard.ts, 75, 1))
protected prop: A | B;
>prop : Symbol(NegativeClassTest.prop, Decl(inKeywordTypeguard.ts, 77, 25))
>A : Symbol(A, Decl(inKeywordTypeguard.ts, 0, 0))
>B : Symbol(B, Decl(inKeywordTypeguard.ts, 0, 22))
inThis() {
>inThis : Symbol(NegativeClassTest.inThis, Decl(inKeywordTypeguard.ts, 78, 26))
if ("a" in this.prop) {
>this.prop : Symbol(NegativeClassTest.prop, Decl(inKeywordTypeguard.ts, 77, 25))
>this : Symbol(NegativeClassTest, Decl(inKeywordTypeguard.ts, 75, 1))
>prop : Symbol(NegativeClassTest.prop, Decl(inKeywordTypeguard.ts, 77, 25))
let z: number = this.prop.b;
>z : Symbol(z, Decl(inKeywordTypeguard.ts, 81, 15))
>this.prop : Symbol(NegativeClassTest.prop, Decl(inKeywordTypeguard.ts, 77, 25))
>this : Symbol(NegativeClassTest, Decl(inKeywordTypeguard.ts, 75, 1))
>prop : Symbol(NegativeClassTest.prop, Decl(inKeywordTypeguard.ts, 77, 25))
} else {
let y: string = this.prop.a;
>y : Symbol(y, Decl(inKeywordTypeguard.ts, 83, 15))
>this.prop : Symbol(NegativeClassTest.prop, Decl(inKeywordTypeguard.ts, 77, 25))
>this : Symbol(NegativeClassTest, Decl(inKeywordTypeguard.ts, 75, 1))
>prop : Symbol(NegativeClassTest.prop, Decl(inKeywordTypeguard.ts, 77, 25))
}
}
}
class UnreachableCodeDetection {
>UnreachableCodeDetection : Symbol(UnreachableCodeDetection, Decl(inKeywordTypeguard.ts, 86, 1))
a: string;
>a : Symbol(UnreachableCodeDetection.a, Decl(inKeywordTypeguard.ts, 88, 32))
inThis() {
>inThis : Symbol(UnreachableCodeDetection.inThis, Decl(inKeywordTypeguard.ts, 89, 14))
if ("a" in this) {
>this : Symbol(UnreachableCodeDetection, Decl(inKeywordTypeguard.ts, 86, 1))
} else {
let y = this.a;
>y : Symbol(y, Decl(inKeywordTypeguard.ts, 93, 15))
}
}
}
function positiveIntersectionTest(x: { a: string } & { b: string }) {
>positiveIntersectionTest : Symbol(positiveIntersectionTest, Decl(inKeywordTypeguard.ts, 96, 1))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 98, 34))
>a : Symbol(a, Decl(inKeywordTypeguard.ts, 98, 38))
>b : Symbol(b, Decl(inKeywordTypeguard.ts, 98, 54))
if ("a" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 98, 34))
let s: string = x.a;
>s : Symbol(s, Decl(inKeywordTypeguard.ts, 100, 11))
>x.a : Symbol(a, Decl(inKeywordTypeguard.ts, 98, 38))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 98, 34))
>a : Symbol(a, Decl(inKeywordTypeguard.ts, 98, 38))
} else {
let n: never = x;
>n : Symbol(n, Decl(inKeywordTypeguard.ts, 102, 11))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 98, 34))
}
}
// Repro from #38608
declare const error: Error;
>error : Symbol(error, Decl(inKeywordTypeguard.ts, 107, 13))
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
if ('extra' in error) {
>error : Symbol(error, Decl(inKeywordTypeguard.ts, 107, 13))
error // Still Error
>error : Symbol(error, Decl(inKeywordTypeguard.ts, 107, 13))
} else {
error // Error
>error : Symbol(error, Decl(inKeywordTypeguard.ts, 107, 13))
}
function narrowsToNever(x: { l: number } | { r: number }) {
>narrowsToNever : Symbol(narrowsToNever, Decl(inKeywordTypeguard.ts, 112, 1))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 114, 24))
>l : Symbol(l, Decl(inKeywordTypeguard.ts, 114, 28))
>r : Symbol(r, Decl(inKeywordTypeguard.ts, 114, 44))
let v: number;
>v : Symbol(v, Decl(inKeywordTypeguard.ts, 115, 7))
if ("l" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 114, 24))
v = x.l;
>v : Symbol(v, Decl(inKeywordTypeguard.ts, 115, 7))
>x.l : Symbol(l, Decl(inKeywordTypeguard.ts, 114, 28))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 114, 24))
>l : Symbol(l, Decl(inKeywordTypeguard.ts, 114, 28))
}
else if ("r" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 114, 24))
v = x.r;
>v : Symbol(v, Decl(inKeywordTypeguard.ts, 115, 7))
>x.r : Symbol(r, Decl(inKeywordTypeguard.ts, 114, 44))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 114, 24))
>r : Symbol(r, Decl(inKeywordTypeguard.ts, 114, 44))
}
else {
v = x
>v : Symbol(v, Decl(inKeywordTypeguard.ts, 115, 7))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 114, 24))
}
return v;
>v : Symbol(v, Decl(inKeywordTypeguard.ts, 115, 7))
}
type AOrB = { aProp: number } | { bProp: number };
>AOrB : Symbol(AOrB, Decl(inKeywordTypeguard.ts, 126, 1))
>aProp : Symbol(aProp, Decl(inKeywordTypeguard.ts, 128, 13))
>bProp : Symbol(bProp, Decl(inKeywordTypeguard.ts, 128, 33))
declare function isAOrB(x: unknown): x is AOrB;
>isAOrB : Symbol(isAOrB, Decl(inKeywordTypeguard.ts, 128, 50))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 129, 24))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 129, 24))
>AOrB : Symbol(AOrB, Decl(inKeywordTypeguard.ts, 126, 1))
declare var x: unknown;
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 131, 11))
if (isAOrB(x)) {
>isAOrB : Symbol(isAOrB, Decl(inKeywordTypeguard.ts, 128, 50))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 131, 11))
if ("aProp" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 131, 11))
x.aProp;
>x.aProp : Symbol(aProp, Decl(inKeywordTypeguard.ts, 128, 13))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 131, 11))
>aProp : Symbol(aProp, Decl(inKeywordTypeguard.ts, 128, 13))
}
else if ("bProp" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 131, 11))
x.bProp;
>x.bProp : Symbol(bProp, Decl(inKeywordTypeguard.ts, 128, 33))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 131, 11))
>bProp : Symbol(bProp, Decl(inKeywordTypeguard.ts, 128, 33))
}
// x is never because of the type predicate from unknown
else if ("cProp" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 131, 11))
const _never: never = x;
>_never : Symbol(_never, Decl(inKeywordTypeguard.ts, 141, 13))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 131, 11))
}
}
function negativeIntersectionTest() {
>negativeIntersectionTest : Symbol(negativeIntersectionTest, Decl(inKeywordTypeguard.ts, 143, 1))
if ("ontouchstart" in window) {
>window : Symbol(window, Decl(lib.dom.d.ts, --, --))
window.ontouchstart
>window.ontouchstart : Symbol(ontouchstart, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))
>window : Symbol(window, Decl(lib.dom.d.ts, --, --))
>ontouchstart : Symbol(ontouchstart, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))
} else {
window.ontouchstart
>window : Symbol(window, Decl(lib.dom.d.ts, --, --))
}
}
function f1(x: unknown) {
>f1 : Symbol(f1, Decl(inKeywordTypeguard.ts, 151, 1))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
if ("a" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
x.a;
>x.a : Symbol(a)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
>a : Symbol(a)
}
if (x && "a" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
x.a;
>x.a : Symbol(a)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
>a : Symbol(a)
}
if (x && typeof x === "object" && "a" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
x.a;
>x.a : Symbol(a)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
>a : Symbol(a)
}
if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
x.a;
>x.a : Symbol(a)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
>a : Symbol(a)
x.b;
>x.b : Symbol(b)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
>b : Symbol(b)
x.c;
>x.c : Symbol(c)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 153, 12))
>c : Symbol(c)
}
}
function f2(x: object) {
>f2 : Symbol(f2, Decl(inKeywordTypeguard.ts, 168, 1))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12))
if ("a" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12))
x.a;
>x.a : Symbol(a)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12))
>a : Symbol(a)
}
if ("a" in x && "b" in x && "c" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12))
x.a;
>x.a : Symbol(a)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12))
>a : Symbol(a)
x.b;
>x.b : Symbol(b)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12))
>b : Symbol(b)
x.c;
>x.c : Symbol(c)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 170, 12))
>c : Symbol(c)
}
}
function f3<T>(x: T) {
>f3 : Symbol(f3, Decl(inKeywordTypeguard.ts, 179, 1))
>T : Symbol(T, Decl(inKeywordTypeguard.ts, 181, 12))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
>T : Symbol(T, Decl(inKeywordTypeguard.ts, 181, 12))
if ("a" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
x.a;
>x.a : Symbol(a)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
>a : Symbol(a)
}
if (x && "a" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
x.a;
>x.a : Symbol(a)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
>a : Symbol(a)
}
if (x && typeof x === "object" && "a" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
x.a;
>x.a : Symbol(a)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
>a : Symbol(a)
}
if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
x.a;
>x.a : Symbol(a)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
>a : Symbol(a)
x.b;
>x.b : Symbol(b)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
>b : Symbol(b)
x.c;
>x.c : Symbol(c)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 181, 15))
>c : Symbol(c)
}
}
function f4(x: { a: string }) {
>f4 : Symbol(f4, Decl(inKeywordTypeguard.ts, 196, 1))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12))
>a : Symbol(a, Decl(inKeywordTypeguard.ts, 198, 16))
if ("a" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12))
x.a;
>x.a : Symbol(a, Decl(inKeywordTypeguard.ts, 198, 16))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12))
>a : Symbol(a, Decl(inKeywordTypeguard.ts, 198, 16))
}
if ("a" in x && "b" in x && "c" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12))
x.a;
>x.a : Symbol(a, Decl(inKeywordTypeguard.ts, 198, 16))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12))
>a : Symbol(a, Decl(inKeywordTypeguard.ts, 198, 16))
x.b;
>x.b : Symbol(b)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12))
>b : Symbol(b)
x.c;
>x.c : Symbol(c)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 198, 12))
>c : Symbol(c)
}
}
function f5(x: { a: string } | { b: string }) {
>f5 : Symbol(f5, Decl(inKeywordTypeguard.ts, 207, 1))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 209, 12))
>a : Symbol(a, Decl(inKeywordTypeguard.ts, 209, 16))
>b : Symbol(b, Decl(inKeywordTypeguard.ts, 209, 32))
if ("a" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 209, 12))
x; // { a: string }
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 209, 12))
}
else if ("b" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 209, 12))
x; // { b: string }
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 209, 12))
}
else {
x; // never
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 209, 12))
}
}
function f6(x: { a: string } | { b: string }) {
>f6 : Symbol(f6, Decl(inKeywordTypeguard.ts, 219, 1))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 221, 12))
>a : Symbol(a, Decl(inKeywordTypeguard.ts, 221, 16))
>b : Symbol(b, Decl(inKeywordTypeguard.ts, 221, 32))
if ("a" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 221, 12))
x; // { a: string }
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 221, 12))
}
else if ("a" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 221, 12))
x; // { b: string } & Record<"a", unknown>
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 221, 12))
}
else {
x; // { b: string }
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 221, 12))
}
}
// Object and corresponding intersection should narrow the same
function f7(x: { a: string, b: number }, y: { a: string } & { b: number }) {
>f7 : Symbol(f7, Decl(inKeywordTypeguard.ts, 231, 1))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 235, 12))
>a : Symbol(a, Decl(inKeywordTypeguard.ts, 235, 16))
>b : Symbol(b, Decl(inKeywordTypeguard.ts, 235, 27))
>y : Symbol(y, Decl(inKeywordTypeguard.ts, 235, 40))
>a : Symbol(a, Decl(inKeywordTypeguard.ts, 235, 45))
>b : Symbol(b, Decl(inKeywordTypeguard.ts, 235, 61))
if ("a" in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 235, 12))
x;
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 235, 12))
}
else {
x; // never
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 235, 12))
}
if ("a" in y) {
>y : Symbol(y, Decl(inKeywordTypeguard.ts, 235, 40))
y;
>y : Symbol(y, Decl(inKeywordTypeguard.ts, 235, 40))
}
else {
y; // never
>y : Symbol(y, Decl(inKeywordTypeguard.ts, 235, 40))
}
}
const sym = Symbol();
>sym : Symbol(sym, Decl(inKeywordTypeguard.ts, 250, 5))
>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
function f8(x: object) {
>f8 : Symbol(f8, Decl(inKeywordTypeguard.ts, 250, 21))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12))
if ("a" in x && 1 in x && sym in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12))
>sym : Symbol(sym, Decl(inKeywordTypeguard.ts, 250, 5))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12))
x.a;
>x.a : Symbol(a)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12))
>a : Symbol(a)
x["a"];
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12))
>"a" : Symbol(a)
x[1];
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12))
>1 : Symbol(1)
x["1"];
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12))
>"1" : Symbol(1)
x[sym];
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 252, 12))
>sym : Symbol(sym, Decl(inKeywordTypeguard.ts, 250, 5))
}
}
function f9(x: object) {
>f9 : Symbol(f9, Decl(inKeywordTypeguard.ts, 260, 1))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12))
if ("a" in x && "1" in x && sym in x) {
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12))
>sym : Symbol(sym, Decl(inKeywordTypeguard.ts, 250, 5))
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12))
x.a;
>x.a : Symbol(a)
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12))
>a : Symbol(a)
x["a"];
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12))
>"a" : Symbol(a)
x[1];
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12))
>1 : Symbol(1)
x["1"];
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12))
>"1" : Symbol(1)
x[sym];
>x : Symbol(x, Decl(inKeywordTypeguard.ts, 262, 12))
>sym : Symbol(sym, Decl(inKeywordTypeguard.ts, 250, 5))
}
}
// Repro from #50639
function foo<A>(value: A) {
>foo : Symbol(foo, Decl(inKeywordTypeguard.ts, 270, 1))
>A : Symbol(A, Decl(inKeywordTypeguard.ts, 274, 13))
>value : Symbol(value, Decl(inKeywordTypeguard.ts, 274, 16))
>A : Symbol(A, Decl(inKeywordTypeguard.ts, 274, 13))
if (typeof value === "object" && value !== null && "prop" in value) {
>value : Symbol(value, Decl(inKeywordTypeguard.ts, 274, 16))
>value : Symbol(value, Decl(inKeywordTypeguard.ts, 274, 16))
>value : Symbol(value, Decl(inKeywordTypeguard.ts, 274, 16))
value; // A & object & Record<"prop", unknown>
>value : Symbol(value, Decl(inKeywordTypeguard.ts, 274, 16))
}
}

View File

@ -0,0 +1,914 @@
=== tests/cases/compiler/inKeywordTypeguard.ts ===
class A { a: string; }
>A : A
>a : string
class B { b: string; }
>B : B
>b : string
function negativeClassesTest(x: A | B) {
>negativeClassesTest : (x: A | B) => void
>x : A | B
if ("a" in x) {
>"a" in x : boolean
>"a" : "a"
>x : A | B
x.b = "1";
>x.b = "1" : "1"
>x.b : any
>x : A
>b : any
>"1" : "1"
} else {
x.a = "1";
>x.a = "1" : "1"
>x.a : any
>x : B
>a : any
>"1" : "1"
}
}
function positiveClassesTest(x: A | B) {
>positiveClassesTest : (x: A | B) => void
>x : A | B
if ("a" in x) {
>"a" in x : boolean
>"a" : "a"
>x : A | B
x.b = "1";
>x.b = "1" : "1"
>x.b : any
>x : A
>b : any
>"1" : "1"
} else {
x.a = "1";
>x.a = "1" : "1"
>x.a : any
>x : B
>a : any
>"1" : "1"
}
}
class AWithOptionalProp { a?: string; }
>AWithOptionalProp : AWithOptionalProp
>a : string | undefined
class BWithOptionalProp { b?: string; }
>BWithOptionalProp : BWithOptionalProp
>b : string | undefined
function positiveTestClassesWithOptionalProperties(x: AWithOptionalProp | BWithOptionalProp) {
>positiveTestClassesWithOptionalProperties : (x: AWithOptionalProp | BWithOptionalProp) => void
>x : AWithOptionalProp | BWithOptionalProp
if ("a" in x) {
>"a" in x : boolean
>"a" : "a"
>x : AWithOptionalProp | BWithOptionalProp
x.a = "1";
>x.a = "1" : "1"
>x.a : string | undefined
>x : AWithOptionalProp
>a : string | undefined
>"1" : "1"
} else {
x.b = "1";
>x.b = "1" : "1"
>x.b : any
>x : AWithOptionalProp | BWithOptionalProp
>b : any
>"1" : "1"
}
}
class AWithMethod {
>AWithMethod : AWithMethod
a(): string { return ""; }
>a : () => string
>"" : ""
}
class BWithMethod {
>BWithMethod : BWithMethod
b(): string { return ""; }
>b : () => string
>"" : ""
}
function negativeTestClassesWithMembers(x: AWithMethod | BWithMethod) {
>negativeTestClassesWithMembers : (x: AWithMethod | BWithMethod) => void
>x : AWithMethod | BWithMethod
if ("a" in x) {
>"a" in x : boolean
>"a" : "a"
>x : AWithMethod | BWithMethod
x.a();
>x.a() : string
>x.a : () => string
>x : AWithMethod
>a : () => string
x.b();
>x.b() : any
>x.b : any
>x : AWithMethod
>b : any
} else {
}
}
function negativeTestClassesWithMemberMissingInBothClasses(x: AWithMethod | BWithMethod) {
>negativeTestClassesWithMemberMissingInBothClasses : (x: AWithMethod | BWithMethod) => void
>x : AWithMethod | BWithMethod
if ("c" in x) {
>"c" in x : boolean
>"c" : "c"
>x : AWithMethod | BWithMethod
x.a();
>x.a() : any
>x.a : any
>x : (AWithMethod | BWithMethod) & Record<"c", unknown>
>a : any
x.b();
>x.b() : any
>x.b : any
>x : (AWithMethod | BWithMethod) & Record<"c", unknown>
>b : any
} else {
x.a();
>x.a() : any
>x.a : any
>x : AWithMethod | BWithMethod
>a : any
x.b();
>x.b() : any
>x.b : any
>x : AWithMethod | BWithMethod
>b : any
}
}
class C { a: string; }
>C : C
>a : string
class D { a: string; }
>D : D
>a : string
function negativeMultipleClassesTest(x: A | B | C | D) {
>negativeMultipleClassesTest : (x: A | B | C | D) => void
>x : A | B | C | D
if ("a" in x) {
>"a" in x : boolean
>"a" : "a"
>x : A | B | C | D
x.b = "1";
>x.b = "1" : "1"
>x.b : any
>x : A | C | D
>b : any
>"1" : "1"
} else {
x.a = "1";
>x.a = "1" : "1"
>x.a : any
>x : B
>a : any
>"1" : "1"
}
}
class ClassWithUnionProp { prop: A | B }
>ClassWithUnionProp : ClassWithUnionProp
>prop : A | B
function negativePropTest(x: ClassWithUnionProp) {
>negativePropTest : (x: ClassWithUnionProp) => void
>x : ClassWithUnionProp
if ("a" in x.prop) {
>"a" in x.prop : boolean
>"a" : "a"
>x.prop : A | B
>x : ClassWithUnionProp
>prop : A | B
let y: string = x.prop.b;
>y : string
>x.prop.b : any
>x.prop : A
>x : ClassWithUnionProp
>prop : A
>b : any
} else {
let z: string = x.prop.a;
>z : string
>x.prop.a : any
>x.prop : B
>x : ClassWithUnionProp
>prop : B
>a : any
}
}
class NegativeClassTest {
>NegativeClassTest : NegativeClassTest
protected prop: A | B;
>prop : A | B
inThis() {
>inThis : () => void
if ("a" in this.prop) {
>"a" in this.prop : boolean
>"a" : "a"
>this.prop : A | B
>this : this
>prop : A | B
let z: number = this.prop.b;
>z : number
>this.prop.b : any
>this.prop : A
>this : this
>prop : A
>b : any
} else {
let y: string = this.prop.a;
>y : string
>this.prop.a : any
>this.prop : B
>this : this
>prop : B
>a : any
}
}
}
class UnreachableCodeDetection {
>UnreachableCodeDetection : UnreachableCodeDetection
a: string;
>a : string
inThis() {
>inThis : () => void
if ("a" in this) {
>"a" in this : boolean
>"a" : "a"
>this : this
} else {
let y = this.a;
>y : any
>this.a : any
>this : never
>a : any
}
}
}
function positiveIntersectionTest(x: { a: string } & { b: string }) {
>positiveIntersectionTest : (x: { a: string;} & { b: string;}) => void
>x : { a: string; } & { b: string; }
>a : string
>b : string
if ("a" in x) {
>"a" in x : boolean
>"a" : "a"
>x : { a: string; } & { b: string; }
let s: string = x.a;
>s : string
>x.a : string
>x : { a: string; } & { b: string; }
>a : string
} else {
let n: never = x;
>n : never
>x : never
}
}
// Repro from #38608
declare const error: Error;
>error : Error
if ('extra' in error) {
>'extra' in error : boolean
>'extra' : "extra"
>error : Error
error // Still Error
>error : Error & Record<"extra", unknown>
} else {
error // Error
>error : Error
}
function narrowsToNever(x: { l: number } | { r: number }) {
>narrowsToNever : (x: { l: number;} | { r: number;}) => number
>x : { l: number; } | { r: number; }
>l : number
>r : number
let v: number;
>v : number
if ("l" in x) {
>"l" in x : boolean
>"l" : "l"
>x : { l: number; } | { r: number; }
v = x.l;
>v = x.l : number
>v : number
>x.l : number
>x : { l: number; }
>l : number
}
else if ("r" in x) {
>"r" in x : boolean
>"r" : "r"
>x : { r: number; }
v = x.r;
>v = x.r : number
>v : number
>x.r : number
>x : { r: number; }
>r : number
}
else {
v = x
>v = x : never
>v : number
>x : never
}
return v;
>v : number
}
type AOrB = { aProp: number } | { bProp: number };
>AOrB : { aProp: number; } | { bProp: number; }
>aProp : number
>bProp : number
declare function isAOrB(x: unknown): x is AOrB;
>isAOrB : (x: unknown) => x is AOrB
>x : unknown
declare var x: unknown;
>x : unknown
if (isAOrB(x)) {
>isAOrB(x) : boolean
>isAOrB : (x: unknown) => x is AOrB
>x : unknown
if ("aProp" in x) {
>"aProp" in x : boolean
>"aProp" : "aProp"
>x : AOrB
x.aProp;
>x.aProp : number
>x : { aProp: number; }
>aProp : number
}
else if ("bProp" in x) {
>"bProp" in x : boolean
>"bProp" : "bProp"
>x : { bProp: number; }
x.bProp;
>x.bProp : number
>x : { bProp: number; }
>bProp : number
}
// x is never because of the type predicate from unknown
else if ("cProp" in x) {
>"cProp" in x : boolean
>"cProp" : "cProp"
>x : never
const _never: never = x;
>_never : never
>x : never
}
}
function negativeIntersectionTest() {
>negativeIntersectionTest : () => void
if ("ontouchstart" in window) {
>"ontouchstart" in window : boolean
>"ontouchstart" : "ontouchstart"
>window : Window & typeof globalThis
window.ontouchstart
>window.ontouchstart : (((this: GlobalEventHandlers, ev: TouchEvent) => any) & ((this: Window, ev: TouchEvent) => any)) | null | undefined
>window : Window & typeof globalThis
>ontouchstart : (((this: GlobalEventHandlers, ev: TouchEvent) => any) & ((this: Window, ev: TouchEvent) => any)) | null | undefined
} else {
window.ontouchstart
>window.ontouchstart : any
>window : never
>ontouchstart : any
}
}
function f1(x: unknown) {
>f1 : (x: unknown) => void
>x : unknown
if ("a" in x) {
>"a" in x : boolean
>"a" : "a"
>x : unknown
x.a;
>x.a : unknown
>x : Record<"a", unknown>
>a : unknown
}
if (x && "a" in x) {
>x && "a" in x : unknown
>x : unknown
>"a" in x : boolean
>"a" : "a"
>x : {}
x.a;
>x.a : unknown
>x : Record<"a", unknown>
>a : unknown
}
if (x && typeof x === "object" && "a" in x) {
>x && typeof x === "object" && "a" in x : unknown
>x && typeof x === "object" : unknown
>x : unknown
>typeof x === "object" : boolean
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : {}
>"object" : "object"
>"a" in x : boolean
>"a" : "a"
>x : object
x.a;
>x.a : unknown
>x : object & Record<"a", unknown>
>a : unknown
}
if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) {
>x && typeof x === "object" && "a" in x && "b" in x && "c" in x : unknown
>x && typeof x === "object" && "a" in x && "b" in x : unknown
>x && typeof x === "object" && "a" in x : unknown
>x && typeof x === "object" : unknown
>x : unknown
>typeof x === "object" : boolean
>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function"
>x : {}
>"object" : "object"
>"a" in x : boolean
>"a" : "a"
>x : object
>"b" in x : boolean
>"b" : "b"
>x : object & Record<"a", unknown>
>"c" in x : boolean
>"c" : "c"
>x : object & Record<"a", unknown> & Record<"b", unknown>
x.a;
>x.a : unknown
>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown>
>a : unknown
x.b;
>x.b : unknown
>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown>
>b : unknown
x.c;
>x.c : unknown
>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown>
>c : unknown
}
}
function f2(x: object) {
>f2 : (x: object) => void
>x : object
if ("a" in x) {
>"a" in x : boolean
>"a" : "a"
>x : object
x.a;
>x.a : unknown
>x : object & Record<"a", unknown>
>a : unknown
}
if ("a" in x && "b" in x && "c" in x) {
>"a" in x && "b" in x && "c" in x : boolean
>"a" in x && "b" in x : boolean
>"a" in x : boolean
>"a" : "a"
>x : object
>"b" in x : boolean
>"b" : "b"
>x : object & Record<"a", unknown>
>"c" in x : boolean
>"c" : "c"
>x : object & Record<"a", unknown> & Record<"b", unknown>
x.a;
>x.a : unknown
>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown>
>a : unknown
x.b;
>x.b : unknown
>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown>
>b : unknown
x.c;
>x.c : unknown
>x : object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown>
>c : unknown
}
}
function f3<T>(x: T) {
>f3 : <T>(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<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 : NonNullable<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 : NonNullable<T>
>"object" : "object"
>"a" in x : boolean
>"a" : "a"
>x : T & object
>"b" in x : boolean
>"b" : "b"
>x : T & object & Record<"a", unknown>
>"c" in x : boolean
>"c" : "c"
>x : T & object & Record<"a", unknown> & Record<"b", unknown>
x.a;
>x.a : unknown
>x : T & object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown>
>a : unknown
x.b;
>x.b : unknown
>x : T & object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown>
>b : unknown
x.c;
>x.c : unknown
>x : T & object & Record<"a", unknown> & Record<"b", unknown> & Record<"c", unknown>
>c : unknown
}
}
function f4(x: { a: string }) {
>f4 : (x: { a: string;}) => void
>x : { a: string; }
>a : string
if ("a" in x) {
>"a" in x : boolean
>"a" : "a"
>x : { a: string; }
x.a;
>x.a : string
>x : { a: string; }
>a : string
}
if ("a" in x && "b" in x && "c" in x) {
>"a" in x && "b" in x && "c" in x : boolean
>"a" in x && "b" in x : boolean
>"a" in x : boolean
>"a" : "a"
>x : { a: string; }
>"b" in x : boolean
>"b" : "b"
>x : { a: string; }
>"c" in x : boolean
>"c" : "c"
>x : { a: string; } & Record<"b", unknown>
x.a;
>x.a : string
>x : { a: string; } & Record<"b", unknown> & Record<"c", unknown>
>a : string
x.b;
>x.b : unknown
>x : { a: string; } & Record<"b", unknown> & Record<"c", unknown>
>b : unknown
x.c;
>x.c : unknown
>x : { a: string; } & Record<"b", unknown> & Record<"c", unknown>
>c : unknown
}
}
function f5(x: { a: string } | { b: string }) {
>f5 : (x: { a: string;} | { b: string;}) => void
>x : { a: string; } | { b: string; }
>a : string
>b : string
if ("a" in x) {
>"a" in x : boolean
>"a" : "a"
>x : { a: string; } | { b: string; }
x; // { a: string }
>x : { a: string; }
}
else if ("b" in x) {
>"b" in x : boolean
>"b" : "b"
>x : { b: string; }
x; // { b: string }
>x : { b: string; }
}
else {
x; // never
>x : never
}
}
function f6(x: { a: string } | { b: string }) {
>f6 : (x: { a: string;} | { b: string;}) => void
>x : { a: string; } | { b: string; }
>a : string
>b : string
if ("a" in x) {
>"a" in x : boolean
>"a" : "a"
>x : { a: string; } | { b: string; }
x; // { a: string }
>x : { a: string; }
}
else if ("a" in x) {
>"a" in x : boolean
>"a" : "a"
>x : { b: string; }
x; // { b: string } & Record<"a", unknown>
>x : { b: string; } & Record<"a", unknown>
}
else {
x; // { b: string }
>x : { b: string; }
}
}
// Object and corresponding intersection should narrow the same
function f7(x: { a: string, b: number }, y: { a: string } & { b: number }) {
>f7 : (x: { a: string; b: number;}, y: { a: string;} & { b: number;}) => void
>x : { a: string; b: number; }
>a : string
>b : number
>y : { a: string; } & { b: number; }
>a : string
>b : number
if ("a" in x) {
>"a" in x : boolean
>"a" : "a"
>x : { a: string; b: number; }
x;
>x : { a: string; b: number; }
}
else {
x; // never
>x : never
}
if ("a" in y) {
>"a" in y : boolean
>"a" : "a"
>y : { a: string; } & { b: number; }
y;
>y : { a: string; } & { b: number; }
}
else {
y; // never
>y : never
}
}
const sym = Symbol();
>sym : unique symbol
>Symbol() : unique symbol
>Symbol : SymbolConstructor
function f8(x: object) {
>f8 : (x: object) => void
>x : object
if ("a" in x && 1 in x && sym in x) {
>"a" in x && 1 in x && sym in x : boolean
>"a" in x && 1 in x : boolean
>"a" in x : boolean
>"a" : "a"
>x : object
>1 in x : boolean
>1 : 1
>x : object & Record<"a", unknown>
>sym in x : boolean
>sym : unique symbol
>x : object & Record<"a", unknown> & Record<1, unknown>
x.a;
>x.a : unknown
>x : object & Record<"a", unknown> & Record<1, unknown> & Record<unique symbol, unknown>
>a : unknown
x["a"];
>x["a"] : unknown
>x : object & Record<"a", unknown> & Record<1, unknown> & Record<unique symbol, unknown>
>"a" : "a"
x[1];
>x[1] : unknown
>x : object & Record<"a", unknown> & Record<1, unknown> & Record<unique symbol, unknown>
>1 : 1
x["1"];
>x["1"] : unknown
>x : object & Record<"a", unknown> & Record<1, unknown> & Record<unique symbol, unknown>
>"1" : "1"
x[sym];
>x[sym] : unknown
>x : object & Record<"a", unknown> & Record<1, unknown> & Record<unique symbol, unknown>
>sym : unique symbol
}
}
function f9(x: object) {
>f9 : (x: object) => void
>x : object
if ("a" in x && "1" in x && sym in x) {
>"a" in x && "1" in x && sym in x : boolean
>"a" in x && "1" in x : boolean
>"a" in x : boolean
>"a" : "a"
>x : object
>"1" in x : boolean
>"1" : "1"
>x : object & Record<"a", unknown>
>sym in x : boolean
>sym : unique symbol
>x : object & Record<"a", unknown> & Record<"1", unknown>
x.a;
>x.a : unknown
>x : object & Record<"a", unknown> & Record<"1", unknown> & Record<unique symbol, unknown>
>a : unknown
x["a"];
>x["a"] : unknown
>x : object & Record<"a", unknown> & Record<"1", unknown> & Record<unique symbol, unknown>
>"a" : "a"
x[1];
>x[1] : unknown
>x : object & Record<"a", unknown> & Record<"1", unknown> & Record<unique symbol, unknown>
>1 : 1
x["1"];
>x["1"] : unknown
>x : object & Record<"a", unknown> & Record<"1", unknown> & Record<unique symbol, unknown>
>"1" : "1"
x[sym];
>x[sym] : unknown
>x : object & Record<"a", unknown> & Record<"1", unknown> & Record<unique symbol, unknown>
>sym : unique symbol
}
}
// Repro from #50639
function foo<A>(value: A) {
>foo : <A>(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>
}
}

View File

@ -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;
}
}

View File

@ -1,454 +0,0 @@
=== tests/cases/compiler/inKeywordTypeguard.ts ===
class A { a: string; }
>A : A
>a : string
class B { b: string; }
>B : B
>b : string
function negativeClassesTest(x: A | B) {
>negativeClassesTest : (x: A | B) => void
>x : A | B
if ("a" in x) {
>"a" in x : boolean
>"a" : "a"
>x : A | B
x.b = "1";
>x.b = "1" : "1"
>x.b : any
>x : A
>b : any
>"1" : "1"
} else {
x.a = "1";
>x.a = "1" : "1"
>x.a : any
>x : B
>a : any
>"1" : "1"
}
}
function positiveClassesTest(x: A | B) {
>positiveClassesTest : (x: A | B) => void
>x : A | B
if ("a" in x) {
>"a" in x : boolean
>"a" : "a"
>x : A | B
x.b = "1";
>x.b = "1" : "1"
>x.b : any
>x : A
>b : any
>"1" : "1"
} else {
x.a = "1";
>x.a = "1" : "1"
>x.a : any
>x : B
>a : any
>"1" : "1"
}
}
class AWithOptionalProp { a?: string; }
>AWithOptionalProp : AWithOptionalProp
>a : string
class BWithOptionalProp { b?: string; }
>BWithOptionalProp : BWithOptionalProp
>b : string
function positiveTestClassesWithOptionalProperties(x: AWithOptionalProp | BWithOptionalProp) {
>positiveTestClassesWithOptionalProperties : (x: AWithOptionalProp | BWithOptionalProp) => void
>x : AWithOptionalProp | BWithOptionalProp
if ("a" in x) {
>"a" in x : boolean
>"a" : "a"
>x : AWithOptionalProp | BWithOptionalProp
x.a = "1";
>x.a = "1" : "1"
>x.a : string
>x : AWithOptionalProp
>a : string
>"1" : "1"
} else {
x.b = "1";
>x.b = "1" : "1"
>x.b : any
>x : AWithOptionalProp | BWithOptionalProp
>b : any
>"1" : "1"
}
}
class AWithMethod {
>AWithMethod : AWithMethod
a(): string { return ""; }
>a : () => string
>"" : ""
}
class BWithMethod {
>BWithMethod : BWithMethod
b(): string { return ""; }
>b : () => string
>"" : ""
}
function negativeTestClassesWithMembers(x: AWithMethod | BWithMethod) {
>negativeTestClassesWithMembers : (x: AWithMethod | BWithMethod) => void
>x : AWithMethod | BWithMethod
if ("a" in x) {
>"a" in x : boolean
>"a" : "a"
>x : AWithMethod | BWithMethod
x.a();
>x.a() : string
>x.a : () => string
>x : AWithMethod
>a : () => string
x.b();
>x.b() : any
>x.b : any
>x : AWithMethod
>b : any
} else {
}
}
function negativeTestClassesWithMemberMissingInBothClasses(x: AWithMethod | BWithMethod) {
>negativeTestClassesWithMemberMissingInBothClasses : (x: AWithMethod | BWithMethod) => void
>x : AWithMethod | BWithMethod
if ("c" in x) {
>"c" in x : boolean
>"c" : "c"
>x : AWithMethod | BWithMethod
x.a();
>x.a() : any
>x.a : any
>x : never
>a : any
x.b();
>x.b() : any
>x.b : any
>x : never
>b : any
} else {
x.a();
>x.a() : any
>x.a : any
>x : AWithMethod | BWithMethod
>a : any
x.b();
>x.b() : any
>x.b : any
>x : AWithMethod | BWithMethod
>b : any
}
}
class C { a: string; }
>C : C
>a : string
class D { a: string; }
>D : D
>a : string
function negativeMultipleClassesTest(x: A | B | C | D) {
>negativeMultipleClassesTest : (x: A | B | C | D) => void
>x : A | B | C | D
if ("a" in x) {
>"a" in x : boolean
>"a" : "a"
>x : A | B | C | D
x.b = "1";
>x.b = "1" : "1"
>x.b : any
>x : A | C | D
>b : any
>"1" : "1"
} else {
x.a = "1";
>x.a = "1" : "1"
>x.a : any
>x : B
>a : any
>"1" : "1"
}
}
class ClassWithUnionProp { prop: A | B }
>ClassWithUnionProp : ClassWithUnionProp
>prop : A | B
function negativePropTest(x: ClassWithUnionProp) {
>negativePropTest : (x: ClassWithUnionProp) => void
>x : ClassWithUnionProp
if ("a" in x.prop) {
>"a" in x.prop : boolean
>"a" : "a"
>x.prop : A | B
>x : ClassWithUnionProp
>prop : A | B
let y: string = x.prop.b;
>y : string
>x.prop.b : any
>x.prop : A
>x : ClassWithUnionProp
>prop : A
>b : any
} else {
let z: string = x.prop.a;
>z : string
>x.prop.a : any
>x.prop : B
>x : ClassWithUnionProp
>prop : B
>a : any
}
}
class NegativeClassTest {
>NegativeClassTest : NegativeClassTest
protected prop: A | B;
>prop : A | B
inThis() {
>inThis : () => void
if ("a" in this.prop) {
>"a" in this.prop : boolean
>"a" : "a"
>this.prop : A | B
>this : this
>prop : A | B
let z: number = this.prop.b;
>z : number
>this.prop.b : any
>this.prop : A
>this : this
>prop : A
>b : any
} else {
let y: string = this.prop.a;
>y : string
>this.prop.a : any
>this.prop : B
>this : this
>prop : B
>a : any
}
}
}
class UnreachableCodeDetection {
>UnreachableCodeDetection : UnreachableCodeDetection
a: string;
>a : string
inThis() {
>inThis : () => void
if ("a" in this) {
>"a" in this : boolean
>"a" : "a"
>this : this
} else {
let y = this.a;
>y : any
>this.a : any
>this : never
>a : any
}
}
}
function positiveIntersectionTest(x: { a: string } & { b: string }) {
>positiveIntersectionTest : (x: { a: string;} & { b: string;}) => void
>x : { a: string; } & { b: string; }
>a : string
>b : string
if ("a" in x) {
>"a" in x : boolean
>"a" : "a"
>x : { a: string; } & { b: string; }
let s: string = x.a;
>s : string
>x.a : string
>x : { a: string; } & { b: string; }
>a : string
} else {
let n: never = x;
>n : never
>x : never
}
}
// Repro from #38608
declare const error: Error;
>error : Error
if ('extra' in error) {
>'extra' in error : boolean
>'extra' : "extra"
>error : Error
error // Still Error
>error : Error
} else {
error // Error
>error : Error
}
function narrowsToNever(x: { l: number } | { r: number }) {
>narrowsToNever : (x: { l: number;} | { r: number;}) => number
>x : { l: number; } | { r: number; }
>l : number
>r : number
let v: number;
>v : number
if ("l" in x) {
>"l" in x : boolean
>"l" : "l"
>x : { l: number; } | { r: number; }
v = x.l;
>v = x.l : number
>v : number
>x.l : number
>x : { l: number; }
>l : number
}
else if ("r" in x) {
>"r" in x : boolean
>"r" : "r"
>x : { r: number; }
v = x.r;
>v = x.r : number
>v : number
>x.r : number
>x : { r: number; }
>r : number
}
else {
v = x
>v = x : never
>v : number
>x : never
}
return v;
>v : number
}
type AOrB = { aProp: number } | { bProp: number };
>AOrB : { aProp: number; } | { bProp: number; }
>aProp : number
>bProp : number
declare function isAOrB(x: unknown): x is AOrB;
>isAOrB : (x: unknown) => x is AOrB
>x : unknown
declare var x: unknown;
>x : unknown
if (isAOrB(x)) {
>isAOrB(x) : boolean
>isAOrB : (x: unknown) => x is AOrB
>x : unknown
if ("aProp" in x) {
>"aProp" in x : boolean
>"aProp" : "aProp"
>x : AOrB
x.aProp;
>x.aProp : number
>x : { aProp: number; }
>aProp : number
}
else if ("bProp" in x) {
>"bProp" in x : boolean
>"bProp" : "bProp"
>x : { bProp: number; }
x.bProp;
>x.bProp : number
>x : { bProp: number; }
>bProp : number
}
// x is never because of the type predicate from unknown
else if ("cProp" in x) {
>"cProp" in x : boolean
>"cProp" : "cProp"
>x : never
const _never: never = x;
>_never : never
>x : never
}
}
function negativeIntersectionTest() {
>negativeIntersectionTest : () => void
if ("ontouchstart" in window) {
>"ontouchstart" in window : boolean
>"ontouchstart" : "ontouchstart"
>window : Window & typeof globalThis
window.ontouchstart
>window.ontouchstart : ((this: GlobalEventHandlers, ev: TouchEvent) => any) & ((this: Window, ev: TouchEvent) => any)
>window : Window & typeof globalThis
>ontouchstart : ((this: GlobalEventHandlers, ev: TouchEvent) => any) & ((this: Window, ev: TouchEvent) => any)
} else {
window.ontouchstart
>window.ontouchstart : ((this: GlobalEventHandlers, ev: TouchEvent) => any) & ((this: Window, ev: TouchEvent) => any)
>window : Window & typeof globalThis
>ontouchstart : ((this: GlobalEventHandlers, ev: TouchEvent) => any) & ((this: Window, ev: TouchEvent) => any)
}
}

View File

@ -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;

View File

@ -1,24 +1,26 @@
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(15,11): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(16,11): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(17,11): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(15,11): error TS2322: Type 'boolean' is not assignable to type 'string | number | symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(16,11): error TS2322: Type 'void' is not assignable to type 'string | number | symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(17,11): error TS2322: Type '{}' is not assignable to type 'string | number | symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(19,11): error TS18050: The value 'null' cannot be used here.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(20,11): error TS18050: The value 'undefined' cannot be used here.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(22,11): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(23,11): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(24,12): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(25,12): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(35,16): error TS2361: The right-hand side of an 'in' expression must not be a primitive.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(36,16): error TS2361: The right-hand side of an 'in' expression must not be a primitive.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(37,16): error TS2361: The right-hand side of an 'in' expression must not be a primitive.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(38,16): error TS2361: The right-hand side of an 'in' expression must not be a primitive.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(39,16): error TS2361: The right-hand side of an 'in' expression must not be a primitive.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(40,16): error TS2361: The right-hand side of an 'in' expression must not be a primitive.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(41,16): error TS2361: The right-hand side of an 'in' expression must not be a primitive.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(42,16): error TS2361: The right-hand side of an 'in' expression must not be a primitive.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(22,11): error TS2322: Type 'boolean' is not assignable to type 'string | number | symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(23,11): error TS2322: Type '{}' is not assignable to type 'string | number | symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(24,12): error TS2322: Type 'string | Foo' is not assignable to type 'string | number | symbol'.
Type 'Foo' is not assignable to type 'string | number | symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(25,12): error TS2322: Type 'Foo' is not assignable to type 'string | number | symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(35,16): error TS2322: Type 'number' is not assignable to type 'object'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(36,16): error TS2322: Type 'boolean' is not assignable to type 'object'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(37,16): error TS2322: Type 'string' is not assignable to type 'object'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(38,16): error TS2322: Type 'void' is not assignable to type 'object'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(39,16): error TS2322: Type 'string | number' is not assignable to type 'object'.
Type 'string' is not assignable to type 'object'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(40,16): error TS2322: Type 'number' is not assignable to type 'object'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(41,16): error TS2322: Type 'boolean' is not assignable to type 'object'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(42,16): error TS2322: Type 'string' is not assignable to type 'object'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(43,16): error TS18050: The value 'null' cannot be used here.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(44,17): error TS18050: The value 'undefined' cannot be used here.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(47,11): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(47,17): error TS2361: The right-hand side of an 'in' expression must not be a primitive.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(47,11): error TS2322: Type '{}' is not assignable to type 'string | number | symbol'.
tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(47,17): error TS2322: Type 'string' is not assignable to type 'object'.
==== tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts (21 errors) ====
@ -38,13 +40,13 @@ tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInv
var ra1 = a1 in x;
~~
!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
!!! error TS2322: Type 'boolean' is not assignable to type 'string | number | symbol'.
var ra2 = a2 in x;
~~
!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
!!! error TS2322: Type 'void' is not assignable to type 'string | number | symbol'.
var ra3 = a3 in x;
~~
!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
!!! error TS2322: Type '{}' is not assignable to type 'string | number | symbol'.
var ra4 = a4 in x;
var ra5 = null in x;
~~~~
@ -55,16 +57,17 @@ tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInv
var ra7 = E.a in x;
var ra8 = false in x;
~~~~~
!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
!!! error TS2322: Type 'boolean' is not assignable to type 'string | number | symbol'.
var ra9 = {} in x;
~~
!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
!!! error TS2322: Type '{}' is not assignable to type 'string | number | symbol'.
var ra10 = a5 in x;
~~
!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
!!! error TS2322: Type 'string | Foo' is not assignable to type 'string | number | symbol'.
!!! error TS2322: Type 'Foo' is not assignable to type 'string | number | symbol'.
var ra11 = a6 in x;
~~
!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
!!! error TS2322: Type 'Foo' is not assignable to type 'string | number | symbol'.
// invalid right operands
// the right operand is required to be of type Any, an object type, or a type parameter type
@ -76,28 +79,29 @@ tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInv
var rb1 = x in b1;
~~
!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive.
!!! error TS2322: Type 'number' is not assignable to type 'object'.
var rb2 = x in b2;
~~
!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive.
!!! error TS2322: Type 'boolean' is not assignable to type 'object'.
var rb3 = x in b3;
~~
!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive.
!!! error TS2322: Type 'string' is not assignable to type 'object'.
var rb4 = x in b4;
~~
!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive.
!!! error TS2322: Type 'void' is not assignable to type 'object'.
var rb5 = x in b5;
~~
!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive.
!!! error TS2322: Type 'string | number' is not assignable to type 'object'.
!!! error TS2322: Type 'string' is not assignable to type 'object'.
var rb6 = x in 0;
~
!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive.
!!! error TS2322: Type 'number' is not assignable to type 'object'.
var rb7 = x in false;
~~~~~
!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive.
!!! error TS2322: Type 'boolean' is not assignable to type 'object'.
var rb8 = x in '';
~~
!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive.
!!! error TS2322: Type 'string' is not assignable to type 'object'.
var rb9 = x in null;
~~~~
!!! error TS18050: The value 'null' cannot be used here.
@ -108,6 +112,6 @@ tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInv
// both operands are invalid
var rc1 = {} in '';
~~
!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.
!!! error TS2322: Type '{}' is not assignable to type 'string | number | symbol'.
~~
!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive.
!!! error TS2322: Type 'string' is not assignable to type 'object'.

View File

@ -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: 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, U>(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: 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);

View File

@ -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<T, K extends keyof T>(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<T>(source: T, target: T) {

View File

@ -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.
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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.
!!! error TS2322: Type 'typeof Symbol.toPrimitive' is not assignable to type 'object'.

View File

@ -1,3 +1,6 @@
// @strict: true, false
// @target: es2015
class A { a: string; }
class B { b: string; }
@ -150,3 +153,130 @@ function negativeIntersectionTest() {
window.ontouchstart
}
}
function f1(x: unknown) {
if ("a" in x) {
x.a;
}
if (x && "a" in x) {
x.a;
}
if (x && typeof x === "object" && "a" in x) {
x.a;
}
if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) {
x.a;
x.b;
x.c;
}
}
function f2(x: object) {
if ("a" in x) {
x.a;
}
if ("a" in x && "b" in x && "c" in x) {
x.a;
x.b;
x.c;
}
}
function f3<T>(x: T) {
if ("a" in x) {
x.a;
}
if (x && "a" in x) {
x.a;
}
if (x && typeof x === "object" && "a" in x) {
x.a;
}
if (x && typeof x === "object" && "a" in x && "b" in x && "c" in x) {
x.a;
x.b;
x.c;
}
}
function f4(x: { a: string }) {
if ("a" in x) {
x.a;
}
if ("a" in x && "b" in x && "c" in x) {
x.a;
x.b;
x.c;
}
}
function f5(x: { a: string } | { b: string }) {
if ("a" in x) {
x; // { a: string }
}
else if ("b" in x) {
x; // { b: string }
}
else {
x; // never
}
}
function f6(x: { a: string } | { b: string }) {
if ("a" in x) {
x; // { a: string }
}
else if ("a" in x) {
x; // { b: string } & Record<"a", unknown>
}
else {
x; // { b: string }
}
}
// Object and corresponding intersection should narrow the same
function f7(x: { a: string, b: number }, y: { a: string } & { b: number }) {
if ("a" in x) {
x;
}
else {
x; // never
}
if ("a" in y) {
y;
}
else {
y; // never
}
}
const sym = Symbol();
function f8(x: object) {
if ("a" in x && 1 in x && sym in x) {
x.a;
x["a"];
x[1];
x["1"];
x[sym];
}
}
function f9(x: object) {
if ("a" in x && "1" in x && sym in x) {
x.a;
x["a"];
x[1];
x["1"];
x[sym];
}
}
// Repro from #50639
function foo<A>(value: A) {
if (typeof value === "object" && value !== null && "prop" in value) {
value; // A & object & Record<"prop", unknown>
}
}