Skip to content

Commit

Permalink
Merge branch 'master' into feature/conditional-type
Browse files Browse the repository at this point in the history
  • Loading branch information
Klaus Reimer committed Jun 5, 2019
2 parents b9a5a5e + 28bbe6e commit 00ed2ba
Show file tree
Hide file tree
Showing 17 changed files with 295 additions and 16 deletions.
4 changes: 2 additions & 2 deletions factory/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { ConditionalTypeNodeParser } from "../src/NodeParser/ConditionalTypeNode
import { EnumNodeParser } from "../src/NodeParser/EnumNodeParser";
import { ExpressionWithTypeArgumentsNodeParser } from "../src/NodeParser/ExpressionWithTypeArgumentsNodeParser";
import { IndexedAccessTypeNodeParser } from "../src/NodeParser/IndexedAccessTypeNodeParser";
import { InterfaceNodeParser } from "../src/NodeParser/InterfaceNodeParser";
import { InterfaceAndClassNodeParser } from "../src/NodeParser/InterfaceAndClassNodeParser";
import { IntersectionNodeParser } from "../src/NodeParser/IntersectionNodeParser";
import { LiteralNodeParser } from "../src/NodeParser/LiteralNodeParser";
import { MappedTypeNodeParser } from "../src/NodeParser/MappedTypeNodeParser";
Expand Down Expand Up @@ -105,7 +105,7 @@ export function createParser(program: ts.Program, config: Config): NodeParser {
new TypeAliasNodeParser(typeChecker, chainNodeParser)))))
.addNodeParser(withExpose(withJsDoc(new EnumNodeParser(typeChecker))))
.addNodeParser(withCircular(withExpose(withJsDoc(
new InterfaceNodeParser(typeChecker, withJsDoc(chainNodeParser)),
new InterfaceAndClassNodeParser(typeChecker, withJsDoc(chainNodeParser)),
))))
.addNodeParser(withCircular(withExpose(withJsDoc(
new TypeLiteralNodeParser(withJsDoc(chainNodeParser)),
Expand Down
2 changes: 1 addition & 1 deletion index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export * from "./src/NodeParser/NumberTypeNodeParser";
export * from "./src/NodeParser/StringTypeNodeParser";
export * from "./src/NodeParser/EnumNodeParser";
export * from "./src/NodeParser/ExpressionWithTypeArgumentsNodeParser";
export * from "./src/NodeParser/InterfaceNodeParser";
export * from "./src/NodeParser/InterfaceAndClassNodeParser";
export * from "./src/NodeParser/ParenthesizedNodeParser";
export * from "./src/NodeParser/TypeAliasNodeParser";
export * from "./src/NodeParser/TypeLiteralNodeParser";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@ import { ObjectProperty, ObjectType } from "../Type/ObjectType";
import { isHidden } from "../Utils/isHidden";
import { getKey } from "../Utils/nodeKey";

export class InterfaceNodeParser implements SubNodeParser {
export class InterfaceAndClassNodeParser implements SubNodeParser {
public constructor(
private typeChecker: ts.TypeChecker,
private childNodeParser: NodeParser,
) {
}

public supportsNode(node: ts.InterfaceDeclaration): boolean {
return node.kind === ts.SyntaxKind.InterfaceDeclaration;
public supportsNode(node: ts.InterfaceDeclaration | ts.ClassDeclaration): boolean {
return node.kind === ts.SyntaxKind.InterfaceDeclaration || node.kind === ts.SyntaxKind.ClassDeclaration;
}
public createType(node: ts.InterfaceDeclaration, context: Context): BaseType {

public createType(node: ts.InterfaceDeclaration | ts.ClassDeclaration, context: Context): BaseType {
if (node.typeParameters && node.typeParameters.length) {
node.typeParameters.forEach((typeParam) => {
const nameSymbol = this.typeChecker.getSymbolAtLocation(typeParam.name)!;
Expand All @@ -37,7 +38,7 @@ export class InterfaceNodeParser implements SubNodeParser {
);
}

private getBaseTypes(node: ts.InterfaceDeclaration, context: Context): BaseType[] {
private getBaseTypes(node: ts.InterfaceDeclaration | ts.ClassDeclaration, context: Context): BaseType[] {
if (!node.heritageClauses) {
return [];
}
Expand All @@ -48,26 +49,36 @@ export class InterfaceNodeParser implements SubNodeParser {
], []);
}

private getProperties(node: ts.InterfaceDeclaration, context: Context): ObjectProperty[] {
return node.members
.filter(ts.isPropertySignature)
private getProperties(node: ts.InterfaceDeclaration | ts.ClassDeclaration, context: Context): ObjectProperty[] {
function isProperty(member: ts.Node): member is (ts.PropertyDeclaration | ts.PropertySignature) {
return ts.isPropertySignature(member) || ts.isPropertyDeclaration(member);
}
return (<ts.NodeArray<ts.NamedDeclaration>>node.members)
.filter(isProperty)
.filter(prop => !prop.modifiers || !prop.modifiers.some(modifier =>
modifier.kind === ts.SyntaxKind.PrivateKeyword ||
modifier.kind === ts.SyntaxKind.ProtectedKeyword ||
modifier.kind === ts.SyntaxKind.StaticKeyword))
.reduce((result: ObjectProperty[], propertyNode) => {
const propertySymbol: ts.Symbol = (propertyNode as any).symbol;
if (isHidden(propertySymbol)) {
const propertyType = propertyNode.type;
if (!propertyType || isHidden(propertySymbol)) {
return result;
}
const objectProperty: ObjectProperty = new ObjectProperty(
propertySymbol.getName(),
this.childNodeParser.createType(propertyNode.type!, context),
this.childNodeParser.createType(propertyType, context),
!propertyNode.questionToken,
);

result.push(objectProperty);
return result;
}, []);
}
private getAdditionalProperties(node: ts.InterfaceDeclaration, context: Context): BaseType | false {
const indexSignature = node.members.find(ts.isIndexSignatureDeclaration);

private getAdditionalProperties(node: ts.InterfaceDeclaration | ts.ClassDeclaration, context: Context):
BaseType | false {
const indexSignature = (<ts.NodeArray<ts.NamedDeclaration>>node.members).find(ts.isIndexSignatureDeclaration);
if (!indexSignature) {
return false;
}
Expand All @@ -76,6 +87,7 @@ export class InterfaceNodeParser implements SubNodeParser {
}

private getTypeId(node: ts.Node, context: Context): string {
return `interface-${getKey(node, context)}`;
const nodeType = ts.isInterfaceDeclaration(node) ? "interface" : "class";
return `${nodeType}-${getKey(node, context)}`;
}
}
1 change: 1 addition & 0 deletions src/SchemaGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export class SchemaGenerator {
private inspectNode(node: ts.Node, typeChecker: ts.TypeChecker, allTypes: Map<string, ts.Node>): void {
if (
node.kind === ts.SyntaxKind.InterfaceDeclaration ||
node.kind === ts.SyntaxKind.ClassDeclaration ||
node.kind === ts.SyntaxKind.EnumDeclaration ||
node.kind === ts.SyntaxKind.TypeAliasDeclaration
) {
Expand Down
7 changes: 7 additions & 0 deletions test/valid-data.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ describe("valid-data", () => {
it("interface-recursion", assertSchema("interface-recursion", "MyObject"));
it("interface-extra-props", assertSchema("interface-extra-props", "MyObject"));

it("class-single", assertSchema("class-single", "MyObject"));
it("class-multi", assertSchema("class-multi", "MyObject"));
it("class-recursion", assertSchema("class-recursion", "MyObject"));
it("class-extra-props", assertSchema("class-extra-props", "MyObject"));
it("class-inheritance", assertSchema("class-inheritance", "MyObject"));
it("class-generics", assertSchema("class-generics", "MyObject"));

it("structure-private", assertSchema("structure-private", "MyObject"));
it("structure-anonymous", assertSchema("structure-anonymous", "MyObject"));
it("structure-recursion", assertSchema("structure-recursion", "MyObject"));
Expand Down
5 changes: 5 additions & 0 deletions test/valid-data/class-extra-props/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export class MyObject {
public required: string;
public optional?: number;
[name: string]: string|number;
}
26 changes: 26 additions & 0 deletions test/valid-data/class-extra-props/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"MyObject": {
"type": "object",
"properties": {
"required": {
"type": "string"
},
"optional": {
"type": "number"
}
},
"required": [
"required"
],
"additionalProperties": {
"type": [
"string",
"number"
]
}
}
},
"$ref": "#/definitions/MyObject"
}
9 changes: 9 additions & 0 deletions test/valid-data/class-generics/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export class Base<T> {
public a: T;
}

export class MyObject extends Base<number> {
public b: string;
public c: Base<string>;
public d: Base<boolean>;
}
54 changes: 54 additions & 0 deletions test/valid-data/class-generics/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"$ref": "#/definitions/MyObject",
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"Base<boolean>": {
"additionalProperties": false,
"properties": {
"a": {
"type": "boolean"
}
},
"required": [
"a"
],
"type": "object"
},
"Base<string>": {
"additionalProperties": false,
"properties": {
"a": {
"type": "string"
}
},
"required": [
"a"
],
"type": "object"
},
"MyObject": {
"additionalProperties": false,
"properties": {
"a": {
"type": "number"
},
"b": {
"type": "string"
},
"c": {
"$ref": "#/definitions/Base<string>"
},
"d": {
"$ref": "#/definitions/Base<boolean>"
}
},
"required": [
"a",
"b",
"c",
"d"
],
"type": "object"
}
}
}
9 changes: 9 additions & 0 deletions test/valid-data/class-inheritance/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export class Base {
public a: number;
public b: string | string;
}

export class MyObject extends Base {
public c: boolean;
public b: string;
}
26 changes: 26 additions & 0 deletions test/valid-data/class-inheritance/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"$ref": "#/definitions/MyObject",
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"MyObject": {
"additionalProperties": false,
"properties": {
"a": {
"type": "number"
},
"b": {
"type": "string"
},
"c": {
"type": "boolean"
}
},
"required": [
"a",
"b",
"c"
],
"type": "object"
}
}
}
8 changes: 8 additions & 0 deletions test/valid-data/class-multi/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export class MyObject {
public subA: MySubObject;
public subB: MySubObject;
}
export class MySubObject {
public propA: number;
public propB: number;
}
38 changes: 38 additions & 0 deletions test/valid-data/class-multi/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"MyObject": {
"type": "object",
"properties": {
"subA": {
"$ref": "#/definitions/MySubObject"
},
"subB": {
"$ref": "#/definitions/MySubObject"
}
},
"required": [
"subA",
"subB"
],
"additionalProperties": false
},
"MySubObject": {
"type": "object",
"properties": {
"propA": {
"type": "number"
},
"propB": {
"type": "number"
}
},
"required": [
"propA",
"propB"
],
"additionalProperties": false
}
},
"$ref": "#/definitions/MyObject"
}
4 changes: 4 additions & 0 deletions test/valid-data/class-recursion/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export class MyObject {
public propA: number;
public propB: MyObject;
}
22 changes: 22 additions & 0 deletions test/valid-data/class-recursion/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"definitions": {
"MyObject": {
"type": "object",
"properties": {
"propA": {
"type": "number"
},
"propB": {
"$ref": "#/definitions/MyObject"
}
},
"required": [
"propA",
"propB"
],
"additionalProperties": false
}
},
"$ref": "#/definitions/MyObject"
}
36 changes: 36 additions & 0 deletions test/valid-data/class-single/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
export class MyObject {
// Static properties must be ignored
public static staticProp: number;

public propA: number;
public propB: number;

// Properties without type must be ignored
public noType;

// Protected properties must be ignored
protected protectedProp: string;

// Protected properties must be ignored
private privateProp: boolean;

// Constructors must be ignored
public constructor() {
this.privateProp = false;
}

// Normal method must be ignored
public getPrivateProp() {
return this.privateProp;
}

// Getter methods must be ignored
public get getterSetter(): number {
return this.propA;
}

// Setter methods must be ignored
public set getterSetter(value: number) {
this.propA = value;
}
}
Loading

0 comments on commit 00ed2ba

Please sign in to comment.