Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions api.md
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,32 @@ class MyObject {
```


## [default-properties-ref](./test/programs/default-properties-ref)

```ts

const defaultBooleanFalse = false;
const defaultBooleanTrue = true;
const defaultFloat = 12.3;
const defaultInteger = 123;
const defaultString = "test"

enum FruitEnum {
Apple = 'apple',
Orange = 'orange'
}

class MyObject {
propBooleanFalse: boolean = defaultBooleanFalse;
propBooleanTrue: boolean = defaultBooleanTrue;
propFloat: number = defaultFloat;
propInteger: number = defaultInteger;
propString: string = defaultString;
propEnum: FruitEnum = FruitEnum.Apple;
}
```


## [enums-compiled-compute](./test/programs/enums-compiled-compute)

```ts
Expand Down
20 changes: 20 additions & 0 deletions test/programs/default-properties-ref/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

const defaultBooleanFalse = false;
const defaultBooleanTrue = true;
const defaultFloat = 12.3;
const defaultInteger = 123;
const defaultString = "test"

enum FruitEnum {
Apple = 'apple',
Orange = 'orange'
}

class MyObject {
propBooleanFalse: boolean = defaultBooleanFalse;
propBooleanTrue: boolean = defaultBooleanTrue;
propFloat: number = defaultFloat;
propInteger: number = defaultInteger;
propString: string = defaultString;
propEnum: FruitEnum = FruitEnum.Apple;
}
48 changes: 48 additions & 0 deletions test/programs/default-properties-ref/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"additionalProperties": false,
"definitions": {
"FruitEnum": {
"enum": [
"apple",
"orange"
],
"type": "string"
}
},
"properties": {
"propBooleanFalse": {
"type": "boolean",
"default": false
},
"propBooleanTrue": {
"type": "boolean",
"default": true
},
"propEnum": {
"$ref": "#/definitions/FruitEnum",
"default": "apple"
},
"propFloat": {
"type": "number",
"default": 12.3
},
"propInteger": {
"type": "number",
"default": 123
},
"propString": {
"type": "string",
"default": "test"
}
},
"required": [
"propBooleanFalse",
"propBooleanTrue",
"propEnum",
"propFloat",
"propInteger",
"propString"
],
"type": "object"
}
1 change: 1 addition & 0 deletions test/schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@ describe("schema", () => {
assertSchema("ignored-required", "MyObject");

assertSchema("default-properties", "MyObject");
assertSchema("default-properties-ref", "MyObject");

// not supported yet #116
// assertSchema("interface-extra-props", "MyObject");
Expand Down
95 changes: 72 additions & 23 deletions typescript-json-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,55 @@ export class JsonSchemaGenerator {
return undefined;
}

private getInitializerValue(initializer?: ts.Expression | ts.LiteralToken): any {
let val;
if (initializer === undefined) {
return;
}
switch (initializer.kind) {
case ts.SyntaxKind.NumericLiteral:
const txt = initializer.getText();
if (txt.includes(".")) {
val = Number.parseFloat(initializer.getText());
} else {
val = Number.parseInt(initializer.getText());
}
break;
case ts.SyntaxKind.StringLiteral:
val = (initializer as ts.StringLiteral).text;
break;
case ts.SyntaxKind.FalseKeyword:
val = false;
break;
case ts.SyntaxKind.TrueKeyword:
val = true;
break;
}
return val;
}

private getDeclarationValue(declaration?: ts.Declaration): any {
let val: any;
if (declaration === undefined) {
return;
}
switch (declaration.kind) {
case ts.SyntaxKind.VariableDeclaration:
val = this.getInitializerValue((declaration as ts.VariableDeclaration).initializer);
break;
case ts.SyntaxKind.EnumDeclaration:
const enumDecl = declaration as ts.EnumDeclaration;
val = enumDecl.members.reduce((prev, curr) => {
const v = this.getInitializerValue(curr.initializer);
prev[curr.name.getText()] = v;
return prev;
}, {} as { [k: string]: any });

break;
}
return val;
}

private getDefinitionForProperty(prop: ts.Symbol, node: ts.Node): Definition | null {
if (prop.flags & ts.SymbolFlags.Method) {
return null;
Expand Down Expand Up @@ -847,31 +896,30 @@ export class JsonSchemaGenerator {
initial = initial.expression;
}

if ((<any>initial).expression) {
// node
console.warn("initializer is expression for property " + propertyName);
} else if ((<any>initial).kind && (<any>initial).kind === ts.SyntaxKind.NoSubstitutionTemplateLiteral) {
definition.default = initial.getText();
} else {
try {
const sandbox = { sandboxvar: null as any };
vm.runInNewContext("sandboxvar=" + initial.getText(), sandbox);
try {
const sandbox: Record<string, any> = { sandboxvar: null as any };
// Put user symbols into sandbox
Object.entries(this.userSymbols)
.filter(([_, sym]) => sym.valueDeclaration)
.forEach(([name, sym]) => {
sandbox[name] = this.getDeclarationValue(sym.valueDeclaration);
});
vm.runInNewContext("sandboxvar=" + initial.getText(), sandbox);

const val = sandbox.sandboxvar;
if (
val === null ||
typeof val === "string" ||
typeof val === "number" ||
typeof val === "boolean" ||
Object.prototype.toString.call(val) === "[object Array]"
) {
definition.default = val;
} else if (val) {
console.warn("unknown initializer for property " + propertyName + ": " + val);
}
} catch (e) {
console.warn("exception evaluating initializer for property " + propertyName);
const val = sandbox.sandboxvar;
if (
val === null ||
typeof val === "string" ||
typeof val === "number" ||
typeof val === "boolean" ||
Object.prototype.toString.call(val) === "[object Array]"
) {
definition.default = val;
} else if (val) {
console.warn("unknown initializer for property " + propertyName + ": " + val);
}
} catch (e) {
console.warn("exception evaluating initializer for property " + propertyName);
}
}

Expand Down Expand Up @@ -1700,6 +1748,7 @@ export function buildGenerator(

function inspect(node: ts.Node, tc: ts.TypeChecker) {
if (
node.kind === ts.SyntaxKind.VariableDeclaration ||
node.kind === ts.SyntaxKind.ClassDeclaration ||
node.kind === ts.SyntaxKind.InterfaceDeclaration ||
node.kind === ts.SyntaxKind.EnumDeclaration ||
Expand Down