diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7079ecf8a48f7..968fbbb969a88 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -19747,9 +19747,10 @@ namespace ts { // This ensures the subtype relationship is ordered, and preventing declaration order // from deciding which type "wins" in union subtype reduction. // They're still assignable to one another, since `readonly` doesn't affect assignability. + // This is only applied during the strictSubtypeRelation -- currently used in subtype reduction if ( - (relation === subtypeRelation || relation === strictSubtypeRelation) && - !!(sourcePropFlags & ModifierFlags.Readonly) && !(targetPropFlags & ModifierFlags.Readonly) + relation === strictSubtypeRelation && + isReadonlySymbol(sourceProp) && !isReadonlySymbol(targetProp) ) { return Ternary.False; } diff --git a/tests/baselines/reference/typeGuardNarrowByMutableUntypedField.js b/tests/baselines/reference/typeGuardNarrowByMutableUntypedField.js new file mode 100644 index 0000000000000..58f403ff806c2 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowByMutableUntypedField.js @@ -0,0 +1,11 @@ +//// [typeGuardNarrowByMutableUntypedField.ts] +declare function hasOwnProperty

(target: {}, property: P): target is { [K in P]: unknown }; +declare const arrayLikeOrIterable: ArrayLike | Iterable; +if (hasOwnProperty(arrayLikeOrIterable, 'length')) { + let x: number = arrayLikeOrIterable.length; +} + +//// [typeGuardNarrowByMutableUntypedField.js] +if (hasOwnProperty(arrayLikeOrIterable, 'length')) { + var x = arrayLikeOrIterable.length; +} diff --git a/tests/baselines/reference/typeGuardNarrowByMutableUntypedField.symbols b/tests/baselines/reference/typeGuardNarrowByMutableUntypedField.symbols new file mode 100644 index 0000000000000..6c4720296ccd0 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowByMutableUntypedField.symbols @@ -0,0 +1,27 @@ +=== tests/cases/compiler/typeGuardNarrowByMutableUntypedField.ts === +declare function hasOwnProperty

(target: {}, property: P): target is { [K in P]: unknown }; +>hasOwnProperty : Symbol(hasOwnProperty, Decl(typeGuardNarrowByMutableUntypedField.ts, 0, 0)) +>P : Symbol(P, Decl(typeGuardNarrowByMutableUntypedField.ts, 0, 32)) +>PropertyKey : Symbol(PropertyKey, Decl(lib.es5.d.ts, --, --)) +>target : Symbol(target, Decl(typeGuardNarrowByMutableUntypedField.ts, 0, 55)) +>property : Symbol(property, Decl(typeGuardNarrowByMutableUntypedField.ts, 0, 66)) +>P : Symbol(P, Decl(typeGuardNarrowByMutableUntypedField.ts, 0, 32)) +>target : Symbol(target, Decl(typeGuardNarrowByMutableUntypedField.ts, 0, 55)) +>K : Symbol(K, Decl(typeGuardNarrowByMutableUntypedField.ts, 0, 94)) +>P : Symbol(P, Decl(typeGuardNarrowByMutableUntypedField.ts, 0, 32)) + +declare const arrayLikeOrIterable: ArrayLike | Iterable; +>arrayLikeOrIterable : Symbol(arrayLikeOrIterable, Decl(typeGuardNarrowByMutableUntypedField.ts, 1, 13)) +>ArrayLike : Symbol(ArrayLike, Decl(lib.es5.d.ts, --, --)) +>Iterable : Symbol(Iterable, Decl(lib.es2015.iterable.d.ts, --, --)) + +if (hasOwnProperty(arrayLikeOrIterable, 'length')) { +>hasOwnProperty : Symbol(hasOwnProperty, Decl(typeGuardNarrowByMutableUntypedField.ts, 0, 0)) +>arrayLikeOrIterable : Symbol(arrayLikeOrIterable, Decl(typeGuardNarrowByMutableUntypedField.ts, 1, 13)) + + let x: number = arrayLikeOrIterable.length; +>x : Symbol(x, Decl(typeGuardNarrowByMutableUntypedField.ts, 3, 7)) +>arrayLikeOrIterable.length : Symbol(ArrayLike.length, Decl(lib.es5.d.ts, --, --)) +>arrayLikeOrIterable : Symbol(arrayLikeOrIterable, Decl(typeGuardNarrowByMutableUntypedField.ts, 1, 13)) +>length : Symbol(ArrayLike.length, Decl(lib.es5.d.ts, --, --)) +} diff --git a/tests/baselines/reference/typeGuardNarrowByMutableUntypedField.types b/tests/baselines/reference/typeGuardNarrowByMutableUntypedField.types new file mode 100644 index 0000000000000..46231c8ff6b44 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowByMutableUntypedField.types @@ -0,0 +1,21 @@ +=== tests/cases/compiler/typeGuardNarrowByMutableUntypedField.ts === +declare function hasOwnProperty

(target: {}, property: P): target is { [K in P]: unknown }; +>hasOwnProperty :

(target: {}, property: P) => target is { [K in P]: unknown; } +>target : {} +>property : P + +declare const arrayLikeOrIterable: ArrayLike | Iterable; +>arrayLikeOrIterable : ArrayLike | Iterable + +if (hasOwnProperty(arrayLikeOrIterable, 'length')) { +>hasOwnProperty(arrayLikeOrIterable, 'length') : boolean +>hasOwnProperty :

(target: {}, property: P) => target is { [K in P]: unknown; } +>arrayLikeOrIterable : ArrayLike | Iterable +>'length' : "length" + + let x: number = arrayLikeOrIterable.length; +>x : number +>arrayLikeOrIterable.length : number +>arrayLikeOrIterable : ArrayLike +>length : number +} diff --git a/tests/baselines/reference/typeGuardNarrowByUntypedField.js b/tests/baselines/reference/typeGuardNarrowByUntypedField.js new file mode 100644 index 0000000000000..8c2a90c334782 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowByUntypedField.js @@ -0,0 +1,11 @@ +//// [typeGuardNarrowByUntypedField.ts] +declare function hasOwnProperty

(target: {}, property: P): target is { readonly [K in P]: unknown }; +declare const arrayLikeOrIterable: ArrayLike | Iterable; +if (hasOwnProperty(arrayLikeOrIterable, 'length')) { + let x: number = arrayLikeOrIterable.length; +} + +//// [typeGuardNarrowByUntypedField.js] +if (hasOwnProperty(arrayLikeOrIterable, 'length')) { + var x = arrayLikeOrIterable.length; +} diff --git a/tests/baselines/reference/typeGuardNarrowByUntypedField.symbols b/tests/baselines/reference/typeGuardNarrowByUntypedField.symbols new file mode 100644 index 0000000000000..0bf7a8b6b2546 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowByUntypedField.symbols @@ -0,0 +1,27 @@ +=== tests/cases/compiler/typeGuardNarrowByUntypedField.ts === +declare function hasOwnProperty

(target: {}, property: P): target is { readonly [K in P]: unknown }; +>hasOwnProperty : Symbol(hasOwnProperty, Decl(typeGuardNarrowByUntypedField.ts, 0, 0)) +>P : Symbol(P, Decl(typeGuardNarrowByUntypedField.ts, 0, 32)) +>PropertyKey : Symbol(PropertyKey, Decl(lib.es5.d.ts, --, --)) +>target : Symbol(target, Decl(typeGuardNarrowByUntypedField.ts, 0, 55)) +>property : Symbol(property, Decl(typeGuardNarrowByUntypedField.ts, 0, 66)) +>P : Symbol(P, Decl(typeGuardNarrowByUntypedField.ts, 0, 32)) +>target : Symbol(target, Decl(typeGuardNarrowByUntypedField.ts, 0, 55)) +>K : Symbol(K, Decl(typeGuardNarrowByUntypedField.ts, 0, 103)) +>P : Symbol(P, Decl(typeGuardNarrowByUntypedField.ts, 0, 32)) + +declare const arrayLikeOrIterable: ArrayLike | Iterable; +>arrayLikeOrIterable : Symbol(arrayLikeOrIterable, Decl(typeGuardNarrowByUntypedField.ts, 1, 13)) +>ArrayLike : Symbol(ArrayLike, Decl(lib.es5.d.ts, --, --)) +>Iterable : Symbol(Iterable, Decl(lib.es2015.iterable.d.ts, --, --)) + +if (hasOwnProperty(arrayLikeOrIterable, 'length')) { +>hasOwnProperty : Symbol(hasOwnProperty, Decl(typeGuardNarrowByUntypedField.ts, 0, 0)) +>arrayLikeOrIterable : Symbol(arrayLikeOrIterable, Decl(typeGuardNarrowByUntypedField.ts, 1, 13)) + + let x: number = arrayLikeOrIterable.length; +>x : Symbol(x, Decl(typeGuardNarrowByUntypedField.ts, 3, 7)) +>arrayLikeOrIterable.length : Symbol(ArrayLike.length, Decl(lib.es5.d.ts, --, --)) +>arrayLikeOrIterable : Symbol(arrayLikeOrIterable, Decl(typeGuardNarrowByUntypedField.ts, 1, 13)) +>length : Symbol(ArrayLike.length, Decl(lib.es5.d.ts, --, --)) +} diff --git a/tests/baselines/reference/typeGuardNarrowByUntypedField.types b/tests/baselines/reference/typeGuardNarrowByUntypedField.types new file mode 100644 index 0000000000000..f44fea6b0f2d2 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowByUntypedField.types @@ -0,0 +1,21 @@ +=== tests/cases/compiler/typeGuardNarrowByUntypedField.ts === +declare function hasOwnProperty

(target: {}, property: P): target is { readonly [K in P]: unknown }; +>hasOwnProperty :

(target: {}, property: P) => target is { readonly [K in P]: unknown; } +>target : {} +>property : P + +declare const arrayLikeOrIterable: ArrayLike | Iterable; +>arrayLikeOrIterable : ArrayLike | Iterable + +if (hasOwnProperty(arrayLikeOrIterable, 'length')) { +>hasOwnProperty(arrayLikeOrIterable, 'length') : boolean +>hasOwnProperty :

(target: {}, property: P) => target is { readonly [K in P]: unknown; } +>arrayLikeOrIterable : ArrayLike | Iterable +>'length' : "length" + + let x: number = arrayLikeOrIterable.length; +>x : number +>arrayLikeOrIterable.length : number +>arrayLikeOrIterable : ArrayLike +>length : number +} diff --git a/tests/cases/compiler/typeGuardNarrowByMutableUntypedField.ts b/tests/cases/compiler/typeGuardNarrowByMutableUntypedField.ts new file mode 100644 index 0000000000000..1a585159d8a6d --- /dev/null +++ b/tests/cases/compiler/typeGuardNarrowByMutableUntypedField.ts @@ -0,0 +1,6 @@ +// @lib: es6 +declare function hasOwnProperty

(target: {}, property: P): target is { [K in P]: unknown }; +declare const arrayLikeOrIterable: ArrayLike | Iterable; +if (hasOwnProperty(arrayLikeOrIterable, 'length')) { + let x: number = arrayLikeOrIterable.length; +} \ No newline at end of file diff --git a/tests/cases/compiler/typeGuardNarrowByUntypedField.ts b/tests/cases/compiler/typeGuardNarrowByUntypedField.ts new file mode 100644 index 0000000000000..2bde0c945ece1 --- /dev/null +++ b/tests/cases/compiler/typeGuardNarrowByUntypedField.ts @@ -0,0 +1,6 @@ +// @lib: es6 +declare function hasOwnProperty

(target: {}, property: P): target is { readonly [K in P]: unknown }; +declare const arrayLikeOrIterable: ArrayLike | Iterable; +if (hasOwnProperty(arrayLikeOrIterable, 'length')) { + let x: number = arrayLikeOrIterable.length; +} \ No newline at end of file