diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 00000000..1b8b8e46 --- /dev/null +++ b/index.d.ts @@ -0,0 +1,82 @@ +export type ExtractAttribute = + (entity: any) => string; + +export type AssignEntity = + (obj: any, key: string, entity: any, input: any, schema: SchemaValue) => void; + +export type MergeIntoEntity = + (entityA: any, entityB: any, entityKey: string) => void; + +export type SchemaOptions = { + idAttribute?: string | ExtractAttribute; + meta?: any; + assignEntity?: AssignEntity; +} + +export type IterableSchemaOptions = { + schemaAttribute?: string | ExtractAttribute; +} + +export type UnionSchemaOptions = { + schemaAttribute: string | ExtractAttribute; +} + +export type NormalizeOptions = { + assignEntity?: AssignEntity; + mergeIntoEntity?: MergeIntoEntity; +} + +export type NormalizeInput = Object | Array; + +export type NormalizeOutput = { + result: any; + entities?: any; +} + +export class Schema { + constructor (key: string, options?: SchemaOptions); + + define(schema: SchemaMap): void; + getKey(): string; + getIdAttribute(): string; + getMeta(prop: string): any; +} + +export class IterableSchema { + constructor (schema: SchemaValue, options?: IterableSchemaOptions); + + getItemSchema(): SchemaValue; +} + +export class UnionSchema { + constructor (schema: SchemaValue, options: UnionSchemaOptions); + + getItemSchema(): SchemaValue; +} + +export type SchemaValue = Schema | IterableSchema | UnionSchema | SchemaMap; + +export type SchemaMap = { + [key: string]: SchemaValue; +} + +export function arrayOf( + schema: SchemaValue, + options?: IterableSchemaOptions +): IterableSchema; + +export function valuesOf( + schema: SchemaValue, + options?: IterableSchemaOptions +): IterableSchema; + +export function unionOf( + schema: SchemaValue, + options?: UnionSchemaOptions +): UnionSchema; + +export function normalize( + input: NormalizeInput, + schema: SchemaValue, + options?: NormalizeOptions +): NormalizeOutput; diff --git a/package.json b/package.json index d5850173..50f3654d 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "2.1.0", "description": "Normalizes JSON according to schema for Redux and Flux applications", "main": "lib/index.js", + "typings": "index.d.ts", "repository": { "type": "git", "url": "https://github.com/paularmstrong/normalizr.git" @@ -44,6 +45,8 @@ "chai": "^3.2.0", "mocha": "^2.4.5", "rimraf": "^2.4.2", + "typescript": "^1.8.10", + "typescript-definition-tester": "0.0.4", "webpack": "^1.13.0" }, "dependencies": { diff --git a/test/typescript.js b/test/typescript.js new file mode 100644 index 00000000..eb49855a --- /dev/null +++ b/test/typescript.js @@ -0,0 +1,14 @@ +import * as tt from 'typescript-definition-tester'; + +describe('TypeScript definitions', function () { + this.timeout(0); + + it('compile against index.d.ts', (done) => { + tt.compileDirectory( + __dirname + '/typescript', + fileName => fileName.match(/\.ts$/), + () => done() + ); + }); + +}); diff --git a/test/typescript/normalize.ts b/test/typescript/normalize.ts new file mode 100644 index 00000000..101ce597 --- /dev/null +++ b/test/typescript/normalize.ts @@ -0,0 +1,45 @@ +import {Schema, normalize, arrayOf, unionOf, valuesOf} from "../../index.d.ts"; + +const isObject = function (obj: any) { + return typeof obj === 'object'; +}; + +const assignEntity = function (output: any, key: string, value: any) { + if (key === "timestamp") { + output[key] = Date.now() + } + output[key] = value; +}; + +const mergeIntoEntity = function (entityA: any, entityB: any, entityKey: string) { + for (const key in entityB) { + if (!entityB.hasOwnProperty(key)) { + continue; + } + + if (!entityA.hasOwnProperty(key)) { + entityA[key] = entityB[key]; + continue; + } + + if (isObject(entityA[key]) && isObject(entityB[key])) { + // Merge the two entities. + continue; + } + } +}; + +const user = new Schema("users"); +const group = new Schema("groups", { assignEntity }); +const member = unionOf({ users: user, groups: group }, { schemaAttribute: "type" }); + +group.define({ + members: arrayOf(member), + owner: member, + relations: valuesOf(member) +}); + +const normalizeWithObject = normalize({}, { group }); +const normalizeWithArray = normalize([], { groups: arrayOf(group) }); +const normalizeWithAssignEntity = normalize([], { groups: arrayOf(group) }, { assignEntity }); +const normalizeWithMergeIntoEntity = normalize([], { groups: arrayOf(group) }, { mergeIntoEntity }); diff --git a/test/typescript/schema.ts b/test/typescript/schema.ts new file mode 100644 index 00000000..81b10e46 --- /dev/null +++ b/test/typescript/schema.ts @@ -0,0 +1,38 @@ +import {Schema, IterableSchema, UnionSchema, SchemaValue} from "../../index.d.ts"; + +const idAttribute = "slug"; +const meta = { removeProps: ["year", "publisher"] }; + +const generateSlug = function (entity: any) { + if (entity.slug != null) { + return "slug"; + } + return "id"; +}; + +const assignEntity = function (output: any, key: string, value: any, input: any, schema: SchemaValue) { + const itemSchema = (schema instanceof IterableSchema || schema instanceof UnionSchema) ? + schema.getItemSchema() : schema; + const removeProps = (itemSchema instanceof Schema) ? + itemSchema.getMeta("removeProps") : null; + if (!removeProps || removeProps.indexOf(key) < 0) { + output[key] = value; + } +}; + +const schemaWithKey = new Schema("articles"); +const schemaWithStringIdAttribute = new Schema("articles", { idAttribute }); +const schemaWithFunctionIdAttribute = new Schema("articles", { idAttribute: generateSlug }); +const schemaWithMeta = new Schema("articles", { meta }); +const schemaWithAssignEntity = new Schema("articles", { meta, assignEntity }); + +const someKey: string = schemaWithKey.getKey(); +const someIdAttribute: string = schemaWithStringIdAttribute.getIdAttribute(); +const someOtherIdAttribute: string = schemaWithFunctionIdAttribute.getIdAttribute(); +const someMeta: any = schemaWithMeta.getMeta("removeProps"); + +const article = new Schema("articles"); +const user = new Schema("users"); +article.define({ + author: user +});