Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release #1513

Merged
merged 73 commits into from
Dec 22, 2022
Merged

Release #1513

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
c54fa56
chore(deps): bump typescript from 4.8.3 to 4.8.4 (#1430)
dependabot[bot] Oct 2, 2022
866385d
chore(deps): bump commander from 9.4.0 to 9.4.1 (#1429)
dependabot[bot] Oct 2, 2022
208b415
chore(deps-dev): bump @typescript-eslint/eslint-plugin (#1428)
dependabot[bot] Oct 2, 2022
31b9b2d
chore(deps-dev): bump @babel/preset-env from 7.19.1 to 7.19.3 (#1427)
dependabot[bot] Oct 2, 2022
b2a01ff
chore(deps-dev): bump @babel/core from 7.19.1 to 7.19.3 (#1425)
dependabot[bot] Oct 2, 2022
5f97cbb
chore(deps-dev): bump @types/node from 18.7.20 to 18.7.23 (#1424)
dependabot[bot] Oct 2, 2022
cc657ef
chore(deps-dev): bump @typescript-eslint/parser from 5.38.0 to 5.38.1…
dependabot[bot] Oct 2, 2022
97af349
chore(deps-dev): bump @typescript-eslint/eslint-plugin (#1436)
dependabot[bot] Oct 9, 2022
ab2d501
chore(deps-dev): bump @types/node from 18.7.23 to 18.8.3 (#1434)
dependabot[bot] Oct 9, 2022
9d5c0b4
chore(deps-dev): bump jest from 29.0.3 to 29.1.2 (#1433)
dependabot[bot] Oct 9, 2022
2b558a2
chore(deps-dev): bump eslint from 8.24.0 to 8.25.0 (#1432)
dependabot[bot] Oct 9, 2022
34bb70b
chore(deps-dev): bump @typescript-eslint/parser from 5.38.1 to 5.39.0…
dependabot[bot] Oct 9, 2022
b836552
chore(deps-dev): bump @babel/preset-env from 7.19.3 to 7.19.4 (#1445)
dependabot[bot] Oct 16, 2022
19f87d1
chore(deps-dev): bump @typescript-eslint/parser from 5.39.0 to 5.40.0…
dependabot[bot] Oct 16, 2022
8865f8f
chore(deps-dev): bump jest from 29.1.2 to 29.2.0 (#1442)
dependabot[bot] Oct 16, 2022
55c7e6d
chore(deps-dev): bump @types/node from 18.8.3 to 18.11.0 (#1440)
dependabot[bot] Oct 16, 2022
5010953
chore(deps-dev): bump @typescript-eslint/eslint-plugin (#1441)
dependabot[bot] Oct 16, 2022
b920c95
chore(deps-dev): bump vega-lite from 5.5.0 to 5.6.0 (#1444)
dependabot[bot] Oct 16, 2022
028906c
feat: Add support for computed types on literals (#1438)
sean9keenan Oct 16, 2022
85f08ed
fix: Export HiddenType and HiddenTypeFormatter (#1437)
sean9keenan Oct 16, 2022
a1ef303
feat: support for sourceless nodes. (#1386)
arthurfiorette Oct 17, 2022
bb761fa
chore(deps-dev): bump @typescript-eslint/parser from 5.40.0 to 5.40.1…
dependabot[bot] Oct 23, 2022
2b3ad9e
chore(deps-dev): bump jest from 29.2.0 to 29.2.1 (#1452)
dependabot[bot] Oct 23, 2022
1e7d30d
chore(deps-dev): bump @babel/core from 7.19.3 to 7.19.6 (#1451)
dependabot[bot] Oct 23, 2022
af0c32f
chore(deps-dev): bump @types/node from 18.11.0 to 18.11.3 (#1450)
dependabot[bot] Oct 23, 2022
2b06ab0
chore(deps-dev): bump eslint from 8.25.0 to 8.26.0 (#1447)
dependabot[bot] Oct 23, 2022
0272d1d
chore(deps-dev): bump @typescript-eslint/eslint-plugin (#1449)
dependabot[bot] Oct 23, 2022
8559218
chore(deps): bump safe-stable-stringify from 2.4.0 to 2.4.1 (#1448)
dependabot[bot] Oct 23, 2022
b6adf72
chore(deps-dev): bump @types/node from 18.11.3 to 18.11.7 (#1460)
dependabot[bot] Oct 30, 2022
3c52d65
chore(deps-dev): bump @typescript-eslint/parser from 5.40.1 to 5.41.0…
dependabot[bot] Oct 30, 2022
a1f9df1
chore(deps-dev): bump jest from 29.2.1 to 29.2.2 (#1457)
dependabot[bot] Oct 30, 2022
53ba038
chore(deps-dev): bump @typescript-eslint/eslint-plugin (#1458)
dependabot[bot] Oct 30, 2022
0cb1083
chore(deps-dev): bump @babel/core from 7.19.6 to 7.20.2 (#1467)
dependabot[bot] Nov 6, 2022
83b4d6f
chore(deps-dev): bump @types/node from 18.11.7 to 18.11.9 (#1466)
dependabot[bot] Nov 6, 2022
0cd8522
chore(deps-dev): bump @typescript-eslint/eslint-plugin (#1465)
dependabot[bot] Nov 6, 2022
97021cc
chore(deps-dev): bump eslint from 8.26.0 to 8.27.0 (#1463)
dependabot[bot] Nov 6, 2022
a2098f5
chore(deps-dev): bump @types/jest from 29.0.3 to 29.2.2 (#1462)
dependabot[bot] Nov 6, 2022
e70cb2d
chore(deps-dev): bump @babel/preset-env from 7.19.4 to 7.20.2 (#1464)
dependabot[bot] Nov 6, 2022
a1c82f4
chore(deps-dev): bump @typescript-eslint/parser from 5.41.0 to 5.42.0…
dependabot[bot] Nov 6, 2022
d1070ab
chore(deps-dev): bump jest from 29.2.2 to 29.3.1 (#1471)
dependabot[bot] Nov 13, 2022
1b095ff
chore(deps-dev): bump @typescript-eslint/eslint-plugin (#1470)
dependabot[bot] Nov 13, 2022
e7f54a9
chore(deps-dev): bump chai from 4.3.6 to 4.3.7 (#1469)
dependabot[bot] Nov 13, 2022
ba8b7a1
chore(deps-dev): bump @typescript-eslint/parser from 5.42.0 to 5.42.1…
dependabot[bot] Nov 13, 2022
5562571
chore(deps-dev): bump ajv from 8.11.0 to 8.11.2 (#1478)
dependabot[bot] Nov 20, 2022
37775de
chore(deps-dev): bump eslint from 8.27.0 to 8.28.0 (#1476)
dependabot[bot] Nov 20, 2022
c343eb2
chore(deps-dev): bump @typescript-eslint/eslint-plugin (#1475)
dependabot[bot] Nov 20, 2022
e366b91
chore(deps-dev): bump @types/jest from 29.2.2 to 29.2.3 (#1474)
dependabot[bot] Nov 20, 2022
7c1f6d5
chore(deps-dev): bump @typescript-eslint/parser from 5.42.1 to 5.43.0…
dependabot[bot] Nov 20, 2022
2ed8ab1
chore(deps-dev): bump jest-junit from 14.0.1 to 15.0.0 (#1479)
dependabot[bot] Nov 20, 2022
2ba0135
feat: add support for type URL (string with format "uri") (#1480)
thomaswr Nov 22, 2022
676febe
chore(deps-dev): bump @typescript-eslint/eslint-plugin (#1484)
dependabot[bot] Nov 27, 2022
9dd86e9
chore(deps-dev): bump prettier from 2.7.1 to 2.8.0 (#1482)
dependabot[bot] Nov 27, 2022
49e6ab6
chore(deps-dev): bump @typescript-eslint/parser from 5.43.0 to 5.44.0…
dependabot[bot] Nov 27, 2022
62a17a9
feat: basic support for inferrable types (#1407)
swnf Dec 3, 2022
03114a0
chore: upgrade deps (#1487)
domoritz Dec 3, 2022
99ae34f
docs: clarify escaping (#1489)
domoritz Dec 4, 2022
decb278
chore: upgrade deps (#1490)
domoritz Dec 6, 2022
738addd
fix: add enum to discriminator to avoid schema accepting too much (#1…
mvanniekerkSQ Dec 9, 2022
22aec03
chore(deps-dev): bump prettier from 2.8.0 to 2.8.1 (#1501)
dependabot[bot] Dec 11, 2022
b1c919c
chore(deps-dev): bump @types/jest from 29.2.3 to 29.2.4 (#1500)
dependabot[bot] Dec 11, 2022
4817f2b
chore(deps-dev): bump @typescript-eslint/eslint-plugin (#1499)
dependabot[bot] Dec 11, 2022
475203d
chore(deps-dev): bump @types/node from 18.11.10 to 18.11.13 (#1498)
dependabot[bot] Dec 11, 2022
9c44513
chore(deps): bump typescript from 4.9.3 to 4.9.4 (#1496)
dependabot[bot] Dec 11, 2022
4a8deac
chore(deps-dev): bump @typescript-eslint/parser from 5.45.0 to 5.46.0…
dependabot[bot] Dec 11, 2022
f4afabf
chore(deps-dev): bump @types/node from 18.11.13 to 18.11.17 (#1508)
dependabot[bot] Dec 18, 2022
41cb810
chore(deps-dev): bump eslint from 8.29.0 to 8.30.0 (#1507)
dependabot[bot] Dec 18, 2022
d24246d
chore(deps): bump json5 from 2.2.1 to 2.2.2 (#1506)
dependabot[bot] Dec 18, 2022
c4d8a88
chore(deps-dev): bump @typescript-eslint/parser from 5.46.0 to 5.46.1…
dependabot[bot] Dec 18, 2022
3d339ce
chore(deps-dev): bump @typescript-eslint/eslint-plugin (#1504)
dependabot[bot] Dec 18, 2022
df2de31
feat: support method signatures in objects (#1511)
flugg Dec 21, 2022
888f691
feat: support constructor node types (#1512)
flugg Dec 21, 2022
c5e8393
feat: support conditionals in mapped types (#1510)
flugg Dec 21, 2022
0ac5df1
fix: support non-literal types in template literal strings (#1509)
flugg Dec 22, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,23 @@ This project is made possible by a [community of contributors](https://github.co

## CLI Usage

Run the schema generator with npx:

```bash
npx ts-json-schema-generator --path 'my/project/**/*.ts' --type 'My.Type.Name'
```

Or install the package and then run it

```bash
npm install --save ts-json-schema-generator
./node_modules/.bin/ts-json-schema-generator --path 'my/project/**/*.ts' --type 'My.Type.Name'
```

Note that different platforms (e.g. Windows) may use different path separators so you may have to adjust the command above.

Also note that you need to quote paths with `*` as otherwise the shell will expand the paths and therefore only pass the first path to the generator.

## Programmatic Usage

```js
Expand Down Expand Up @@ -252,7 +262,7 @@ fs.writeFile(output_path, schemaString, (err) => {
- `interface` types
- `enum` types
- `union`, `tuple`, `type[]` types
- `Date`, `RegExp` types
- `Date`, `RegExp`, `URL` types
- `string`, `boolean`, `number` types
- `"value"`, `123`, `true`, `false`, `null`, `undefined` literals
- type aliases
Expand Down
10 changes: 9 additions & 1 deletion factory/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { BooleanLiteralNodeParser } from "../src/NodeParser/BooleanLiteralNodePa
import { BooleanTypeNodeParser } from "../src/NodeParser/BooleanTypeNodeParser";
import { CallExpressionParser } from "../src/NodeParser/CallExpressionParser";
import { ConditionalTypeNodeParser } from "../src/NodeParser/ConditionalTypeNodeParser";
import { ConstructorNodeParser } from "../src/NodeParser/ConstructorNodeParser";
import { EnumNodeParser } from "../src/NodeParser/EnumNodeParser";
import { ExpressionWithTypeArgumentsNodeParser } from "../src/NodeParser/ExpressionWithTypeArgumentsNodeParser";
import { FunctionNodeParser } from "../src/NodeParser/FunctionNodeParser";
Expand Down Expand Up @@ -112,6 +113,7 @@ export function createParser(program: ts.Program, config: Config, augmentor?: Pa
.addNodeParser(new BooleanLiteralNodeParser())
.addNodeParser(new NullLiteralNodeParser())
.addNodeParser(new FunctionNodeParser())
.addNodeParser(new ConstructorNodeParser())
.addNodeParser(new ObjectLiteralExpressionNodeParser(chainNodeParser))
.addNodeParser(new ArrayLiteralExpressionNodeParser(chainNodeParser))

Expand Down Expand Up @@ -157,7 +159,13 @@ export function createParser(program: ts.Program, config: Config, augmentor?: Pa
.addNodeParser(
withCircular(
withExpose(
withJsDoc(new TypeLiteralNodeParser(withJsDoc(chainNodeParser), mergedConfig.additionalProperties))
withJsDoc(
new TypeLiteralNodeParser(
typeChecker,
withJsDoc(chainNodeParser),
mergedConfig.additionalProperties
)
)
)
)
)
Expand Down
2 changes: 2 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export * from "./src/Type/BooleanType";
export * from "./src/Type/DefinitionType";
export * from "./src/Type/EnumType";
export * from "./src/Type/FunctionType";
export * from "./src/Type/HiddenType";
export * from "./src/Type/IntersectionType";
export * from "./src/Type/LiteralType";
export * from "./src/Type/NeverType";
Expand Down Expand Up @@ -82,6 +83,7 @@ export * from "./src/TypeFormatter/ArrayTypeFormatter";
export * from "./src/TypeFormatter/BooleanTypeFormatter";
export * from "./src/TypeFormatter/DefinitionTypeFormatter";
export * from "./src/TypeFormatter/EnumTypeFormatter";
export * from "./src/TypeFormatter/HiddenTypeFormatter";
export * from "./src/TypeFormatter/IntersectionTypeFormatter";
export * from "./src/TypeFormatter/LiteralTypeFormatter";
export * from "./src/TypeFormatter/LiteralUnionTypeFormatter";
Expand Down
32 changes: 16 additions & 16 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,39 +45,39 @@
},
"dependencies": {
"@types/json-schema": "^7.0.11",
"commander": "^9.4.0",
"commander": "^9.4.1",
"glob": "^8.0.3",
"json5": "^2.2.1",
"normalize-path": "^3.0.0",
"safe-stable-stringify": "^2.4.0",
"typescript": "~4.8.3"
"safe-stable-stringify": "^2.4.1",
"typescript": "~4.9.3"
},
"devDependencies": {
"@auto-it/conventional-commits": "^10.37.6",
"@auto-it/first-time-contributor": "^10.37.6",
"@babel/core": "^7.19.1",
"@babel/preset-env": "^7.19.1",
"@babel/core": "^7.20.5",
"@babel/preset-env": "^7.20.2",
"@babel/preset-typescript": "^7.18.6",
"@types/glob": "^8.0.0",
"@types/jest": "^29.0.3",
"@types/node": "^18.7.18",
"@types/jest": "^29.2.3",
"@types/node": "^18.11.10",
"@types/normalize-path": "^3.0.0",
"@typescript-eslint/eslint-plugin": "^5.38.0",
"@typescript-eslint/parser": "^5.38.0",
"ajv": "^8.11.0",
"@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.45.0",
"ajv": "^8.11.2",
"ajv-formats": "^2.1.1",
"auto": "^10.37.6",
"chai": "^4.3.6",
"chai": "^4.3.7",
"cross-env": "^7.0.3",
"eslint": "^8.23.1",
"eslint": "^8.29.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.2.1",
"jest": "^29.0.3",
"jest-junit": "^14.0.1",
"prettier": "^2.7.1",
"jest": "^29.3.1",
"jest-junit": "^15.0.0",
"prettier": "^2.8.0",
"ts-node": "^10.9.1",
"vega": "^5.22.1",
"vega-lite": "^5.5.0"
"vega-lite": "^5.6.0"
},
"scripts": {
"prepublishOnly": "yarn build",
Expand Down
3 changes: 2 additions & 1 deletion src/NodeParser/AnnotatedNodeParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ export class AnnotatedNodeParser implements SubNodeParser {
const baseType = this.childNodeParser.createType(node, context, reference);

// Don't return annotations for lib types such as Exclude.
if (node.getSourceFile().fileName.match(/[/\\]typescript[/\\]lib[/\\]lib\.[^/\\]+\.d\.ts$/i)) {
// Sourceless nodes may not have a fileName, just ignore them.
if (node.getSourceFile()?.fileName.match(/[/\\]typescript[/\\]lib[/\\]lib\.[^/\\]+\.d\.ts$/i)) {
let specialCase = false;

// Special case for Exclude<T, U>: use the annotation of T.
Expand Down
18 changes: 18 additions & 0 deletions src/NodeParser/ConstructorNodeParser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import ts from "typescript";
import { SubNodeParser } from "../SubNodeParser";
import { BaseType } from "../Type/BaseType";
import { ConstructorType } from "../Type/ConstructorType";

/**
* A constructor node parser that creates a constructor type so that mapped
* types can use constructors as values. There is no formatter for constructor
* types.
*/
export class ConstructorNodeParser implements SubNodeParser {
public supportsNode(node: ts.ConstructorTypeNode): boolean {
return node.kind === ts.SyntaxKind.ConstructorType;
}
public createType(): BaseType {
return new ConstructorType();
}
}
21 changes: 18 additions & 3 deletions src/NodeParser/InterfaceAndClassNodeParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,27 @@ export class InterfaceAndClassNodeParser implements SubNodeParser {
}
return members;
}, [] as (ts.PropertyDeclaration | ts.PropertySignature | ts.ParameterPropertyDeclaration)[])
.filter((member) => isPublic(member) && !isStatic(member) && member.type && !isNodeHidden(member))
.filter((member) => isPublic(member) && !isStatic(member) && !isNodeHidden(member))
.reduce((entries, member) => {
let memberType: ts.Node | undefined = member.type;

// Use the type checker if the member has no explicit type
// Ignore members without an initializer. They have no useful type.
if (memberType === undefined && member.initializer !== undefined) {
const type = this.typeChecker.getTypeAtLocation(member);
memberType = this.typeChecker.typeToTypeNode(type, node, ts.NodeBuilderFlags.NoTruncation);
}

if (memberType !== undefined) {
return [...entries, { member, memberType }];
}
return entries;
}, [])
.map(
(member) =>
({ member, memberType }) =>
new ObjectProperty(
this.getPropertyName(member.name),
this.childNodeParser.createType(member.type!, context),
this.childNodeParser.createType(memberType, context),
!member.questionToken
)
)
Expand Down
12 changes: 6 additions & 6 deletions src/NodeParser/MappedTypeNodeParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import { ObjectProperty, ObjectType } from "../Type/ObjectType";
import { StringType } from "../Type/StringType";
import { SymbolType } from "../Type/SymbolType";
import { UnionType } from "../Type/UnionType";
import assert from "../Utils/assert";
import { derefAnnotatedType, derefType } from "../Utils/derefType";
import { getKey } from "../Utils/nodeKey";
import { preserveAnnotation } from "../Utils/preserveAnnotation";
Expand Down Expand Up @@ -79,23 +78,24 @@ export class MappedTypeNodeParser implements SubNodeParser {
}
}

protected mapKey(node: ts.MappedTypeNode, rawKey: LiteralType, context: Context): LiteralType {
protected mapKey(node: ts.MappedTypeNode, rawKey: LiteralType, context: Context): BaseType {
if (!node.nameType) {
return rawKey;
}
const key = derefType(
this.childNodeParser.createType(node.nameType, this.createSubContext(node, rawKey, context))
);
assert(key instanceof LiteralType, "Must resolve to Literal");

return key;
}

protected getProperties(node: ts.MappedTypeNode, keyListType: UnionType, context: Context): ObjectProperty[] {
return keyListType
.getTypes()
.filter((type): type is LiteralType => type instanceof LiteralType)
.reduce((result: ObjectProperty[], key: LiteralType) => {
const namedKey = this.mapKey(node, key, context);
.map((type) => [type, this.mapKey(node, type, context)])
.filter((value): value is [LiteralType, LiteralType] => value[1] instanceof LiteralType)
.reduce((result: ObjectProperty[], [key, mappedKey]: [LiteralType, LiteralType]) => {
const propertyType = this.childNodeParser.createType(
node.type!,
this.createSubContext(node, key, context)
Expand All @@ -110,7 +110,7 @@ export class MappedTypeNodeParser implements SubNodeParser {
}

const objectProperty = new ObjectProperty(
namedKey.getValue().toString(),
mappedKey.getValue().toString(),
preserveAnnotation(propertyType, newType),
!node.questionToken && !hasUndefined
);
Expand Down
39 changes: 25 additions & 14 deletions src/NodeParser/StringTemplateLiteralNodeParser.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import ts from "typescript";
import { UnknownTypeError } from "../Error/UnknownTypeError";
import { Context, NodeParser } from "../NodeParser";
import { SubNodeParser } from "../SubNodeParser";
import { BaseType } from "../Type/BaseType";
import { LiteralType } from "../Type/LiteralType";
import { StringType } from "../Type/StringType";
import { UnionType } from "../Type/UnionType";
import { extractLiterals } from "../Utils/extractLiterals";

Expand All @@ -18,24 +20,33 @@ export class StringTemplateLiteralNodeParser implements SubNodeParser {
if (node.kind === ts.SyntaxKind.NoSubstitutionTemplateLiteral) {
return new LiteralType(node.text);
}
const prefix = node.head.text;
const matrix: string[][] = [[prefix]].concat(
node.templateSpans.map((span) => {
const suffix = span.literal.text;
const type = this.childNodeParser.createType(span.type, context);
return extractLiterals(type).map((value) => value + suffix);
})
);

const expandedLiterals = expand(matrix);
try {
const prefix = node.head.text;
const matrix: string[][] = [[prefix]].concat(
node.templateSpans.map((span) => {
const suffix = span.literal.text;
const type = this.childNodeParser.createType(span.type, context);
return extractLiterals(type).map((value) => value + suffix);
})
);

const expandedTypes = expandedLiterals.map((literal) => new LiteralType(literal));
const expandedLiterals = expand(matrix);

if (expandedTypes.length === 1) {
return expandedTypes[0];
}
const expandedTypes = expandedLiterals.map((literal) => new LiteralType(literal));

if (expandedTypes.length === 1) {
return expandedTypes[0];
}

return new UnionType(expandedTypes);
return new UnionType(expandedTypes);
} catch (error) {
if (error instanceof UnknownTypeError) {
return new StringType();
}

throw error;
}
}
}

Expand Down
50 changes: 40 additions & 10 deletions src/NodeParser/TypeLiteralNodeParser.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import ts from "typescript";
import ts, { isPropertySignature, MethodSignature, PropertySignature } from "typescript";
import { Context, NodeParser } from "../NodeParser";
import { SubNodeParser } from "../SubNodeParser";
import { BaseType } from "../Type/BaseType";
import { FunctionType } from "../Type/FunctionType";
import { NeverType } from "../Type/NeverType";
import { ObjectProperty, ObjectType } from "../Type/ObjectType";
import { ReferenceType } from "../Type/ReferenceType";
import { isNodeHidden } from "../Utils/isHidden";
import { getKey } from "../Utils/nodeKey";

export class TypeLiteralNodeParser implements SubNodeParser {
public constructor(protected childNodeParser: NodeParser, protected readonly additionalProperties: boolean) {}
public constructor(
protected typeChecker: ts.TypeChecker,
protected childNodeParser: NodeParser,
protected readonly additionalProperties: boolean
) {}

public supportsNode(node: ts.TypeLiteralNode): boolean {
return node.kind === ts.SyntaxKind.TypeLiteral;
Expand All @@ -34,15 +39,21 @@ export class TypeLiteralNodeParser implements SubNodeParser {
let hasRequiredNever = false;

const properties = node.members
.filter(ts.isPropertySignature)
.filter(
(element): element is PropertySignature | MethodSignature =>
ts.isPropertySignature(element) || ts.isMethodSignature(element)
)
.filter((propertyNode) => !isNodeHidden(propertyNode))
.map((propertyNode) => {
const propertySymbol: ts.Symbol = (propertyNode as any).symbol;
const type = this.childNodeParser.createType(propertyNode.type!, context);
const objectProperty = new ObjectProperty(propertySymbol.getName(), type, !propertyNode.questionToken);

return objectProperty;
})
.map(
(propertyNode) =>
new ObjectProperty(
this.getPropertyName(propertyNode.name),
isPropertySignature(propertyNode)
? this.childNodeParser.createType(propertyNode.type!, context)
: new FunctionType(),
!propertyNode.questionToken
)
)
.filter((prop) => {
if (prop.isRequired() && prop.getType() instanceof NeverType) {
hasRequiredNever = true;
Expand All @@ -69,4 +80,23 @@ export class TypeLiteralNodeParser implements SubNodeParser {
protected getTypeId(node: ts.Node, context: Context): string {
return `structure-${getKey(node, context)}`;
}

protected getPropertyName(propertyName: ts.PropertyName): string {
if (propertyName.kind === ts.SyntaxKind.ComputedPropertyName) {
const symbol = this.typeChecker.getSymbolAtLocation(propertyName);

if (symbol) {
return symbol.getName();
}
}

try {
return propertyName.getText();
} catch {
// When propertyName was programmatically created, it doesn't have a source file.
// Then, getText() will throw an error. But, for programmatically created nodes,`
// `escapedText` is available.
return (propertyName as ts.Identifier).escapedText as string;
}
}
}