Skip to content
Closed
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
39 changes: 30 additions & 9 deletions lib/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {default as Emitter, emitProxyTypeCheck} from './emit';
import { default as Emitter, emitProxyTypeCheck } from './emit';

export const enum BaseShape {
BOTTOM,
Expand All @@ -18,6 +18,11 @@ export const enum ContextType {
FIELD
}

// This is a function because regex are stateful
function invalidIdentifiedRegex(): RegExp {
return /^\d|[^0-9a-zA-Z$_]/g;
}

function pascalCase(n: string): string {
return n.split("_").map((s) => (s[0] ? s[0].toUpperCase() : "") + s.slice(1)).join("");
}
Expand Down Expand Up @@ -395,7 +400,7 @@ export class CRecordShape {
const w = e.interfaces;
w.write(`export interface ${this.getName(e)} {`).endl();
this.forEachField((t, name) => {
w.tab(1).write(name);
w.tab(1).write(this.sanitizeKeyName(name));
if (t.nullable) {
w.write("?");
}
Expand All @@ -409,7 +414,7 @@ export class CRecordShape {
const w = e.proxies;
w.writeln(`export class ${this.getProxyClass(e)} {`);
this.forEachField((t, name) => {
w.tab(1).writeln(`public readonly ${name}: ${t.getProxyType(e)};`);
w.tab(1).writeln(`public readonly ${this.sanitizeKeyName(name)}: ${t.getProxyType(e)};`);
});
w.tab(1).writeln(`public static Parse(d: string): ${this.getProxyType(e)} {`);
w.tab(2).writeln(`return ${this.getProxyClass(e)}.Create(JSON.parse(d));`);
Expand Down Expand Up @@ -437,14 +442,14 @@ export class CRecordShape {
// At this point, we know we have a non-null object.
// Check all fields.
this.forEachField((t, name) => {
emitProxyTypeCheck(e, w, t, 2, `d.${name}`, `field + ".${name}"`);
emitProxyTypeCheck(e, w, t, 2, `d["${name}"]`, `field + "[\\"${name}\\"]"`);
});
w.tab(2).writeln(`return new ${this.getProxyClass(e)}(d);`);
w.tab(1).writeln(`}`);
w.tab(1).writeln(`private constructor(d: any) {`);
// Emit an assignment for each field.
this.forEachField((t, name) => {
w.tab(2).writeln(`this.${name} = d.${name};`);
w.tab(2).writeln(`this["${name}"] = d["${name}"];`);
});
w.tab(1).writeln(`}`);
w.writeln('}');
Expand All @@ -458,8 +463,8 @@ export class CRecordShape {
this._name = name;
}
public getName(e: Emitter): string {
if (typeof(this._name) === 'string') {
return this._name;
if (typeof (this._name) === 'string') {
return this.sanitizeTypeName(this._name);
}
// Calculate unique name.
const nameSet = new Set<string>();
Expand All @@ -474,9 +479,23 @@ export class CRecordShape {
return false;
})
.join("Or");
name = this.sanitizeTypeName(name);
this._name = e.registerName(name);
return this._name;
}
public sanitizeKeyName(name: string): string {
if (invalidIdentifiedRegex().test(name)) {
return `["${name}"]`;
}
return name;
}
public sanitizeTypeName(name: string): string {
// Accept 'a to z', '$' and '_' in any position.
// Accept '0 to 9' in any position except first.
// Replace invalid by '_'
const result = name.replace(invalidIdentifiedRegex(), '_');
return result;
}
}

export class CCollectionShape {
Expand Down Expand Up @@ -522,7 +541,7 @@ export class CCollectionShape {
return t.type === BaseShape.COLLECTION && this.baseShape.equal(t.baseShape);
}
public getName(e: Emitter): string {
if (typeof(this._name) === 'string') {
if (typeof (this._name) === 'string') {
return this._name;
}
const nameSet = new Set<string>();
Expand All @@ -541,6 +560,7 @@ export class CCollectionShape {
}
}

// Combine shapes: return s1 + s2
export function csh(e: Emitter, s1: Shape, s2: Shape): Shape {
// csh(σ, σ) = σ
if (s1 === s2) {
Expand Down Expand Up @@ -601,11 +621,12 @@ export function csh(e: Emitter, s1: Shape, s2: Shape): Shape {
return new CAnyShape([s1, s2], s1.nullable || s2.nullable);
}

// returns the Shape for the type `d`
export function d2s(e: Emitter, d: any): Shape {
if (d === undefined || d === null) {
return NullShape;
}
switch (typeof(d)) {
switch (typeof (d)) {
case 'number':
return NumberShape;
case 'string':
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "maketypes",
"version": "1.1.2",
"version": "1.1.3",
"description": "Make TypeScript types and proxy objects from example JSON objects. Can use proxy objects to dynamically type check JSON at runtime.",
"main": "lib/index.js",
"types": "lib/index",
Expand Down
5 changes: 5 additions & 0 deletions test/samples/SpecialCharacters.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[
{
"&Foo": "x"
}
]