Skip to content

Commit

Permalink
Cherry-pick PR #43435 into release-4.3 (#43438)
Browse files Browse the repository at this point in the history
Component commits:
684b039 Types that extend Array or ReadonlyArray are automatically array-like

63c6346 Add React repro to test

Co-authored-by: Andrew Branch <andrew@wheream.io>
  • Loading branch information
TypeScript Bot and andrewbranch authored Mar 30, 2021
1 parent 563592d commit 1625bfb
Show file tree
Hide file tree
Showing 6 changed files with 392 additions and 1 deletion.
8 changes: 7 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19789,7 +19789,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<any>
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 {
Expand Down
48 changes: 48 additions & 0 deletions tests/baselines/reference/excessiveStackDepthFlatArray.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
tests/cases/compiler/index.tsx(35,13): error TS2322: Type '{ key: string; }' is not assignable to type 'HTMLAttributes<HTMLLIElement>'.
Property 'key' does not exist on type 'HTMLAttributes<HTMLLIElement>'.


==== tests/cases/compiler/index.tsx (1 errors) ====
interface MiddlewareArray<T> extends Array<T> {}
declare function configureStore(options: { middleware: MiddlewareArray<any> }): void;

declare const defaultMiddleware: MiddlewareArray<any>;
configureStore({
middleware: [...defaultMiddleware], // Should not error
});

declare namespace React {
type DetailedHTMLProps<E extends HTMLAttributes<T>, T> = E;
interface HTMLAttributes<T> {
children?: ReactNode;
}
type ReactNode = ReactChild | ReactFragment | boolean | null | undefined;
type ReactText = string | number;
type ReactChild = ReactText;
type ReactFragment = {} | ReactNodeArray;
interface ReactNodeArray extends Array<ReactNode> {}
}
declare namespace JSX {
interface IntrinsicElements {
ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>;
li: React.DetailedHTMLProps<React.HTMLAttributes<HTMLLIElement>, HTMLLIElement>;
}
}
declare var React: any;

const Component = () => {
const categories = ['Fruit', 'Vegetables'];

return (
<ul>
<li>All</li>
{categories.map((category) => (
<li key={category}>{category}</li> // Error about 'key' only
~~~
!!! error TS2322: Type '{ key: string; }' is not assignable to type 'HTMLAttributes<HTMLLIElement>'.
!!! error TS2322: Property 'key' does not exist on type 'HTMLAttributes<HTMLLIElement>'.
))}
</ul>
);
};

58 changes: 58 additions & 0 deletions tests/baselines/reference/excessiveStackDepthFlatArray.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//// [index.tsx]
interface MiddlewareArray<T> extends Array<T> {}
declare function configureStore(options: { middleware: MiddlewareArray<any> }): void;

declare const defaultMiddleware: MiddlewareArray<any>;
configureStore({
middleware: [...defaultMiddleware], // Should not error
});

declare namespace React {
type DetailedHTMLProps<E extends HTMLAttributes<T>, T> = E;
interface HTMLAttributes<T> {
children?: ReactNode;
}
type ReactNode = ReactChild | ReactFragment | boolean | null | undefined;
type ReactText = string | number;
type ReactChild = ReactText;
type ReactFragment = {} | ReactNodeArray;
interface ReactNodeArray extends Array<ReactNode> {}
}
declare namespace JSX {
interface IntrinsicElements {
ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>;
li: React.DetailedHTMLProps<React.HTMLAttributes<HTMLLIElement>, HTMLLIElement>;
}
}
declare var React: any;

const Component = () => {
const categories = ['Fruit', 'Vegetables'];

return (
<ul>
<li>All</li>
{categories.map((category) => (
<li key={category}>{category}</li> // Error about 'key' only
))}
</ul>
);
};


//// [index.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)
});
var Component = function () {
var categories = ['Fruit', 'Vegetables'];
return (React.createElement("ul", null,
React.createElement("li", null, "All"),
categories.map(function (category) { return (React.createElement("li", { key: category }, category) // Error about 'key' only
); })));
};
128 changes: 128 additions & 0 deletions tests/baselines/reference/excessiveStackDepthFlatArray.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
=== tests/cases/compiler/index.tsx ===
interface MiddlewareArray<T> extends Array<T> {}
>MiddlewareArray : Symbol(MiddlewareArray, Decl(index.tsx, 0, 0))
>T : Symbol(T, Decl(index.tsx, 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(index.tsx, 0, 26))

declare function configureStore(options: { middleware: MiddlewareArray<any> }): void;
>configureStore : Symbol(configureStore, Decl(index.tsx, 0, 48))
>options : Symbol(options, Decl(index.tsx, 1, 32))
>middleware : Symbol(middleware, Decl(index.tsx, 1, 42))
>MiddlewareArray : Symbol(MiddlewareArray, Decl(index.tsx, 0, 0))

declare const defaultMiddleware: MiddlewareArray<any>;
>defaultMiddleware : Symbol(defaultMiddleware, Decl(index.tsx, 3, 13))
>MiddlewareArray : Symbol(MiddlewareArray, Decl(index.tsx, 0, 0))

configureStore({
>configureStore : Symbol(configureStore, Decl(index.tsx, 0, 48))

middleware: [...defaultMiddleware], // Should not error
>middleware : Symbol(middleware, Decl(index.tsx, 4, 16))
>defaultMiddleware : Symbol(defaultMiddleware, Decl(index.tsx, 3, 13))

});

declare namespace React {
>React : Symbol(React, Decl(index.tsx, 6, 3), Decl(index.tsx, 25, 11))

type DetailedHTMLProps<E extends HTMLAttributes<T>, T> = E;
>DetailedHTMLProps : Symbol(DetailedHTMLProps, Decl(index.tsx, 8, 25))
>E : Symbol(E, Decl(index.tsx, 9, 25))
>HTMLAttributes : Symbol(HTMLAttributes, Decl(index.tsx, 9, 61))
>T : Symbol(T, Decl(index.tsx, 9, 53))
>T : Symbol(T, Decl(index.tsx, 9, 53))
>E : Symbol(E, Decl(index.tsx, 9, 25))

interface HTMLAttributes<T> {
>HTMLAttributes : Symbol(HTMLAttributes, Decl(index.tsx, 9, 61))
>T : Symbol(T, Decl(index.tsx, 10, 27))

children?: ReactNode;
>children : Symbol(HTMLAttributes.children, Decl(index.tsx, 10, 31))
>ReactNode : Symbol(ReactNode, Decl(index.tsx, 12, 3))
}
type ReactNode = ReactChild | ReactFragment | boolean | null | undefined;
>ReactNode : Symbol(ReactNode, Decl(index.tsx, 12, 3))
>ReactChild : Symbol(ReactChild, Decl(index.tsx, 14, 35))
>ReactFragment : Symbol(ReactFragment, Decl(index.tsx, 15, 30))

type ReactText = string | number;
>ReactText : Symbol(ReactText, Decl(index.tsx, 13, 75))

type ReactChild = ReactText;
>ReactChild : Symbol(ReactChild, Decl(index.tsx, 14, 35))
>ReactText : Symbol(ReactText, Decl(index.tsx, 13, 75))

type ReactFragment = {} | ReactNodeArray;
>ReactFragment : Symbol(ReactFragment, Decl(index.tsx, 15, 30))
>ReactNodeArray : Symbol(ReactNodeArray, Decl(index.tsx, 16, 43))

interface ReactNodeArray extends Array<ReactNode> {}
>ReactNodeArray : Symbol(ReactNodeArray, Decl(index.tsx, 16, 43))
>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)
>ReactNode : Symbol(ReactNode, Decl(index.tsx, 12, 3))
}
declare namespace JSX {
>JSX : Symbol(JSX, Decl(index.tsx, 18, 1))

interface IntrinsicElements {
>IntrinsicElements : Symbol(IntrinsicElements, Decl(index.tsx, 19, 23))

ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>;
>ul : Symbol(IntrinsicElements.ul, Decl(index.tsx, 20, 31))
>React : Symbol(React, Decl(index.tsx, 6, 3), Decl(index.tsx, 25, 11))
>DetailedHTMLProps : Symbol(React.DetailedHTMLProps, Decl(index.tsx, 8, 25))
>React : Symbol(React, Decl(index.tsx, 6, 3), Decl(index.tsx, 25, 11))
>HTMLAttributes : Symbol(React.HTMLAttributes, Decl(index.tsx, 9, 61))
>HTMLUListElement : Symbol(HTMLUListElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))
>HTMLUListElement : Symbol(HTMLUListElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))

li: React.DetailedHTMLProps<React.HTMLAttributes<HTMLLIElement>, HTMLLIElement>;
>li : Symbol(IntrinsicElements.li, Decl(index.tsx, 21, 90))
>React : Symbol(React, Decl(index.tsx, 6, 3), Decl(index.tsx, 25, 11))
>DetailedHTMLProps : Symbol(React.DetailedHTMLProps, Decl(index.tsx, 8, 25))
>React : Symbol(React, Decl(index.tsx, 6, 3), Decl(index.tsx, 25, 11))
>HTMLAttributes : Symbol(React.HTMLAttributes, Decl(index.tsx, 9, 61))
>HTMLLIElement : Symbol(HTMLLIElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))
>HTMLLIElement : Symbol(HTMLLIElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))
}
}
declare var React: any;
>React : Symbol(React, Decl(index.tsx, 6, 3), Decl(index.tsx, 25, 11))

const Component = () => {
>Component : Symbol(Component, Decl(index.tsx, 27, 5))

const categories = ['Fruit', 'Vegetables'];
>categories : Symbol(categories, Decl(index.tsx, 28, 7))

return (
<ul>
>ul : Symbol(JSX.IntrinsicElements.ul, Decl(index.tsx, 20, 31))

<li>All</li>
>li : Symbol(JSX.IntrinsicElements.li, Decl(index.tsx, 21, 90))
>li : Symbol(JSX.IntrinsicElements.li, Decl(index.tsx, 21, 90))

{categories.map((category) => (
>categories.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
>categories : Symbol(categories, Decl(index.tsx, 28, 7))
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
>category : Symbol(category, Decl(index.tsx, 33, 23))

<li key={category}>{category}</li> // Error about 'key' only
>li : Symbol(JSX.IntrinsicElements.li, Decl(index.tsx, 21, 90))
>key : Symbol(key, Decl(index.tsx, 34, 11))
>category : Symbol(category, Decl(index.tsx, 33, 23))
>category : Symbol(category, Decl(index.tsx, 33, 23))
>li : Symbol(JSX.IntrinsicElements.li, Decl(index.tsx, 21, 90))

))}
</ul>
>ul : Symbol(JSX.IntrinsicElements.ul, Decl(index.tsx, 20, 31))

);
};

108 changes: 108 additions & 0 deletions tests/baselines/reference/excessiveStackDepthFlatArray.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
=== tests/cases/compiler/index.tsx ===
interface MiddlewareArray<T> extends Array<T> {}
declare function configureStore(options: { middleware: MiddlewareArray<any> }): void;
>configureStore : (options: { middleware: MiddlewareArray<any>;}) => void
>options : { middleware: MiddlewareArray<any>; }
>middleware : MiddlewareArray<any>

declare const defaultMiddleware: MiddlewareArray<any>;
>defaultMiddleware : MiddlewareArray<any>

configureStore({
>configureStore({ middleware: [...defaultMiddleware], // Should not error}) : void
>configureStore : (options: { middleware: MiddlewareArray<any>; }) => void
>{ middleware: [...defaultMiddleware], // Should not error} : { middleware: any[]; }

middleware: [...defaultMiddleware], // Should not error
>middleware : any[]
>[...defaultMiddleware] : any[]
>...defaultMiddleware : any
>defaultMiddleware : MiddlewareArray<any>

});

declare namespace React {
type DetailedHTMLProps<E extends HTMLAttributes<T>, T> = E;
>DetailedHTMLProps : E

interface HTMLAttributes<T> {
children?: ReactNode;
>children : ReactNode
}
type ReactNode = ReactChild | ReactFragment | boolean | null | undefined;
>ReactNode : ReactNode
>null : null

type ReactText = string | number;
>ReactText : ReactText

type ReactChild = ReactText;
>ReactChild : ReactText

type ReactFragment = {} | ReactNodeArray;
>ReactFragment : ReactFragment

interface ReactNodeArray extends Array<ReactNode> {}
}
declare namespace JSX {
interface IntrinsicElements {
ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>;
>ul : React.HTMLAttributes<HTMLUListElement>
>React : any
>React : any

li: React.DetailedHTMLProps<React.HTMLAttributes<HTMLLIElement>, HTMLLIElement>;
>li : React.HTMLAttributes<HTMLLIElement>
>React : any
>React : any
}
}
declare var React: any;
>React : any

const Component = () => {
>Component : () => any
>() => { const categories = ['Fruit', 'Vegetables']; return ( <ul> <li>All</li> {categories.map((category) => ( <li key={category}>{category}</li> // Error about 'key' only ))} </ul> );} : () => any

const categories = ['Fruit', 'Vegetables'];
>categories : string[]
>['Fruit', 'Vegetables'] : string[]
>'Fruit' : "Fruit"
>'Vegetables' : "Vegetables"

return (
>( <ul> <li>All</li> {categories.map((category) => ( <li key={category}>{category}</li> // Error about 'key' only ))} </ul> ) : any

<ul>
><ul> <li>All</li> {categories.map((category) => ( <li key={category}>{category}</li> // Error about 'key' only ))} </ul> : any
>ul : any

<li>All</li>
><li>All</li> : any
>li : any
>li : any

{categories.map((category) => (
>categories.map((category) => ( <li key={category}>{category}</li> // Error about 'key' only )) : any[]
>categories.map : <U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]
>categories : string[]
>map : <U>(callbackfn: (value: string, index: number, array: string[]) => U, thisArg?: any) => U[]
>(category) => ( <li key={category}>{category}</li> // Error about 'key' only ) : (category: string) => any
>category : string
>( <li key={category}>{category}</li> // Error about 'key' only ) : any

<li key={category}>{category}</li> // Error about 'key' only
><li key={category}>{category}</li> : any
>li : any
>key : string
>category : string
>category : string
>li : any

))}
</ul>
>ul : any

);
};

Loading

0 comments on commit 1625bfb

Please sign in to comment.