UNPKG

ts-schema-factory

Version:

Generate TypeScript interfaces from database query results or JS arrays

120 lines (98 loc) 3.52 kB
// index.js const fs = require('fs'); // -------------------- Schema generator -------------------- function detectType(value) { if (value === null) return 'null'; if (Array.isArray(value)) return 'array'; if (value instanceof Date) return 'string'; return typeof value; } function mergeTypes(type1, type2) { const set = new Set([].concat(type1 || []).concat(type2 || [])); return [...set]; } function mergeSchemas(schema1, schema2) { if (!schema1) return schema2; if (!schema2) return schema1; const type = mergeTypes(schema1.type, schema2.type); const properties = {}; const keys = new Set([ ...Object.keys(schema1.properties || {}), ...Object.keys(schema2.properties || {}) ]); for (const key of keys) { properties[key] = mergeSchemas(schema1.properties?.[key], schema2.properties?.[key]); } return { type, properties }; } function generateSchemaFromObject(obj) { const type = detectType(obj); if (type === 'object') { const properties = {}; for (const key in obj) { properties[key] = generateSchemaFromObject(obj[key]); } return { type: 'object', properties }; } else if (type === 'array') { if (obj.length === 0) return { type: 'array', items: { type: 'any' } }; let itemsSchema = generateSchemaFromObject(obj[0]); for (let i = 1; i < obj.length; i++) { itemsSchema = mergeSchemas(itemsSchema, generateSchemaFromObject(obj[i])); } return { type: 'array', items: itemsSchema }; } else { return { type }; } } function generateSchemaFromArray(arr) { if (!Array.isArray(arr) || arr.length === 0) return { type: 'array', items: { type: 'any' } }; let schema = generateSchemaFromObject(arr[0]); for (let i = 1; i < arr.length; i++) { schema = mergeSchemas(schema, generateSchemaFromObject(arr[i])); } return { type: 'array', items: schema }; } // -------------------- TS Interface generator -------------------- function convertType(prop) { let types = prop.type; if (!Array.isArray(types)) types = [types]; const mapped = types.map(t => { switch (t) { case 'string': return 'string'; case 'number': return 'number'; case 'boolean': return 'boolean'; case 'array': return prop.items ? `${convertType(prop.items)}[]` : 'any[]'; case 'object': return prop.properties ? '{ ' + Object.entries(prop.properties).map(([k,v]) => `${k}: ${convertType(v)}`).join('; ') + ' }' : 'any'; case 'null': return 'null'; default: return 'any'; } }); return mapped.join(' | '); } function generateTSInterface(schema, interfaceName = 'Root') { if (!schema || !schema.properties) return ''; const lines = []; lines.push(`interface ${interfaceName} {`); for (const key in schema.properties) { const prop = schema.properties[key]; const optional = prop.type.includes('null') ? '?' : ''; const tsType = convertType(prop); lines.push(` ${key}${optional}: ${tsType};`); } lines.push('}'); return lines.join('\n'); } // -------------------- Helper functions -------------------- function generateInterfaceFromArray(dataArray, interfaceName = 'Root', outputFile) { const schema = generateSchemaFromArray(dataArray); const objectSchema = schema.items; const tsInterface = generateTSInterface(objectSchema, interfaceName); if (outputFile) fs.writeFileSync(outputFile, tsInterface); return tsInterface; } module.exports = { generateSchemaFromArray, generateTSInterface, generateInterfaceFromArray };