diff --git a/CHANGELOG.md b/CHANGELOG.md index b408c9c4a..1290b7cf2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,13 @@ - Added support for TypeScript 4.7, #1935. - Support enum-like objects with numeric literal values tagged with `@enum`, #1918. +- Enum member reflections will now have their `type` set to either a `LiteralType` with a string or numeric value or an `IntrinsicType` with type `number`, #1942. + Using `defaultValue` on `EnumMember` reflections is now deprecated, and will be broken in 0.23. ### Bug Fixes - Fixed invalid type output in some uncommon edge cases, TypeDoc also now renders fewer superfluous parenthesis when creating types. +- TypeDoc is now more consistent about ordering with `enum-value-ascending` or `enum-value-descending` sort strategies in mixed string/number enums. ### Thanks! @@ -844,7 +847,7 @@ - isExternal flag wasn't set properly - JSON schema had incorrect value types, closes #1389 - Hidden module-namespaces, closes #1396 -- Some issues with inheritence +- Some issues with inheritance - We pick up all properties now - Support for specify a directory as an entry point - Lint @@ -963,7 +966,7 @@ - Missed a test configuration update - Rename external modules to modules, closes #109 - Check for compiler errors before converting -- Moved @types/minimatch dependency to devDepencencies (#1206) +- Moved @types/minimatch dependency to devDependencies (#1206) - Plugin resolution for relative paths (#1194), closes #1188 ### Thanks! diff --git a/scripts/rebuild_specs.js b/scripts/rebuild_specs.js index d4092e4c8..6f5e20066 100644 --- a/scripts/rebuild_specs.js +++ b/scripts/rebuild_specs.js @@ -85,9 +85,10 @@ function rebuildConverterTests(dirs) { ); const serialized = app.serializer.toObject(result); - const data = JSON.stringify(serialized, null, " ") - .split(TypeDoc.normalizePath(base)) - .join("%BASE%"); + const data = + JSON.stringify(serialized, null, " ") + .split(TypeDoc.normalizePath(base)) + .join("%BASE%") + "\n"; after(); fs.writeFileSync(out.replace("dist", "src"), data); } diff --git a/src/lib/converter/symbols.ts b/src/lib/converter/symbols.ts index c5f10c913..9e246b342 100644 --- a/src/lib/converter/symbols.ts +++ b/src/lib/converter/symbols.ts @@ -2,6 +2,8 @@ import * as assert from "assert"; import * as ts from "typescript"; import { DeclarationReflection, + IntrinsicType, + LiteralType, ReferenceReflection, Reflection, ReflectionFlag, @@ -245,11 +247,18 @@ function convertEnumMember( exportSymbol ); - reflection.defaultValue = JSON.stringify( - context.checker.getConstantValue( - symbol.getDeclarations()![0] as ts.EnumMember - ) + const defaultValue = context.checker.getConstantValue( + symbol.getDeclarations()![0] as ts.EnumMember ); + reflection.defaultValue = JSON.stringify(defaultValue); + + if (defaultValue !== undefined) { + reflection.type = new LiteralType(defaultValue); + } else { + // We know this has to be a number, because computed values aren't allowed + // in string enums, so otherwise we would have to have the constant value + reflection.type = new IntrinsicType("number"); + } context.finalizeDeclarationReflection(reflection, symbol, exportSymbol); } @@ -657,16 +666,10 @@ function convertProperty( } reflection.defaultValue = declaration && convertDefaultValue(declaration); - // FIXME: This is a horrible hack because getTypeOfSymbol is not exposed. - // The right solution here is probably to keep track of parent nodes... - // but that's tricky because not every reflection is guaranteed to have a - // parent node. This will probably break in a future TS version. reflection.type = context.converter.convertType( context, (context.isConvertingTypeNode() ? parameterType : void 0) ?? - context.checker.getTypeOfSymbolAtLocation(symbol, { - kind: ts.SyntaxKind.SourceFile, - } as any) + context.checker.getTypeOfSymbol(symbol) ); if (reflection.flags.isOptional) { @@ -896,6 +899,7 @@ function convertVariableAsEnum( assert(propType.isStringLiteral() || propType.isNumberLiteral()); reflection.defaultValue = JSON.stringify(propType.value); + reflection.type = new LiteralType(propType.value); rc.finalizeDeclarationReflection(reflection, prop, void 0); } diff --git a/src/lib/models/reflections/declaration.ts b/src/lib/models/reflections/declaration.ts index bcd8d1e54..a589f98c6 100644 --- a/src/lib/models/reflections/declaration.ts +++ b/src/lib/models/reflections/declaration.ts @@ -79,6 +79,8 @@ export class DeclarationReflection extends ContainerReflection { * The default value of this reflection. * * Applies to function parameters. + * + * Note: Using this for enum members is DEPRECATED and will be removed in 0.23. */ defaultValue?: string; diff --git a/src/lib/output/themes/default/partials/member.declaration.tsx b/src/lib/output/themes/default/partials/member.declaration.tsx index a2012091b..564c9601d 100644 --- a/src/lib/output/themes/default/partials/member.declaration.tsx +++ b/src/lib/output/themes/default/partials/member.declaration.tsx @@ -1,4 +1,4 @@ -import { DeclarationReflection, ReflectionType } from "../../../../models"; +import { DeclarationReflection, ReflectionKind, ReflectionType } from "../../../../models"; import { JSX } from "../../../../utils"; import { renderTypeParametersSignature, wbr } from "../../lib"; import type { DefaultThemeRenderContext } from "../DefaultThemeRenderContext"; @@ -14,7 +14,7 @@ export const memberDeclaration = (context: DefaultThemeRenderContext, props: Dec {context.type(props.type)} )} - {!!props.defaultValue && ( + {!!props.defaultValue && props.kind !== ReflectionKind.EnumMember && ( <> {" = "} diff --git a/src/lib/types/ts-internal/index.d.ts b/src/lib/types/ts-internal/index.d.ts index f3b50c680..76ff46700 100644 --- a/src/lib/types/ts-internal/index.d.ts +++ b/src/lib/types/ts-internal/index.d.ts @@ -20,6 +20,9 @@ declare module "typescript" { getTypePredicateOfSignature( signature: ts.Signature ): ts.TypePredicate | undefined; + + //https://github.com/microsoft/TypeScript/blob/v4.7.2/src/compiler/types.ts#L4188 + getTypeOfSymbol(symbol: Symbol): Type; } export interface Signature { diff --git a/src/lib/utils/sort.ts b/src/lib/utils/sort.ts index 0fb40a6a1..d06afe635 100644 --- a/src/lib/utils/sort.ts +++ b/src/lib/utils/sort.ts @@ -5,6 +5,7 @@ import { ReflectionKind } from "../models/reflections/kind"; import type { DeclarationReflection } from "../models/reflections/declaration"; +import { LiteralType } from "../models"; export const SORT_STRATEGIES = [ "source-order", @@ -59,10 +60,12 @@ const sorts: Record< a.kind == ReflectionKind.EnumMember && b.kind == ReflectionKind.EnumMember ) { - return ( - parseFloat(a.defaultValue ?? "0") < - parseFloat(b.defaultValue ?? "0") - ); + const aValue = + a.type instanceof LiteralType ? a.type.value : -Infinity; + const bValue = + b.type instanceof LiteralType ? b.type.value : -Infinity; + + return aValue! < bValue!; } return false; }, @@ -71,10 +74,12 @@ const sorts: Record< a.kind == ReflectionKind.EnumMember && b.kind == ReflectionKind.EnumMember ) { - return ( - parseFloat(b.defaultValue ?? "0") < - parseFloat(a.defaultValue ?? "0") - ); + const aValue = + a.type instanceof LiteralType ? a.type.value : -Infinity; + const bValue = + b.type instanceof LiteralType ? b.type.value : -Infinity; + + return bValue! < aValue!; } return false; }, @@ -138,11 +143,11 @@ const sorts: Record< }; export function sortReflections( - strategies: DeclarationReflection[], - strats: readonly SortStrategy[] + reflections: DeclarationReflection[], + strategies: readonly SortStrategy[] ) { - strategies.sort((a, b) => { - for (const s of strats) { + reflections.sort((a, b) => { + for (const s of strategies) { if (sorts[s](a, b)) { return -1; } diff --git a/src/test/behaviorTests.ts b/src/test/behaviorTests.ts index 4e34d7e0d..5f5179a77 100644 --- a/src/test/behaviorTests.ts +++ b/src/test/behaviorTests.ts @@ -1,6 +1,7 @@ import { deepStrictEqual as equal, ok } from "assert"; import { DeclarationReflection, + LiteralType, ProjectReflection, ReflectionKind, } from "../lib/models"; @@ -25,6 +26,7 @@ export const behaviorTests: Record< "SomeEnumLikeTagged" ); const A = query(project, "SomeEnumLikeTagged.a"); + equal(A.type, new LiteralType("a")); equal(A.defaultValue, '"a"'); const ManualEnum = query(project, "ManualEnum"); @@ -56,6 +58,7 @@ export const behaviorTests: Record< "SomeEnumLikeTaggedNumeric" ); const B = query(project, "SomeEnumLikeTaggedNumeric.b"); + equal(B.type, new LiteralType(1)); equal(B.defaultValue, "1"); const ManualEnumNumeric = query(project, "ManualEnumNumeric"); diff --git a/src/test/converter/comment/specs.json b/src/test/converter/comment/specs.json index 81d9e0f2e..08cc145d0 100644 --- a/src/test/converter/comment/specs.json +++ b/src/test/converter/comment/specs.json @@ -361,4 +361,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/src/test/converter/declaration/specs.json b/src/test/converter/declaration/specs.json index 0572bfe1c..9c0eb4ff5 100644 --- a/src/test/converter/declaration/specs.json +++ b/src/test/converter/declaration/specs.json @@ -241,4 +241,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/src/test/converter/enum/specs.json b/src/test/converter/enum/specs.json index e6173eb1f..979f859bc 100644 --- a/src/test/converter/enum/specs.json +++ b/src/test/converter/enum/specs.json @@ -88,6 +88,10 @@ "kind": 16, "kindString": "Enumeration Member", "flags": {}, + "type": { + "type": "literal", + "value": 1 + }, "defaultValue": "1" }, { @@ -96,6 +100,10 @@ "kind": 16, "kindString": "Enumeration Member", "flags": {}, + "type": { + "type": "literal", + "value": 2 + }, "defaultValue": "2" } ], @@ -129,6 +137,10 @@ "comment": { "shortText": "This is the first enum member." }, + "type": { + "type": "literal", + "value": 1 + }, "defaultValue": "1" }, { @@ -140,6 +152,10 @@ "comment": { "shortText": "This is the second enum member." }, + "type": { + "type": "literal", + "value": 2 + }, "defaultValue": "2" }, { @@ -151,6 +167,10 @@ "comment": { "shortText": "This is the third enum member." }, + "type": { + "type": "literal", + "value": 4 + }, "defaultValue": "4" } ], @@ -185,6 +205,10 @@ "comment": { "shortText": "This is the first enum member." }, + "type": { + "type": "literal", + "value": 1 + }, "defaultValue": "1" }, { @@ -196,6 +220,10 @@ "comment": { "shortText": "This is the second enum member." }, + "type": { + "type": "literal", + "value": 2 + }, "defaultValue": "2" }, { @@ -207,6 +235,10 @@ "comment": { "shortText": "This is the third enum member." }, + "type": { + "type": "literal", + "value": 4 + }, "defaultValue": "4" } ], diff --git a/src/test/converter/enum/specs.nodoc.json b/src/test/converter/enum/specs.nodoc.json index e6173eb1f..979f859bc 100644 --- a/src/test/converter/enum/specs.nodoc.json +++ b/src/test/converter/enum/specs.nodoc.json @@ -88,6 +88,10 @@ "kind": 16, "kindString": "Enumeration Member", "flags": {}, + "type": { + "type": "literal", + "value": 1 + }, "defaultValue": "1" }, { @@ -96,6 +100,10 @@ "kind": 16, "kindString": "Enumeration Member", "flags": {}, + "type": { + "type": "literal", + "value": 2 + }, "defaultValue": "2" } ], @@ -129,6 +137,10 @@ "comment": { "shortText": "This is the first enum member." }, + "type": { + "type": "literal", + "value": 1 + }, "defaultValue": "1" }, { @@ -140,6 +152,10 @@ "comment": { "shortText": "This is the second enum member." }, + "type": { + "type": "literal", + "value": 2 + }, "defaultValue": "2" }, { @@ -151,6 +167,10 @@ "comment": { "shortText": "This is the third enum member." }, + "type": { + "type": "literal", + "value": 4 + }, "defaultValue": "4" } ], @@ -185,6 +205,10 @@ "comment": { "shortText": "This is the first enum member." }, + "type": { + "type": "literal", + "value": 1 + }, "defaultValue": "1" }, { @@ -196,6 +220,10 @@ "comment": { "shortText": "This is the second enum member." }, + "type": { + "type": "literal", + "value": 2 + }, "defaultValue": "2" }, { @@ -207,6 +235,10 @@ "comment": { "shortText": "This is the third enum member." }, + "type": { + "type": "literal", + "value": 4 + }, "defaultValue": "4" } ], diff --git a/src/test/converter/function/specs.json b/src/test/converter/function/specs.json index ee6cc342a..084d19e94 100644 --- a/src/test/converter/function/specs.json +++ b/src/test/converter/function/specs.json @@ -2002,4 +2002,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/src/test/converter/inherit-param-doc/specs.json b/src/test/converter/inherit-param-doc/specs.json index dabcd437c..968d0f64b 100644 --- a/src/test/converter/inherit-param-doc/specs.json +++ b/src/test/converter/inherit-param-doc/specs.json @@ -452,4 +452,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/src/test/converter/inheritance/specs.json b/src/test/converter/inheritance/specs.json index b913ba82f..9b224c8e8 100644 --- a/src/test/converter/inheritance/specs.json +++ b/src/test/converter/inheritance/specs.json @@ -659,4 +659,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/src/test/converter/react/specs.json b/src/test/converter/react/specs.json index b1bca70d3..df6f1898a 100644 --- a/src/test/converter/react/specs.json +++ b/src/test/converter/react/specs.json @@ -163,4 +163,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/src/test/converter/variables/specs.json b/src/test/converter/variables/specs.json index 7456c00ac..0992984f6 100644 --- a/src/test/converter/variables/specs.json +++ b/src/test/converter/variables/specs.json @@ -1408,4 +1408,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/src/test/converter/variables/specs.nodoc.json b/src/test/converter/variables/specs.nodoc.json index b6f9e6e33..532968927 100644 --- a/src/test/converter/variables/specs.nodoc.json +++ b/src/test/converter/variables/specs.nodoc.json @@ -423,4 +423,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/src/test/converter2/issues/gh1942.ts b/src/test/converter2/issues/gh1942.ts new file mode 100644 index 000000000..f7ba9cc31 --- /dev/null +++ b/src/test/converter2/issues/gh1942.ts @@ -0,0 +1,8 @@ +export enum Foo { + A, + B = Math.random(), +} + +export enum Bar { + C = "C", +} diff --git a/src/test/issueTests.ts b/src/test/issueTests.ts index c20afd8ea..ead65d591 100644 --- a/src/test/issueTests.ts +++ b/src/test/issueTests.ts @@ -9,6 +9,8 @@ import { Comment, CommentTag, UnionType, + LiteralType, + IntrinsicType, } from "../lib/models"; import { getConverter2App } from "./programs"; import type { TestLogger } from "./TestLogger"; @@ -378,4 +380,10 @@ export const issueTests: { logger.discardDebugMessages(); logger.expectNoOtherMessages(); }, + + gh1942(project) { + equal(query(project, "Foo.A").type, new LiteralType(0)); + equal(query(project, "Foo.B").type, new IntrinsicType("number")); + equal(query(project, "Bar.C").type, new LiteralType("C")); + }, }; diff --git a/src/test/utils/sort.test.ts b/src/test/utils/sort.test.ts index 562144b2a..3c89e78fd 100644 --- a/src/test/utils/sort.test.ts +++ b/src/test/utils/sort.test.ts @@ -1,6 +1,7 @@ import { deepStrictEqual as equal } from "assert"; import { DeclarationReflection, + LiteralType, ProjectReflection, ReflectionFlag, ReflectionKind, @@ -29,9 +30,9 @@ describe("Sort", () => { new DeclarationReflection("b", ReflectionKind.EnumMember), new DeclarationReflection("c", ReflectionKind.EnumMember), ]; - arr[0].defaultValue = "123"; - arr[1].defaultValue = "12"; - arr[2].defaultValue = "3"; + arr[0].type = new LiteralType(123); + arr[1].type = new LiteralType(12); + arr[2].type = new LiteralType(3); sortReflections(arr, ["enum-value-ascending"]); equal( @@ -46,9 +47,9 @@ describe("Sort", () => { new DeclarationReflection("b", ReflectionKind.EnumMember), new DeclarationReflection("c", ReflectionKind.EnumMember), ]; - arr[0].defaultValue = "123"; - arr[1].defaultValue = "12"; - arr[2].defaultValue = "3"; + arr[0].type = new LiteralType(123); + arr[1].type = new LiteralType(12); + arr[2].type = new LiteralType(3); sortReflections(arr, ["enum-value-ascending"]); equal( @@ -63,9 +64,9 @@ describe("Sort", () => { new DeclarationReflection("b", ReflectionKind.EnumMember), new DeclarationReflection("c", ReflectionKind.EnumMember), ]; - arr[0].defaultValue = "123"; - arr[1].defaultValue = "12"; - arr[2].defaultValue = "3"; + arr[0].type = new LiteralType(123); + arr[1].type = new LiteralType(12); + arr[2].type = new LiteralType(3); sortReflections(arr, ["enum-value-descending"]); equal( @@ -80,9 +81,9 @@ describe("Sort", () => { new DeclarationReflection("a", ReflectionKind.EnumMember), new DeclarationReflection("b", ReflectionKind.EnumMember), ]; - arr[0].defaultValue = "123"; - arr[1].defaultValue = "-1"; - arr[2].defaultValue = "3"; + arr[0].type = new LiteralType(123); + arr[1].type = new LiteralType(-1); + arr[2].type = new LiteralType(3); sortReflections(arr, ["enum-value-descending"]); equal(