From f1d5c4338a4b0c634d4317488e4e9710c8ae36f2 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Fri, 2 Sep 2022 21:12:13 -0400 Subject: [PATCH] feat: add `Type#isReadonlyArray()` and `Type#isArray()` also includes readonly arrays Closes #1306 Closes #1305 --- deno/ts_morph.d.ts | 2 + deno/ts_morph.js | 8 +++- packages/ts-morph/lib/ts-morph.d.ts | 2 + packages/ts-morph/src/compiler/types/Type.ts | 14 +++++- .../src/tests/compiler/type/typeTests.ts | 48 ++++++++++++++++++- 5 files changed, 71 insertions(+), 3 deletions(-) diff --git a/deno/ts_morph.d.ts b/deno/ts_morph.d.ts index 51f56590d..6c5a46de7 100644 --- a/deno/ts_morph.d.ts +++ b/deno/ts_morph.d.ts @@ -10125,6 +10125,8 @@ export declare class Type { isAny(): boolean; /** Gets if this is an array type. */ isArray(): boolean; + /** Gets if this is a readonly array type. */ + isReadonlyArray(): boolean; /** Gets if this is a template literal type. */ isTemplateLiteral(): boolean; /** Gets if this is a boolean type. */ diff --git a/deno/ts_morph.js b/deno/ts_morph.js index 8efcdc4b6..804add91b 100644 --- a/deno/ts_morph.js +++ b/deno/ts_morph.js @@ -17984,7 +17984,13 @@ class Type { const symbol = this.getSymbol(); if (symbol == null) return false; - return symbol.getName() === "Array" && this.getTypeArguments().length === 1; + return (symbol.getName() === "Array" || symbol.getName() === "ReadonlyArray") && this.getTypeArguments().length === 1; + } + isReadonlyArray() { + const symbol = this.getSymbol(); + if (symbol == null) + return false; + return symbol.getName() === "ReadonlyArray" && this.getTypeArguments().length === 1; } isTemplateLiteral() { return this._hasTypeFlag(TypeFlags.TemplateLiteral); diff --git a/packages/ts-morph/lib/ts-morph.d.ts b/packages/ts-morph/lib/ts-morph.d.ts index a9df21fab..aedae560b 100644 --- a/packages/ts-morph/lib/ts-morph.d.ts +++ b/packages/ts-morph/lib/ts-morph.d.ts @@ -10125,6 +10125,8 @@ export declare class Type { isAny(): boolean; /** Gets if this is an array type. */ isArray(): boolean; + /** Gets if this is a readonly array type. */ + isReadonlyArray(): boolean; /** Gets if this is a template literal type. */ isTemplateLiteral(): boolean; /** Gets if this is a boolean type. */ diff --git a/packages/ts-morph/src/compiler/types/Type.ts b/packages/ts-morph/src/compiler/types/Type.ts index b8160cf3e..457472b2c 100644 --- a/packages/ts-morph/src/compiler/types/Type.ts +++ b/packages/ts-morph/src/compiler/types/Type.ts @@ -386,7 +386,19 @@ export class Type { const symbol = this.getSymbol(); if (symbol == null) return false; - return symbol.getName() === "Array" && this.getTypeArguments().length === 1; + // this is not bulletproof and should be improved + return (symbol.getName() === "Array" || symbol.getName() === "ReadonlyArray") && this.getTypeArguments().length === 1; + } + + /** + * Gets if this is a readonly array type. + */ + isReadonlyArray() { + const symbol = this.getSymbol(); + if (symbol == null) + return false; + // this is not bulletproof and should be improved + return symbol.getName() === "ReadonlyArray" && this.getTypeArguments().length === 1; } /** diff --git a/packages/ts-morph/src/tests/compiler/type/typeTests.ts b/packages/ts-morph/src/tests/compiler/type/typeTests.ts index 7547f9fba..0b3646f44 100644 --- a/packages/ts-morph/src/tests/compiler/type/typeTests.ts +++ b/packages/ts-morph/src/tests/compiler/type/typeTests.ts @@ -52,6 +52,8 @@ let tupleType: [string]; let tupleTypeMultiple: [string, number]; let genericArrayType: Array; let arrayType: string[]; +let readonlyArrayType: readonly string[]; +let explicitReadonlyArrayType: ReadonlyArray; let arrayTypeOfTuples: [string][]; let undefinedType: undefined; let classType: MyClass; @@ -415,7 +417,7 @@ let unknownType: unknown; doTest("tupleTypeMultiple", true); }); - it("should not be when not an array", () => { + it("should not be when an array", () => { doTest("arrayType", false); }); @@ -428,6 +430,50 @@ let unknownType: unknown; }); }); + describe(nameof("isArray"), () => { + function doTest(typeName: string, expected: boolean) { + expect(typesByName[typeName].isArray()).to.equal(expected); + } + + it("should be when an array", () => { + doTest("arrayType", true); + }); + + it("should be when a readonly array", () => { + doTest("readonlyArrayType", true); + }); + + it("should be when explicitly a readonly array", () => { + doTest("explicitReadonlyArrayType", true); + }); + + it("should not be when not", () => { + doTest("stringType", false); + }); + }); + + describe(nameof("isReadonlyArray"), () => { + function doTest(typeName: string, expected: boolean) { + expect(typesByName[typeName].isReadonlyArray()).to.equal(expected); + } + + it("should not be when an array", () => { + doTest("arrayType", false); + }); + + it("should be when a readonly array", () => { + doTest("readonlyArrayType", true); + }); + + it("should be when explicitly a readonly array", () => { + doTest("explicitReadonlyArrayType", true); + }); + + it("should not be when not", () => { + doTest("stringType", false); + }); + }); + describe(nameof("isUndefined"), () => { function doTest(typeName: string, expected: boolean) { expect(typesByName[typeName].isUndefined()).to.equal(expected);