Skip to content

Commit

Permalink
Array.isArray for readonly array
Browse files Browse the repository at this point in the history
  • Loading branch information
graphemecluster committed Aug 3, 2022
1 parent 9439e2f commit d158cbb
Show file tree
Hide file tree
Showing 38 changed files with 173 additions and 141 deletions.
1 change: 1 addition & 0 deletions src/harness/fourslashInterfaceImpl.ts
Expand Up @@ -1173,6 +1173,7 @@ namespace FourSlashInterface {
typeEntry("Record"),
typeEntry("Exclude"),
typeEntry("Extract"),
typeEntry("Cast"),
typeEntry("Omit"),
typeEntry("NonNullable"),
typeEntry("Parameters"),
Expand Down
14 changes: 14 additions & 0 deletions src/lib/es2015.iterable.d.ts
Expand Up @@ -56,6 +56,20 @@ interface Array<T> {
}

interface ArrayConstructor {
/**
* Determines whether an object is an array.
* @param arg The object to test.
*/
isArray<T>(arg: T): arg is Cast<
0 extends (1 & T) ? any[] :
T extends readonly unknown[] ? T :
T extends string ? never :
T extends Iterable<infer U> | ArrayLike<infer U> ? U[] :
unknown extends T ? unknown[] :
never,
T
>;

/**
* Creates an array from an array-like or iterable object.
* @param source An array-like or iterable object to convert to an array.
Expand Down
2 changes: 1 addition & 1 deletion src/lib/es2017.object.d.ts
Expand Up @@ -27,5 +27,5 @@ interface ObjectConstructor {
* Returns an object containing all own property descriptors of an object
* @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object.
*/
getOwnPropertyDescriptors<T extends {}>(o: T): { [P in keyof T]: TypedPropertyDescriptor<T[P]> } & { [x: string]: PropertyDescriptor };
getOwnPropertyDescriptors<T extends {}>(o: T): { [P in keyof T]: TypedPropertyDescriptor<T[P]> } & PropertyDescriptorMap;
}
19 changes: 18 additions & 1 deletion src/lib/es5.d.ts
Expand Up @@ -1495,7 +1495,19 @@ interface ArrayConstructor {
(arrayLength?: number): any[];
<T>(arrayLength: number): T[];
<T>(...items: T[]): T[];
isArray(arg: any): arg is any[];
/**
* Determines whether an object is an array.
* @param arg The object to test.
*/
isArray<T>(arg: T): arg is Cast<
0 extends (1 & T) ? any[] :
T extends readonly unknown[] ? T :
T extends string ? never :
T extends ArrayLike<infer U> ? U[] :
unknown extends T ? unknown[] :
never,
T
>;
readonly prototype: any[];
}

Expand Down Expand Up @@ -1615,6 +1627,11 @@ type Exclude<T, U> = T extends U ? never : T;
*/
type Extract<T, U> = T extends U ? T : never;

/**
* Make sure T is at least assignable to U
*/
type Cast<T, U> = T extends U ? T : U;

/**
* Construct a type with the properties of T except for those in type K.
*/
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/arrayDestructuringInSwitch1.types
Expand Up @@ -11,9 +11,9 @@ export function evaluate(expression: Expression): boolean {

if (Array.isArray(expression)) {
>Array.isArray(expression) : boolean
>Array.isArray : (arg: any) => arg is any[]
>Array.isArray : <T>(arg: T) => arg is Cast<0 extends 1 & T ? any[] : T extends readonly unknown[] ? T : T extends string ? never : T extends ArrayLike<infer U> ? U[] : unknown extends T ? unknown[] : never, T>
>Array : ArrayConstructor
>isArray : (arg: any) => arg is any[]
>isArray : <T>(arg: T) => arg is Cast<0 extends 1 & T ? any[] : T extends readonly unknown[] ? T : T extends string ? never : T extends ArrayLike<infer U> ? U[] : unknown extends T ? unknown[] : never, T>
>expression : Expression

const [operator, ...operands] = expression;
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/arrayTypeOfTypeOf.types
Expand Up @@ -14,11 +14,11 @@ var xs2: typeof Array;
>Array : ArrayConstructor

var xs3: typeof Array<number>;
>xs3 : { (arrayLength: number): number[]; (...items: number[]): number[]; new (arrayLength: number): number[]; new (...items: number[]): number[]; isArray(arg: any): arg is any[]; readonly prototype: any[]; }
>xs3 : { (arrayLength: number): number[]; (...items: number[]): number[]; new (arrayLength: number): number[]; new (...items: number[]): number[]; isArray<T>(arg: T): arg is Cast<0 extends 1 & T ? any[] : T extends readonly unknown[] ? T : T extends string ? never : T extends ArrayLike<infer U> ? U[] : unknown extends T ? unknown[] : never, T>; readonly prototype: any[]; }
>Array : ArrayConstructor

var xs4: typeof Array<typeof x>;
>xs4 : { (arrayLength: number): number[]; (...items: number[]): number[]; new (arrayLength: number): number[]; new (...items: number[]): number[]; isArray(arg: any): arg is any[]; readonly prototype: any[]; }
>xs4 : { (arrayLength: number): number[]; (...items: number[]): number[]; new (arrayLength: number): number[]; new (...items: number[]): number[]; isArray<T>(arg: T): arg is Cast<0 extends 1 & T ? any[] : T extends readonly unknown[] ? T : T extends string ? never : T extends ArrayLike<infer U> ? U[] : unknown extends T ? unknown[] : never, T>; readonly prototype: any[]; }
>Array : ArrayConstructor
>x : number

Expand Up @@ -79,9 +79,9 @@ export const updateIfChanged = <T>(t: T) => {
>Object.assign : Symbol(ObjectConstructor.assign, Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --))
>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>assign : Symbol(ObjectConstructor.assign, Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --))
>Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --))
>Array.isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --))
>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, --, --))
>isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --))
>isArray : Symbol(ArrayConstructor.isArray, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --))
>u : Symbol(u, Decl(declarationsWithRecursiveInternalTypesProduceUniqueTypeParams.ts, 7, 23))
>u : Symbol(u, Decl(declarationsWithRecursiveInternalTypesProduceUniqueTypeParams.ts, 7, 23))
>[key] : Symbol([key], Decl(declarationsWithRecursiveInternalTypesProduceUniqueTypeParams.ts, 12, 80))
Expand Down
Expand Up @@ -67,9 +67,9 @@ export const updateIfChanged = <T>(t: T) => {
>assign : { <T extends {}>(target: T): T; <T extends {}, U>(target: T, source: U): T & Writable<U>; <T extends {}, U, V>(target: T, source1: U, source2: V): T & Writable<U> & Writable<V>; <T extends {}, U, V, W>(target: T, source1: U, source2: V, source3: W): T & Writable<U> & Writable<V> & Writable<W>; (target: {}, ...sources: any[]): any; }
>Array.isArray(u) ? [] : {} : undefined[] | {}
>Array.isArray(u) : boolean
>Array.isArray : (arg: any) => arg is any[]
>Array.isArray : { <T>(arg: T): arg is Cast<0 extends 1 & T ? any[] : T extends readonly unknown[] ? T : T extends string ? never : T extends ArrayLike<infer U> ? U[] : unknown extends T ? unknown[] : never, T>; <T>(arg: T): arg is Cast<0 extends 1 & T ? any[] : T extends readonly unknown[] ? T : T extends string ? never : T extends Iterable<infer U> | ArrayLike<infer U> ? U[] : unknown extends T ? unknown[] : never, T>; }
>Array : ArrayConstructor
>isArray : (arg: any) => arg is any[]
>isArray : { <T>(arg: T): arg is Cast<0 extends 1 & T ? any[] : T extends readonly unknown[] ? T : T extends string ? never : T extends ArrayLike<infer U> ? U[] : unknown extends T ? unknown[] : never, T>; <T>(arg: T): arg is Cast<0 extends 1 & T ? any[] : T extends readonly unknown[] ? T : T extends string ? never : T extends Iterable<infer U> | ArrayLike<infer U> ? U[] : unknown extends T ? unknown[] : never, T>; }
>u : U
>[] : undefined[]
>{} : {}
Expand Down
Expand Up @@ -41,7 +41,7 @@ tests/cases/conformance/es6/destructuring/destructuringParameterDeclaration4.ts(
a1(...array2); // Error parameter type is (number|string)[]
~~~~~~
!!! error TS2552: Cannot find name 'array2'. Did you mean 'Array'?
!!! related TS2728 /.ts/lib.es5.d.ts:1522:13: 'Array' is declared here.
!!! related TS2728 /.ts/lib.es5.d.ts:1534:13: 'Array' is declared here.
a5([1, 2, "string", false, true]); // Error, parameter type is [any, any, [[any]]]
~~~~~~~~
!!! error TS2322: Type 'string' is not assignable to type '[[any]]'.
Expand Down
8 changes: 4 additions & 4 deletions tests/baselines/reference/fixSignatureCaching.types
Expand Up @@ -1109,9 +1109,9 @@ define(function () {
>Array : ArrayConstructor

Array.isArray : function (value) { return Object.prototype.toString.call(value) === '[object Array]'; };
>Array.isArray : (arg: any) => arg is any[]
>Array.isArray : <T>(arg: T) => arg is Cast<0 extends 1 & T ? any[] : T extends readonly unknown[] ? T : T extends string ? never : T extends ArrayLike<infer U> ? U[] : unknown extends T ? unknown[] : never, T>
>Array : ArrayConstructor
>isArray : (arg: any) => arg is any[]
>isArray : <T>(arg: T) => arg is Cast<0 extends 1 & T ? any[] : T extends readonly unknown[] ? T : T extends string ? never : T extends ArrayLike<infer U> ? U[] : unknown extends T ? unknown[] : never, T>
>function (value) { return Object.prototype.toString.call(value) === '[object Array]'; } : (value: any) => boolean
>value : any
>Object.prototype.toString.call(value) === '[object Array]' : boolean
Expand Down Expand Up @@ -1150,9 +1150,9 @@ define(function () {
>'[object Array]' : "[object Array]"

: Array.isArray;
>Array.isArray : (arg: any) => arg is any[]
>Array.isArray : <T>(arg: T) => arg is Cast<0 extends 1 & T ? any[] : T extends readonly unknown[] ? T : T extends string ? never : T extends ArrayLike<infer U> ? U[] : unknown extends T ? unknown[] : never, T>
>Array : ArrayConstructor
>isArray : (arg: any) => arg is any[]
>isArray : <T>(arg: T) => arg is Cast<0 extends 1 & T ? any[] : T extends readonly unknown[] ? T : T extends string ? never : T extends ArrayLike<infer U> ? U[] : unknown extends T ? unknown[] : never, T>

function equalIC(a, b) {
>equalIC : (a: any, b: any) => boolean
Expand Down
12 changes: 6 additions & 6 deletions tests/baselines/reference/instantiationExpressions.types
Expand Up @@ -61,13 +61,13 @@ function f2() {
>Array : ArrayConstructor

const A1 = Array<string>; // new (...) => string[]
>A1 : { (arrayLength: number): string[]; (...items: string[]): string[]; new (arrayLength: number): string[]; new (...items: string[]): string[]; isArray(arg: any): arg is any[]; readonly prototype: any[]; }
>Array<string> : { (arrayLength: number): string[]; (...items: string[]): string[]; new (arrayLength: number): string[]; new (...items: string[]): string[]; isArray(arg: any): arg is any[]; readonly prototype: any[]; }
>A1 : { (arrayLength: number): string[]; (...items: string[]): string[]; new (arrayLength: number): string[]; new (...items: string[]): string[]; isArray<T>(arg: T): arg is Cast<0 extends 1 & T ? any[] : T extends readonly unknown[] ? T : T extends string ? never : T extends ArrayLike<infer U> ? U[] : unknown extends T ? unknown[] : never, T>; readonly prototype: any[]; }
>Array<string> : { (arrayLength: number): string[]; (...items: string[]): string[]; new (arrayLength: number): string[]; new (...items: string[]): string[]; isArray<T>(arg: T): arg is Cast<0 extends 1 & T ? any[] : T extends readonly unknown[] ? T : T extends string ? never : T extends ArrayLike<infer U> ? U[] : unknown extends T ? unknown[] : never, T>; readonly prototype: any[]; }
>Array : ArrayConstructor

const A2 = Array<string, number>; // Error
>A2 : { isArray(arg: any): arg is any[]; readonly prototype: any[]; }
>Array<string, number> : { isArray(arg: any): arg is any[]; readonly prototype: any[]; }
>A2 : { isArray<T>(arg: T): arg is Cast<0 extends 1 & T ? any[] : T extends readonly unknown[] ? T : T extends string ? never : T extends ArrayLike<infer U> ? U[] : unknown extends T ? unknown[] : never, T>; readonly prototype: any[]; }
>Array<string, number> : { isArray<T>(arg: T): arg is Cast<0 extends 1 & T ? any[] : T extends readonly unknown[] ? T : T extends string ? never : T extends ArrayLike<infer U> ? U[] : unknown extends T ? unknown[] : never, T>; readonly prototype: any[]; }
>Array : ArrayConstructor
}

Expand All @@ -76,11 +76,11 @@ type T20 = typeof Array<>; // Error
>Array : ArrayConstructor

type T21 = typeof Array<string>; // new (...) => string[]
>T21 : { (arrayLength: number): string[]; (...items: string[]): string[]; new (arrayLength: number): string[]; new (...items: string[]): string[]; isArray(arg: any): arg is any[]; readonly prototype: any[]; }
>T21 : { (arrayLength: number): string[]; (...items: string[]): string[]; new (arrayLength: number): string[]; new (...items: string[]): string[]; isArray<T>(arg: T): arg is Cast<0 extends 1 & T ? any[] : T extends readonly unknown[] ? T : T extends string ? never : T extends ArrayLike<infer U> ? U[] : unknown extends T ? unknown[] : never, T>; readonly prototype: any[]; }
>Array : ArrayConstructor

type T22 = typeof Array<string, number>; // Error
>T22 : { isArray(arg: any): arg is any[]; readonly prototype: any[]; }
>T22 : { isArray<T>(arg: T): arg is Cast<0 extends 1 & T ? any[] : T extends readonly unknown[] ? T : T extends string ? never : T extends ArrayLike<infer U> ? U[] : unknown extends T ? unknown[] : never, T>; readonly prototype: any[]; }
>Array : ArrayConstructor

declare class C<T> {
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/isArray.js
@@ -1,5 +1,5 @@
//// [isArray.ts]
var maybeArray: number | number[];
var maybeArray: number | number[] | readonly string[];


if (Array.isArray(maybeArray)) {
Expand Down
6 changes: 3 additions & 3 deletions tests/baselines/reference/isArray.symbols
@@ -1,5 +1,5 @@
=== tests/cases/compiler/isArray.ts ===
var maybeArray: number | number[];
var maybeArray: number | number[] | readonly string[];
>maybeArray : Symbol(maybeArray, Decl(isArray.ts, 0, 3))


Expand All @@ -10,9 +10,9 @@ if (Array.isArray(maybeArray)) {
>maybeArray : Symbol(maybeArray, Decl(isArray.ts, 0, 3))

maybeArray.length; // OK
>maybeArray.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
>maybeArray.length : Symbol(length, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
>maybeArray : Symbol(maybeArray, Decl(isArray.ts, 0, 3))
>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --))
>length : Symbol(length, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
}
else {
maybeArray.toFixed(); // OK
Expand Down
12 changes: 6 additions & 6 deletions tests/baselines/reference/isArray.types
@@ -1,18 +1,18 @@
=== tests/cases/compiler/isArray.ts ===
var maybeArray: number | number[];
>maybeArray : number | number[]
var maybeArray: number | number[] | readonly string[];
>maybeArray : number | number[] | readonly string[]


if (Array.isArray(maybeArray)) {
>Array.isArray(maybeArray) : boolean
>Array.isArray : (arg: any) => arg is any[]
>Array.isArray : <T>(arg: T) => arg is Cast<0 extends 1 & T ? any[] : T extends readonly unknown[] ? T : T extends string ? never : T extends ArrayLike<infer U> ? U[] : unknown extends T ? unknown[] : never, T>
>Array : ArrayConstructor
>isArray : (arg: any) => arg is any[]
>maybeArray : number | number[]
>isArray : <T>(arg: T) => arg is Cast<0 extends 1 & T ? any[] : T extends readonly unknown[] ? T : T extends string ? never : T extends ArrayLike<infer U> ? U[] : unknown extends T ? unknown[] : never, T>
>maybeArray : number | number[] | readonly string[]

maybeArray.length; // OK
>maybeArray.length : number
>maybeArray : number[]
>maybeArray : number[] | readonly string[]
>length : number
}
else {
Expand Down
@@ -1,6 +1,6 @@
/src/a.js(10,7): error TS2417: Class static side 'typeof ElementsArray' incorrectly extends base class static side '{ isArray(arg: any): arg is any[]; readonly prototype: any[]; }'.
/src/a.js(10,7): error TS2417: Class static side 'typeof ElementsArray' incorrectly extends base class static side '{ isArray<T>(arg: T): arg is Cast<0 extends 1 & T ? any[] : T extends readonly unknown[] ? T : T extends string ? never : T extends ArrayLike<infer U> ? U[] : unknown extends T ? unknown[] : never, T>; readonly prototype: any[]; }'.
Types of property 'isArray' are incompatible.
Type '(arg: any) => boolean' is not assignable to type '(arg: any) => arg is any[]'.
Type '(arg: any) => boolean' is not assignable to type '<T>(arg: T) => arg is Cast<0 extends 1 & T ? any[] : T extends readonly unknown[] ? T : T extends string ? never : T extends ArrayLike<infer U> ? U[] : unknown extends T ? unknown[] : never, T>'.
Signature '(arg: any): boolean' must be a type predicate.


Expand All @@ -16,9 +16,9 @@
// GH#46468
class ElementsArray extends Array {
~~~~~~~~~~~~~
!!! error TS2417: Class static side 'typeof ElementsArray' incorrectly extends base class static side '{ isArray(arg: any): arg is any[]; readonly prototype: any[]; }'.
!!! error TS2417: Class static side 'typeof ElementsArray' incorrectly extends base class static side '{ isArray<T>(arg: T): arg is Cast<0 extends 1 & T ? any[] : T extends readonly unknown[] ? T : T extends string ? never : T extends ArrayLike<infer U> ? U[] : unknown extends T ? unknown[] : never, T>; readonly prototype: any[]; }'.
!!! error TS2417: Types of property 'isArray' are incompatible.
!!! error TS2417: Type '(arg: any) => boolean' is not assignable to type '(arg: any) => arg is any[]'.
!!! error TS2417: Type '(arg: any) => boolean' is not assignable to type '<T>(arg: T) => arg is Cast<0 extends 1 & T ? any[] : T extends readonly unknown[] ? T : T extends string ? never : T extends ArrayLike<infer U> ? U[] : unknown extends T ? unknown[] : never, T>'.
!!! error TS2417: Signature '(arg: any): boolean' must be a type predicate.
static {
const superisArray = super.isArray;
Expand Down
Expand Up @@ -25,17 +25,17 @@ class ElementsArray extends Array {

static {
const superisArray = super.isArray;
>superisArray : (arg: any) => arg is any[]
>super.isArray : (arg: any) => arg is any[]
>superisArray : <T>(arg: T) => arg is Cast<0 extends 1 & T ? any[] : T extends readonly unknown[] ? T : T extends string ? never : T extends ArrayLike<infer U> ? U[] : unknown extends T ? unknown[] : never, T>
>super.isArray : <T>(arg: T) => arg is Cast<0 extends 1 & T ? any[] : T extends readonly unknown[] ? T : T extends string ? never : T extends ArrayLike<infer U> ? U[] : unknown extends T ? unknown[] : never, T>
>super : ArrayConstructor
>isArray : (arg: any) => arg is any[]
>isArray : <T>(arg: T) => arg is Cast<0 extends 1 & T ? any[] : T extends readonly unknown[] ? T : T extends string ? never : T extends ArrayLike<infer U> ? U[] : unknown extends T ? unknown[] : never, T>

const customIsArray = (arg)=> superisArray(arg);
>customIsArray : (arg: any) => boolean
>(arg)=> superisArray(arg) : (arg: any) => boolean
>arg : any
>superisArray(arg) : boolean
>superisArray : (arg: any) => arg is any[]
>superisArray : <T>(arg: T) => arg is Cast<0 extends 1 & T ? any[] : T extends readonly unknown[] ? T : T extends string ? never : T extends ArrayLike<infer U> ? U[] : unknown extends T ? unknown[] : never, T>
>arg : any

this.isArray = customIsArray;
Expand Down

0 comments on commit d158cbb

Please sign in to comment.