From 684b03983540b04755b9f21ef206dc87b2b06858 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Mon, 15 Mar 2021 14:37:59 -0700 Subject: [PATCH] Types that extend Array or ReadonlyArray are automatically array-like --- src/compiler/checker.ts | 8 +++++- .../reference/excessiveStackDepthFlatArray.js | 19 ++++++++++++++ .../excessiveStackDepthFlatArray.symbols | 26 +++++++++++++++++++ .../excessiveStackDepthFlatArray.types | 23 ++++++++++++++++ .../compiler/excessiveStackDepthFlatArray.ts | 9 +++++++ 5 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/excessiveStackDepthFlatArray.js create mode 100644 tests/baselines/reference/excessiveStackDepthFlatArray.symbols create mode 100644 tests/baselines/reference/excessiveStackDepthFlatArray.types create mode 100644 tests/cases/compiler/excessiveStackDepthFlatArray.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 625cfabd99b..a5260871b52 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -19703,7 +19703,13 @@ namespace ts { function isArrayLikeType(type: Type): boolean { // A type is array-like if it is a reference to the global Array or global ReadonlyArray type, // or if it is not the undefined or null type and if it is assignable to ReadonlyArray - return isArrayType(type) || !(type.flags & TypeFlags.Nullable) && isTypeAssignableTo(type, anyReadonlyArrayType); + return isArrayType(type) || hasArrayOrReadonlyArrayBaseType(type) || !(type.flags & TypeFlags.Nullable) && isTypeAssignableTo(type, anyReadonlyArrayType); + } + + function hasArrayOrReadonlyArrayBaseType(type: Type): boolean { + return !!(getObjectFlags(type) & ObjectFlags.Reference) + && !!(getObjectFlags((type as TypeReference).target) & ObjectFlags.ClassOrInterface) + && some(getBaseTypes((type as TypeReference).target as InterfaceType), isArrayType); } function isEmptyArrayLiteralType(type: Type): boolean { diff --git a/tests/baselines/reference/excessiveStackDepthFlatArray.js b/tests/baselines/reference/excessiveStackDepthFlatArray.js new file mode 100644 index 00000000000..a8e70171466 --- /dev/null +++ b/tests/baselines/reference/excessiveStackDepthFlatArray.js @@ -0,0 +1,19 @@ +//// [excessiveStackDepthFlatArray.ts] +interface MiddlewareArray extends Array {} +declare function configureStore(options: { middleware: MiddlewareArray }): void; + +declare const defaultMiddleware: MiddlewareArray; +configureStore({ + middleware: [...defaultMiddleware], // Should not error +}); + + +//// [excessiveStackDepthFlatArray.js] +var __spreadArray = (this && this.__spreadArray) || function (to, from) { + for (var i = 0, il = from.length, j = to.length; i < il; i++, j++) + to[j] = from[i]; + return to; +}; +configureStore({ + middleware: __spreadArray([], defaultMiddleware) +}); diff --git a/tests/baselines/reference/excessiveStackDepthFlatArray.symbols b/tests/baselines/reference/excessiveStackDepthFlatArray.symbols new file mode 100644 index 00000000000..9d05616763f --- /dev/null +++ b/tests/baselines/reference/excessiveStackDepthFlatArray.symbols @@ -0,0 +1,26 @@ +=== tests/cases/compiler/excessiveStackDepthFlatArray.ts === +interface MiddlewareArray extends Array {} +>MiddlewareArray : Symbol(MiddlewareArray, Decl(excessiveStackDepthFlatArray.ts, 0, 0)) +>T : Symbol(T, Decl(excessiveStackDepthFlatArray.ts, 0, 26)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 2 more) +>T : Symbol(T, Decl(excessiveStackDepthFlatArray.ts, 0, 26)) + +declare function configureStore(options: { middleware: MiddlewareArray }): void; +>configureStore : Symbol(configureStore, Decl(excessiveStackDepthFlatArray.ts, 0, 48)) +>options : Symbol(options, Decl(excessiveStackDepthFlatArray.ts, 1, 32)) +>middleware : Symbol(middleware, Decl(excessiveStackDepthFlatArray.ts, 1, 42)) +>MiddlewareArray : Symbol(MiddlewareArray, Decl(excessiveStackDepthFlatArray.ts, 0, 0)) + +declare const defaultMiddleware: MiddlewareArray; +>defaultMiddleware : Symbol(defaultMiddleware, Decl(excessiveStackDepthFlatArray.ts, 3, 13)) +>MiddlewareArray : Symbol(MiddlewareArray, Decl(excessiveStackDepthFlatArray.ts, 0, 0)) + +configureStore({ +>configureStore : Symbol(configureStore, Decl(excessiveStackDepthFlatArray.ts, 0, 48)) + + middleware: [...defaultMiddleware], // Should not error +>middleware : Symbol(middleware, Decl(excessiveStackDepthFlatArray.ts, 4, 16)) +>defaultMiddleware : Symbol(defaultMiddleware, Decl(excessiveStackDepthFlatArray.ts, 3, 13)) + +}); + diff --git a/tests/baselines/reference/excessiveStackDepthFlatArray.types b/tests/baselines/reference/excessiveStackDepthFlatArray.types new file mode 100644 index 00000000000..ad02a8e74b6 --- /dev/null +++ b/tests/baselines/reference/excessiveStackDepthFlatArray.types @@ -0,0 +1,23 @@ +=== tests/cases/compiler/excessiveStackDepthFlatArray.ts === +interface MiddlewareArray extends Array {} +declare function configureStore(options: { middleware: MiddlewareArray }): void; +>configureStore : (options: { middleware: MiddlewareArray;}) => void +>options : { middleware: MiddlewareArray; } +>middleware : MiddlewareArray + +declare const defaultMiddleware: MiddlewareArray; +>defaultMiddleware : MiddlewareArray + +configureStore({ +>configureStore({ middleware: [...defaultMiddleware], // Should not error}) : void +>configureStore : (options: { middleware: MiddlewareArray; }) => void +>{ middleware: [...defaultMiddleware], // Should not error} : { middleware: any[]; } + + middleware: [...defaultMiddleware], // Should not error +>middleware : any[] +>[...defaultMiddleware] : any[] +>...defaultMiddleware : any +>defaultMiddleware : MiddlewareArray + +}); + diff --git a/tests/cases/compiler/excessiveStackDepthFlatArray.ts b/tests/cases/compiler/excessiveStackDepthFlatArray.ts new file mode 100644 index 00000000000..fd16b622199 --- /dev/null +++ b/tests/cases/compiler/excessiveStackDepthFlatArray.ts @@ -0,0 +1,9 @@ +// @lib: es2019 + +interface MiddlewareArray extends Array {} +declare function configureStore(options: { middleware: MiddlewareArray }): void; + +declare const defaultMiddleware: MiddlewareArray; +configureStore({ + middleware: [...defaultMiddleware], // Should not error +});