Exclude special index signature rule from strict subtype relation (#53388)

Co-authored-by: Daniel Rosenwasser <DanielRosenwasser@users.noreply.github.com>
This commit is contained in:
Anders Hejlsberg 2023-03-23 07:03:09 -07:00 committed by GitHub
parent 7009c76d00
commit 25550bd3d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 744 additions and 72 deletions

View File

@ -22485,7 +22485,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const targetHasStringIndex = some(indexInfos, info => info.keyType === stringType);
let result = Ternary.True;
for (const targetInfo of indexInfos) {
const related = !sourceIsPrimitive && targetHasStringIndex && targetInfo.type.flags & TypeFlags.Any ? Ternary.True :
const related = relation !== strictSubtypeRelation && !sourceIsPrimitive && targetHasStringIndex && targetInfo.type.flags & TypeFlags.Any ? Ternary.True :
isGenericMappedType(source) && targetHasStringIndex ? isRelatedTo(getTemplateTypeFromMappedType(source), targetInfo.type, RecursionFlags.Both, reportErrors) :
typeRelatedToIndexInfo(source, targetInfo, reportErrors, intersectionState);
if (!related) {

View File

@ -0,0 +1,155 @@
tests/cases/compiler/narrowingMutualSubtypes.ts(117,17): error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'any[] | Record<string, any>'.
No index signature with a parameter of type 'string' was found on type 'any[] | Record<string, any>'.
tests/cases/compiler/narrowingMutualSubtypes.ts(118,29): error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'any[] | Record<string, any>'.
No index signature with a parameter of type 'string' was found on type 'any[] | Record<string, any>'.
==== tests/cases/compiler/narrowingMutualSubtypes.ts (2 errors) ====
// Check that `any` is a strict supertype of `unknown`
declare const ru1: { [x: string]: unknown };
declare const ra1: { [x: string]: any };
const a1a = [ru1, ra1]; // { [x: string]: any }[]
const a1b = [ra1, ru1]; // { [x: string]: any }[]
declare const ra2: { [x: string]: any };
declare const ru2: { [x: string]: unknown };
const a2a = [ru2, ra2]; // { [x: string]: any }[]
const a2b = [ra2, ru2]; // { [x: string]: any }[]
// Check that `{}` is strict supertype of any non-empty object
const c3 = {};
declare const r3: { [x: string]: unknown }
const a3a = [c3, r3]; // {}[]
const a3b = [r3, c3]; // {}[]
declare const r4: { [x: string]: unknown }
const c4 = {};
const a4a = [c4, r4]; // {}[]
const a4b = [r4, c4]; // {}[]
// Check that {} is a strict supertype of Record<string, unknown>
declare function isObject1(value: unknown): value is Record<string, unknown>;
function gg1(x: {}) {
if (isObject1(x)) {
x; // Record<string, unknown>
}
else {
x; // {}
}
x; // {}
}
declare function isObject2(value: unknown): value is {};
function gg2(x: Record<string, unknown>) {
if (isObject2(x)) {
x; // Record<string, unknown>
}
else {
x; // never
}
x; // Record<string, unknown>
}
// Check that {} is a strict supertype of Record<string, any>
declare function isObject3(value: unknown): value is Record<string, any>;
function gg3(x: {}) {
if (isObject3(x)) {
x; // Record<string, any>
}
else {
x; // {}
}
x; // {}
}
declare function isObject4(value: unknown): value is {};
function gg4(x: Record<string, any>) {
if (isObject4(x)) {
x; // Record<string, any>
}
else {
x; // never
}
x; // Record<string, any>
}
// Repro from #50916
type Identity<T> = {[K in keyof T]: T[K]};
type Self<T> = T extends unknown ? Identity<T> : never;
function is<T>(value: T): value is Self<T> {
return true;
}
type Union = {a: number} | {b: number} | {c: number};
function example(x: Union) {
if (is(x)) {}
if (is(x)) {}
if (is(x)) {}
if (is(x)) {}
if (is(x)) {}
if (is(x)) {}
if (is(x)) {}
if (is(x)) {}
x; // Union
}
function checksArrayOrObject1(obj: Record<string, any> | Record<string, any>[]) {
// "accidentally" guards the first branch on the length
if (Array.isArray(obj) && obj.length) {
for (let key in obj) {
if (obj[key] !== undefined) {
console.log(obj[key])
}
}
}
else {
// 'obj' should probably not include an array type here.
for (let key in obj) {
if (obj[key] !== undefined) {
~~~~~~~~
!!! error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'any[] | Record<string, any>'.
!!! error TS7053: No index signature with a parameter of type 'string' was found on type 'any[] | Record<string, any>'.
console.log(obj[key])
~~~~~~~~
!!! error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'any[] | Record<string, any>'.
!!! error TS7053: No index signature with a parameter of type 'string' was found on type 'any[] | Record<string, any>'.
}
}
}
}
function checksArrayOrObject2(obj: Record<string, any> | Record<string, any>[]) {
if (Array.isArray(obj)) {
// obj should only be an array type here
for (let key in obj) {
if (obj[key] !== undefined) {
console.log(obj[key])
}
}
}
else {
// 'obj' should probably not include an array type here.
for (let key in obj) {
if (obj[key] !== undefined) {
console.log(obj[key])
}
}
}
}

View File

@ -27,11 +27,11 @@ const c4 = {};
const a4a = [c4, r4]; // {}[]
const a4b = [r4, c4]; // {}[]
// Check that narrowing preserves original type in false branch for non-identical mutual subtypes
// Check that {} is a strict supertype of Record<string, unknown>
declare function isObject1(value: unknown): value is Record<string, unknown>;
function gg(x: {}) {
function gg1(x: {}) {
if (isObject1(x)) {
x; // Record<string, unknown>
}
@ -45,14 +45,40 @@ declare function isObject2(value: unknown): value is {};
function gg2(x: Record<string, unknown>) {
if (isObject2(x)) {
x; // {}
}
else {
x; // Record<string, unknown>
}
else {
x; // never
}
x; // Record<string, unknown>
}
// Check that {} is a strict supertype of Record<string, any>
declare function isObject3(value: unknown): value is Record<string, any>;
function gg3(x: {}) {
if (isObject3(x)) {
x; // Record<string, any>
}
else {
x; // {}
}
x; // {}
}
declare function isObject4(value: unknown): value is {};
function gg4(x: Record<string, any>) {
if (isObject4(x)) {
x; // Record<string, any>
}
else {
x; // never
}
x; // Record<string, any>
}
// Repro from #50916
type Identity<T> = {[K in keyof T]: T[K]};
@ -76,6 +102,44 @@ function example(x: Union) {
if (is(x)) {}
x; // Union
}
function checksArrayOrObject1(obj: Record<string, any> | Record<string, any>[]) {
// "accidentally" guards the first branch on the length
if (Array.isArray(obj) && obj.length) {
for (let key in obj) {
if (obj[key] !== undefined) {
console.log(obj[key])
}
}
}
else {
// 'obj' should probably not include an array type here.
for (let key in obj) {
if (obj[key] !== undefined) {
console.log(obj[key])
}
}
}
}
function checksArrayOrObject2(obj: Record<string, any> | Record<string, any>[]) {
if (Array.isArray(obj)) {
// obj should only be an array type here
for (let key in obj) {
if (obj[key] !== undefined) {
console.log(obj[key])
}
}
}
else {
// 'obj' should probably not include an array type here.
for (let key in obj) {
if (obj[key] !== undefined) {
console.log(obj[key])
}
}
}
}
//// [narrowingMutualSubtypes.js]
@ -92,7 +156,7 @@ var a3b = [r3, c3]; // {}[]
var c4 = {};
var a4a = [c4, r4]; // {}[]
var a4b = [r4, c4]; // {}[]
function gg(x) {
function gg1(x) {
if (isObject1(x)) {
x; // Record<string, unknown>
}
@ -103,13 +167,31 @@ function gg(x) {
}
function gg2(x) {
if (isObject2(x)) {
x; // {}
}
else {
x; // Record<string, unknown>
}
else {
x; // never
}
x; // Record<string, unknown>
}
function gg3(x) {
if (isObject3(x)) {
x; // Record<string, any>
}
else {
x; // {}
}
x; // {}
}
function gg4(x) {
if (isObject4(x)) {
x; // Record<string, any>
}
else {
x; // never
}
x; // Record<string, any>
}
function is(value) {
return true;
}
@ -124,3 +206,39 @@ function example(x) {
if (is(x)) { }
x; // Union
}
function checksArrayOrObject1(obj) {
// "accidentally" guards the first branch on the length
if (Array.isArray(obj) && obj.length) {
for (var key in obj) {
if (obj[key] !== undefined) {
console.log(obj[key]);
}
}
}
else {
// 'obj' should probably not include an array type here.
for (var key in obj) {
if (obj[key] !== undefined) {
console.log(obj[key]);
}
}
}
}
function checksArrayOrObject2(obj) {
if (Array.isArray(obj)) {
// obj should only be an array type here
for (var key in obj) {
if (obj[key] !== undefined) {
console.log(obj[key]);
}
}
}
else {
// 'obj' should probably not include an array type here.
for (var key in obj) {
if (obj[key] !== undefined) {
console.log(obj[key]);
}
}
}
}

View File

@ -73,7 +73,7 @@ const a4b = [r4, c4]; // {}[]
>r4 : Symbol(r4, Decl(narrowingMutualSubtypes.ts, 22, 13))
>c4 : Symbol(c4, Decl(narrowingMutualSubtypes.ts, 23, 5))
// Check that narrowing preserves original type in false branch for non-identical mutual subtypes
// Check that {} is a strict supertype of Record<string, unknown>
declare function isObject1(value: unknown): value is Record<string, unknown>;
>isObject1 : Symbol(isObject1, Decl(narrowingMutualSubtypes.ts, 26, 21))
@ -81,23 +81,23 @@ declare function isObject1(value: unknown): value is Record<string, unknown>;
>value : Symbol(value, Decl(narrowingMutualSubtypes.ts, 30, 27))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
function gg(x: {}) {
>gg : Symbol(gg, Decl(narrowingMutualSubtypes.ts, 30, 77))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 32, 12))
function gg1(x: {}) {
>gg1 : Symbol(gg1, Decl(narrowingMutualSubtypes.ts, 30, 77))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 32, 13))
if (isObject1(x)) {
>isObject1 : Symbol(isObject1, Decl(narrowingMutualSubtypes.ts, 26, 21))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 32, 12))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 32, 13))
x; // Record<string, unknown>
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 32, 12))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 32, 13))
}
else {
x; // {}
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 32, 12))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 32, 13))
}
x; // {}
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 32, 12))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 32, 13))
}
declare function isObject2(value: unknown): value is {};
@ -114,90 +114,251 @@ function gg2(x: Record<string, unknown>) {
>isObject2 : Symbol(isObject2, Decl(narrowingMutualSubtypes.ts, 40, 1))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 44, 13))
x; // {}
x; // Record<string, unknown>
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 44, 13))
}
else {
x; // Record<string, unknown>
x; // never
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 44, 13))
}
x; // Record<string, unknown>
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 44, 13))
}
// Check that {} is a strict supertype of Record<string, any>
declare function isObject3(value: unknown): value is Record<string, any>;
>isObject3 : Symbol(isObject3, Decl(narrowingMutualSubtypes.ts, 52, 1))
>value : Symbol(value, Decl(narrowingMutualSubtypes.ts, 56, 27))
>value : Symbol(value, Decl(narrowingMutualSubtypes.ts, 56, 27))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
function gg3(x: {}) {
>gg3 : Symbol(gg3, Decl(narrowingMutualSubtypes.ts, 56, 73))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 58, 13))
if (isObject3(x)) {
>isObject3 : Symbol(isObject3, Decl(narrowingMutualSubtypes.ts, 52, 1))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 58, 13))
x; // Record<string, any>
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 58, 13))
}
else {
x; // {}
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 58, 13))
}
x; // {}
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 58, 13))
}
declare function isObject4(value: unknown): value is {};
>isObject4 : Symbol(isObject4, Decl(narrowingMutualSubtypes.ts, 66, 1))
>value : Symbol(value, Decl(narrowingMutualSubtypes.ts, 68, 27))
>value : Symbol(value, Decl(narrowingMutualSubtypes.ts, 68, 27))
function gg4(x: Record<string, any>) {
>gg4 : Symbol(gg4, Decl(narrowingMutualSubtypes.ts, 68, 56))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 70, 13))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
if (isObject4(x)) {
>isObject4 : Symbol(isObject4, Decl(narrowingMutualSubtypes.ts, 66, 1))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 70, 13))
x; // Record<string, any>
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 70, 13))
}
else {
x; // never
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 70, 13))
}
x; // Record<string, any>
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 70, 13))
}
// Repro from #50916
type Identity<T> = {[K in keyof T]: T[K]};
>Identity : Symbol(Identity, Decl(narrowingMutualSubtypes.ts, 52, 1))
>T : Symbol(T, Decl(narrowingMutualSubtypes.ts, 56, 14))
>K : Symbol(K, Decl(narrowingMutualSubtypes.ts, 56, 21))
>T : Symbol(T, Decl(narrowingMutualSubtypes.ts, 56, 14))
>T : Symbol(T, Decl(narrowingMutualSubtypes.ts, 56, 14))
>K : Symbol(K, Decl(narrowingMutualSubtypes.ts, 56, 21))
>Identity : Symbol(Identity, Decl(narrowingMutualSubtypes.ts, 78, 1))
>T : Symbol(T, Decl(narrowingMutualSubtypes.ts, 82, 14))
>K : Symbol(K, Decl(narrowingMutualSubtypes.ts, 82, 21))
>T : Symbol(T, Decl(narrowingMutualSubtypes.ts, 82, 14))
>T : Symbol(T, Decl(narrowingMutualSubtypes.ts, 82, 14))
>K : Symbol(K, Decl(narrowingMutualSubtypes.ts, 82, 21))
type Self<T> = T extends unknown ? Identity<T> : never;
>Self : Symbol(Self, Decl(narrowingMutualSubtypes.ts, 56, 42))
>T : Symbol(T, Decl(narrowingMutualSubtypes.ts, 58, 10))
>T : Symbol(T, Decl(narrowingMutualSubtypes.ts, 58, 10))
>Identity : Symbol(Identity, Decl(narrowingMutualSubtypes.ts, 52, 1))
>T : Symbol(T, Decl(narrowingMutualSubtypes.ts, 58, 10))
>Self : Symbol(Self, Decl(narrowingMutualSubtypes.ts, 82, 42))
>T : Symbol(T, Decl(narrowingMutualSubtypes.ts, 84, 10))
>T : Symbol(T, Decl(narrowingMutualSubtypes.ts, 84, 10))
>Identity : Symbol(Identity, Decl(narrowingMutualSubtypes.ts, 78, 1))
>T : Symbol(T, Decl(narrowingMutualSubtypes.ts, 84, 10))
function is<T>(value: T): value is Self<T> {
>is : Symbol(is, Decl(narrowingMutualSubtypes.ts, 58, 55))
>T : Symbol(T, Decl(narrowingMutualSubtypes.ts, 60, 12))
>value : Symbol(value, Decl(narrowingMutualSubtypes.ts, 60, 15))
>T : Symbol(T, Decl(narrowingMutualSubtypes.ts, 60, 12))
>value : Symbol(value, Decl(narrowingMutualSubtypes.ts, 60, 15))
>Self : Symbol(Self, Decl(narrowingMutualSubtypes.ts, 56, 42))
>T : Symbol(T, Decl(narrowingMutualSubtypes.ts, 60, 12))
>is : Symbol(is, Decl(narrowingMutualSubtypes.ts, 84, 55))
>T : Symbol(T, Decl(narrowingMutualSubtypes.ts, 86, 12))
>value : Symbol(value, Decl(narrowingMutualSubtypes.ts, 86, 15))
>T : Symbol(T, Decl(narrowingMutualSubtypes.ts, 86, 12))
>value : Symbol(value, Decl(narrowingMutualSubtypes.ts, 86, 15))
>Self : Symbol(Self, Decl(narrowingMutualSubtypes.ts, 82, 42))
>T : Symbol(T, Decl(narrowingMutualSubtypes.ts, 86, 12))
return true;
}
type Union = {a: number} | {b: number} | {c: number};
>Union : Symbol(Union, Decl(narrowingMutualSubtypes.ts, 62, 1))
>a : Symbol(a, Decl(narrowingMutualSubtypes.ts, 64, 15))
>b : Symbol(b, Decl(narrowingMutualSubtypes.ts, 64, 29))
>c : Symbol(c, Decl(narrowingMutualSubtypes.ts, 64, 43))
>Union : Symbol(Union, Decl(narrowingMutualSubtypes.ts, 88, 1))
>a : Symbol(a, Decl(narrowingMutualSubtypes.ts, 90, 15))
>b : Symbol(b, Decl(narrowingMutualSubtypes.ts, 90, 29))
>c : Symbol(c, Decl(narrowingMutualSubtypes.ts, 90, 43))
function example(x: Union) {
>example : Symbol(example, Decl(narrowingMutualSubtypes.ts, 64, 54))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 66, 17))
>Union : Symbol(Union, Decl(narrowingMutualSubtypes.ts, 62, 1))
>example : Symbol(example, Decl(narrowingMutualSubtypes.ts, 90, 54))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 92, 17))
>Union : Symbol(Union, Decl(narrowingMutualSubtypes.ts, 88, 1))
if (is(x)) {}
>is : Symbol(is, Decl(narrowingMutualSubtypes.ts, 58, 55))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 66, 17))
>is : Symbol(is, Decl(narrowingMutualSubtypes.ts, 84, 55))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 92, 17))
if (is(x)) {}
>is : Symbol(is, Decl(narrowingMutualSubtypes.ts, 58, 55))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 66, 17))
>is : Symbol(is, Decl(narrowingMutualSubtypes.ts, 84, 55))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 92, 17))
if (is(x)) {}
>is : Symbol(is, Decl(narrowingMutualSubtypes.ts, 58, 55))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 66, 17))
>is : Symbol(is, Decl(narrowingMutualSubtypes.ts, 84, 55))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 92, 17))
if (is(x)) {}
>is : Symbol(is, Decl(narrowingMutualSubtypes.ts, 58, 55))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 66, 17))
>is : Symbol(is, Decl(narrowingMutualSubtypes.ts, 84, 55))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 92, 17))
if (is(x)) {}
>is : Symbol(is, Decl(narrowingMutualSubtypes.ts, 58, 55))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 66, 17))
>is : Symbol(is, Decl(narrowingMutualSubtypes.ts, 84, 55))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 92, 17))
if (is(x)) {}
>is : Symbol(is, Decl(narrowingMutualSubtypes.ts, 58, 55))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 66, 17))
>is : Symbol(is, Decl(narrowingMutualSubtypes.ts, 84, 55))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 92, 17))
if (is(x)) {}
>is : Symbol(is, Decl(narrowingMutualSubtypes.ts, 58, 55))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 66, 17))
>is : Symbol(is, Decl(narrowingMutualSubtypes.ts, 84, 55))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 92, 17))
if (is(x)) {}
>is : Symbol(is, Decl(narrowingMutualSubtypes.ts, 58, 55))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 66, 17))
>is : Symbol(is, Decl(narrowingMutualSubtypes.ts, 84, 55))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 92, 17))
x; // Union
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 66, 17))
>x : Symbol(x, Decl(narrowingMutualSubtypes.ts, 92, 17))
}
function checksArrayOrObject1(obj: Record<string, any> | Record<string, any>[]) {
>checksArrayOrObject1 : Symbol(checksArrayOrObject1, Decl(narrowingMutualSubtypes.ts, 102, 1))
>obj : Symbol(obj, Decl(narrowingMutualSubtypes.ts, 104, 30))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
// "accidentally" guards the first branch on the length
if (Array.isArray(obj) && obj.length) {
>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, --, --))
>obj : Symbol(obj, Decl(narrowingMutualSubtypes.ts, 104, 30))
>obj.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
>obj : Symbol(obj, Decl(narrowingMutualSubtypes.ts, 104, 30))
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
for (let key in obj) {
>key : Symbol(key, Decl(narrowingMutualSubtypes.ts, 107, 16))
>obj : Symbol(obj, Decl(narrowingMutualSubtypes.ts, 104, 30))
if (obj[key] !== undefined) {
>obj : Symbol(obj, Decl(narrowingMutualSubtypes.ts, 104, 30))
>key : Symbol(key, Decl(narrowingMutualSubtypes.ts, 107, 16))
>undefined : Symbol(undefined)
console.log(obj[key])
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>obj : Symbol(obj, Decl(narrowingMutualSubtypes.ts, 104, 30))
>key : Symbol(key, Decl(narrowingMutualSubtypes.ts, 107, 16))
}
}
}
else {
// 'obj' should probably not include an array type here.
for (let key in obj) {
>key : Symbol(key, Decl(narrowingMutualSubtypes.ts, 115, 16))
>obj : Symbol(obj, Decl(narrowingMutualSubtypes.ts, 104, 30))
if (obj[key] !== undefined) {
>obj : Symbol(obj, Decl(narrowingMutualSubtypes.ts, 104, 30))
>key : Symbol(key, Decl(narrowingMutualSubtypes.ts, 115, 16))
>undefined : Symbol(undefined)
console.log(obj[key])
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>obj : Symbol(obj, Decl(narrowingMutualSubtypes.ts, 104, 30))
>key : Symbol(key, Decl(narrowingMutualSubtypes.ts, 115, 16))
}
}
}
}
function checksArrayOrObject2(obj: Record<string, any> | Record<string, any>[]) {
>checksArrayOrObject2 : Symbol(checksArrayOrObject2, Decl(narrowingMutualSubtypes.ts, 121, 1))
>obj : Symbol(obj, Decl(narrowingMutualSubtypes.ts, 123, 30))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --))
if (Array.isArray(obj)) {
>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, --, --))
>obj : Symbol(obj, Decl(narrowingMutualSubtypes.ts, 123, 30))
// obj should only be an array type here
for (let key in obj) {
>key : Symbol(key, Decl(narrowingMutualSubtypes.ts, 126, 16))
>obj : Symbol(obj, Decl(narrowingMutualSubtypes.ts, 123, 30))
if (obj[key] !== undefined) {
>obj : Symbol(obj, Decl(narrowingMutualSubtypes.ts, 123, 30))
>key : Symbol(key, Decl(narrowingMutualSubtypes.ts, 126, 16))
>undefined : Symbol(undefined)
console.log(obj[key])
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>obj : Symbol(obj, Decl(narrowingMutualSubtypes.ts, 123, 30))
>key : Symbol(key, Decl(narrowingMutualSubtypes.ts, 126, 16))
}
}
}
else {
// 'obj' should probably not include an array type here.
for (let key in obj) {
>key : Symbol(key, Decl(narrowingMutualSubtypes.ts, 134, 16))
>obj : Symbol(obj, Decl(narrowingMutualSubtypes.ts, 123, 30))
if (obj[key] !== undefined) {
>obj : Symbol(obj, Decl(narrowingMutualSubtypes.ts, 123, 30))
>key : Symbol(key, Decl(narrowingMutualSubtypes.ts, 134, 16))
>undefined : Symbol(undefined)
console.log(obj[key])
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>obj : Symbol(obj, Decl(narrowingMutualSubtypes.ts, 123, 30))
>key : Symbol(key, Decl(narrowingMutualSubtypes.ts, 134, 16))
}
}
}
}

View File

@ -83,14 +83,14 @@ const a4b = [r4, c4]; // {}[]
>r4 : { [x: string]: unknown; }
>c4 : {}
// Check that narrowing preserves original type in false branch for non-identical mutual subtypes
// Check that {} is a strict supertype of Record<string, unknown>
declare function isObject1(value: unknown): value is Record<string, unknown>;
>isObject1 : (value: unknown) => value is Record<string, unknown>
>value : unknown
function gg(x: {}) {
>gg : (x: {}) => void
function gg1(x: {}) {
>gg1 : (x: {}) => void
>x : {}
if (isObject1(x)) {
@ -122,17 +122,67 @@ function gg2(x: Record<string, unknown>) {
>isObject2 : (value: unknown) => value is {}
>x : Record<string, unknown>
x; // {}
x; // Record<string, unknown>
>x : Record<string, unknown>
}
else {
x; // Record<string, unknown>
x; // never
>x : never
}
x; // Record<string, unknown>
>x : Record<string, unknown>
}
// Check that {} is a strict supertype of Record<string, any>
declare function isObject3(value: unknown): value is Record<string, any>;
>isObject3 : (value: unknown) => value is Record<string, any>
>value : unknown
function gg3(x: {}) {
>gg3 : (x: {}) => void
>x : {}
if (isObject3(x)) {
>isObject3(x) : boolean
>isObject3 : (value: unknown) => value is Record<string, any>
>x : {}
x; // Record<string, any>
>x : Record<string, any>
}
else {
x; // {}
>x : {}
}
x; // {}
>x : {}
}
declare function isObject4(value: unknown): value is {};
>isObject4 : (value: unknown) => value is {}
>value : unknown
function gg4(x: Record<string, any>) {
>gg4 : (x: Record<string, any>) => void
>x : Record<string, any>
if (isObject4(x)) {
>isObject4(x) : boolean
>isObject4 : (value: unknown) => value is {}
>x : Record<string, any>
x; // Record<string, any>
>x : Record<string, any>
}
else {
x; // never
>x : never
}
x; // Record<string, any>
>x : Record<string, any>
}
// Repro from #50916
type Identity<T> = {[K in keyof T]: T[K]};
@ -203,3 +253,127 @@ function example(x: Union) {
>x : Union
}
function checksArrayOrObject1(obj: Record<string, any> | Record<string, any>[]) {
>checksArrayOrObject1 : (obj: Record<string, any> | Record<string, any>[]) => void
>obj : Record<string, any> | Record<string, any>[]
// "accidentally" guards the first branch on the length
if (Array.isArray(obj) && obj.length) {
>Array.isArray(obj) && obj.length : number | false
>Array.isArray(obj) : boolean
>Array.isArray : (arg: any) => arg is any[]
>Array : ArrayConstructor
>isArray : (arg: any) => arg is any[]
>obj : Record<string, any> | Record<string, any>[]
>obj.length : number
>obj : any[] | Record<string, any>[]
>length : number
for (let key in obj) {
>key : string
>obj : any[] | Record<string, any>[]
if (obj[key] !== undefined) {
>obj[key] !== undefined : boolean
>obj[key] : any
>obj : any[] | Record<string, any>[]
>key : string
>undefined : undefined
console.log(obj[key])
>console.log(obj[key]) : void
>console.log : (...data: any[]) => void
>console : Console
>log : (...data: any[]) => void
>obj[key] : any
>obj : any[] | Record<string, any>[]
>key : string
}
}
}
else {
// 'obj' should probably not include an array type here.
for (let key in obj) {
>key : string
>obj : any[] | Record<string, any>
if (obj[key] !== undefined) {
>obj[key] !== undefined : boolean
>obj[key] : any
>obj : any[] | Record<string, any>
>key : string
>undefined : undefined
console.log(obj[key])
>console.log(obj[key]) : void
>console.log : (...data: any[]) => void
>console : Console
>log : (...data: any[]) => void
>obj[key] : any
>obj : any[] | Record<string, any>
>key : string
}
}
}
}
function checksArrayOrObject2(obj: Record<string, any> | Record<string, any>[]) {
>checksArrayOrObject2 : (obj: Record<string, any> | Record<string, any>[]) => void
>obj : Record<string, any> | Record<string, any>[]
if (Array.isArray(obj)) {
>Array.isArray(obj) : boolean
>Array.isArray : (arg: any) => arg is any[]
>Array : ArrayConstructor
>isArray : (arg: any) => arg is any[]
>obj : Record<string, any> | Record<string, any>[]
// obj should only be an array type here
for (let key in obj) {
>key : string
>obj : any[] | Record<string, any>[]
if (obj[key] !== undefined) {
>obj[key] !== undefined : boolean
>obj[key] : any
>obj : any[] | Record<string, any>[]
>key : string
>undefined : undefined
console.log(obj[key])
>console.log(obj[key]) : void
>console.log : (...data: any[]) => void
>console : Console
>log : (...data: any[]) => void
>obj[key] : any
>obj : any[] | Record<string, any>[]
>key : string
}
}
}
else {
// 'obj' should probably not include an array type here.
for (let key in obj) {
>key : string
>obj : Record<string, any>
if (obj[key] !== undefined) {
>obj[key] !== undefined : boolean
>obj[key] : any
>obj : Record<string, any>
>key : string
>undefined : undefined
console.log(obj[key])
>console.log(obj[key]) : void
>console.log : (...data: any[]) => void
>console : Console
>log : (...data: any[]) => void
>obj[key] : any
>obj : Record<string, any>
>key : string
}
}
}
}

View File

@ -28,11 +28,11 @@ const c4 = {};
const a4a = [c4, r4]; // {}[]
const a4b = [r4, c4]; // {}[]
// Check that narrowing preserves original type in false branch for non-identical mutual subtypes
// Check that {} is a strict supertype of Record<string, unknown>
declare function isObject1(value: unknown): value is Record<string, unknown>;
function gg(x: {}) {
function gg1(x: {}) {
if (isObject1(x)) {
x; // Record<string, unknown>
}
@ -46,14 +46,40 @@ declare function isObject2(value: unknown): value is {};
function gg2(x: Record<string, unknown>) {
if (isObject2(x)) {
x; // {}
}
else {
x; // Record<string, unknown>
}
else {
x; // never
}
x; // Record<string, unknown>
}
// Check that {} is a strict supertype of Record<string, any>
declare function isObject3(value: unknown): value is Record<string, any>;
function gg3(x: {}) {
if (isObject3(x)) {
x; // Record<string, any>
}
else {
x; // {}
}
x; // {}
}
declare function isObject4(value: unknown): value is {};
function gg4(x: Record<string, any>) {
if (isObject4(x)) {
x; // Record<string, any>
}
else {
x; // never
}
x; // Record<string, any>
}
// Repro from #50916
type Identity<T> = {[K in keyof T]: T[K]};
@ -77,3 +103,41 @@ function example(x: Union) {
if (is(x)) {}
x; // Union
}
function checksArrayOrObject1(obj: Record<string, any> | Record<string, any>[]) {
// "accidentally" guards the first branch on the length
if (Array.isArray(obj) && obj.length) {
for (let key in obj) {
if (obj[key] !== undefined) {
console.log(obj[key])
}
}
}
else {
// 'obj' should probably not include an array type here.
for (let key in obj) {
if (obj[key] !== undefined) {
console.log(obj[key])
}
}
}
}
function checksArrayOrObject2(obj: Record<string, any> | Record<string, any>[]) {
if (Array.isArray(obj)) {
// obj should only be an array type here
for (let key in obj) {
if (obj[key] !== undefined) {
console.log(obj[key])
}
}
}
else {
// 'obj' should probably not include an array type here.
for (let key in obj) {
if (obj[key] !== undefined) {
console.log(obj[key])
}
}
}
}