diff --git a/package.json b/package.json index 72e30ad..d3d2dcd 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "2.6.0", "description": "Knockout Mapping plugin", "main": "dist/knockout.mapping.js", + "types": "types/knockout.mapping.d.ts", "files": [ "dist", "HISTORY.md" @@ -47,4 +48,4 @@ "url": "https://github.com/crissdev/knockout.mapping/issues" }, "license": "MIT" -} +} \ No newline at end of file diff --git a/types/knockout.mapping.d.ts b/types/knockout.mapping.d.ts new file mode 100644 index 0000000..a9ab7d2 --- /dev/null +++ b/types/knockout.mapping.d.ts @@ -0,0 +1,261 @@ +import * as ko from "knockout"; + +declare module "knockout" { + export module mapping { + export type MappedObservable = { + [P in keyof T]: + T[P] extends ko.Observable | ko.ObservableArray ? T[P] : + T[P] extends string | boolean | number | Date ? ko.Observable : + T[P] extends Array ? ko.ObservableArray> : + T[P] extends Function ? T[P] : + T[P] extends object ? MappedObservable : + T[P]; + }; + + export type MappingOptions = MappingOptionsBase & MappingOptionsSpecific; + + export interface MappingOptionsBase { + ignore?: (keyof T)[]; + include?: (keyof T)[]; + copy?: (keyof T)[]; + observe?: (keyof T)[]; + mappedProperties?: (keyof T)[]; + deferEvaluation?: boolean; + } + + export interface MappingOptionsProperty extends MappingOptionsBase { + create?: (options: CreateOptions) => void; + update?: (options: UpdateOptions) => void; + key?: (data: T) => any; + } + + export type MappingOptionsSpecific = { + [P in keyof T]?: + T[P] extends Array ? MappingOptionsProperty : + MappingOptionsProperty; + }; + + export interface CreateOptions { + data: T; + parent: any; + } + + export interface UpdateOptions { + data: T; + parent: any; + target: any; + observable?: ko.Observable; + } + + export interface VisitModelOptions { + visitedObjects?: any; + parentName?: string; + ignore?: string[]; + copy?: string[]; + include?: string[]; + } + + /** + * Checks if an object was created using `knockout.mapping`. + * @param viewModel View model object to be checked. + */ + export function isMapped(viewModel: any): boolean; + + /** + * Updates target observable with value from the source. + * + * @param source Plain JavaScript value to be mapped. + * @param target Observable to be updated. + */ + export function fromJS(source: string, target: ko.Observable): ko.Observable; + /** + * Creates an observable wrapping source's value. + * If 'target' is supplied, instead, target observable is updated. + * + * @param source Plain JavaScript value to be mapped. + * @param options The mapping options. + * @param target Observable to be updated. + */ + export function fromJS(source: string, inputOptions?: MappingOptions, target?: ko.Observable): ko.Observable; + + /** + * Updates target observable with value from the source. + * + * @param source Plain JavaScript value to be mapped. + * @param target Observable to be updated. + */ + export function fromJS(source: number, target: ko.Observable): ko.Observable; + /** + * Creates an observable wrapping source's value. + * If 'target' is supplied, instead, target observable is updated. + * + * @param source Plain JavaScript value to be mapped. + * @param options The mapping options. + * @param target Observable to be updated. + */ + export function fromJS(source: number, inputOptions?: MappingOptions, target?: ko.Observable): ko.Observable; + + /** + * Updates target observable with value from the source. + * + * @param source Plain JavaScript value to be mapped. + * @param target Observable to be updated. + */ + export function fromJS(source: boolean, target: ko.Observable): ko.Observable; + /** + * Creates an observable wrapping source's value. + * If 'target' is supplied, instead, target observable is updated. + * + * @param source Plain JavaScript value to be mapped. + * @param options The mapping options. + * @param target Observable to be updated. + */ + export function fromJS(source: boolean, inputOptions?: MappingOptions, target?: ko.Observable): ko.Observable; + + /** + * Creates a view model object with observable properties for each of the properties on the source. + * + * @param source Plain JavaScript array to be mapped. + */ + export function fromJS(source: SourceT[]): ko.ObservableArray>; + + /** + * Creates a view model object with observable properties for each of the properties on the source. + * + * @param source Plain JavaScript array to be mapped. + * @param inputOptions The mappings options with no properties. + */ + export function fromJS(source: SourceT[], inputOptions: {}): ko.ObservableArray>; + + /** + * Creates a view model object with observable properties for each of the properties on the source. + * If 'target' is supplied, instead, target's observable properties are updated. + * + * @param source Plain JavaScript array to be mapped. + * @param options The mapping options. + * @param target View model object previously mapped to be updated. + */ + export function fromJS(source: SourceT[], inputOptions: MappingOptions, target?: ko.ObservableArray): ko.ObservableArray; + /** + * Updates target's observable properties with those of the sources. + * + * @param source Plain JavaScript array to be mapped. + * @param target View model object previously mapped to be updated. + */ + export function fromJS(source: SourceT[], target: ko.ObservableArray): ko.ObservableArray; + + /** + * Creates a view model object with observable properties for each of the properties on the source. + * + * @param source Plain JavaScript object to be mapped. + */ + export function fromJS(source: SourceT): MappedObservable; + + /** + * Creates a view model object with observable properties for each of the properties on the source. + * + * @param source Plain JavaScript object to be mapped. + * @param inputOptions The mappings options with no properties. + */ + export function fromJS(source: SourceT, inputOptions: {}): MappedObservable; + + /** + * Creates a view model object with observable properties for each of the properties on the source. + * If 'target' is supplied, instead, target's observable properties are updated. + * + * @param source Plain JavaScript object to be mapped. + * @param options The mapping options. + * @param target View model object previously mapped to be updated. + */ + export function fromJS(source: SourceT, inputOptions: MappingOptions, target?: MappedT): MappedT; + /** + * Updates target's observable properties with those of the sources. + * + * @param source Plain JavaScript object to be mapped. + * @param target View model object previously mapped to be updated. + */ + export function fromJS(source: SourceT, target: MappedT): MappedT; + + /** + * Creates a view model object with observable properties for each of the properties on the source. + * If 'target' is supplied, instead, target's observable properties are updated. + * + * @param jsonString JSON of a JavaScript object to be mapped. + * @param options Options on mapping behavior. + * @param target View model object previosly mapped to be updated. + */ + export function fromJSON(jsonString: string, inputOptions?: MappingOptions, target?: MappedT): MappedT; + /** + * Updates target's observable properties with those of the sources. + * + * @param jsonString JSON of a JavaScript object to be mapped. + * @param target View model object previously mapped to be updated. + */ + export function fromJSON(jsonString: string, target: MappedT): MappedT; + + /** + * Creates an unmapped object containing only the properties of the mapped object that were part of your original JS object. + * + * @param rootObject Object with observables to be converted. + * @param options The mapping options + */ + export function toJS(rootObject: MappedT, options?: MappingOptions): SourceT; + + /** + * Creates an unmapped object containing only the properties of the mapped object that were part of your original JS object. + * Stringify the result. + * + * @param rootObject Object with observables to be converted. + * @param options The mapping options. + * @param replacer Same as JSON.stringify + * @param space Sam as JSON.stringify + */ + export function toJSON(rootObject: SourceT, options?: MappingOptions, replacer?: (this: any, key: string, value: any) => any, space?: string | number): string; + + /** Get the default mapping options. */ + export function defaultOptions(): MappingOptions; + /** + * Sets the default mapping options. + * + * @param options The new default options. + */ + export function defaultOptions(options: MappingOptions): void; + + /** Undocumented. Reset Mapping default options to the original ones. */ + export function resetDefaultOptions(): void; + + /** + * Undocumented. Custom implementation of JavaScript's typeof. + * + * @param x Object to check type. + */ + export function getType(x: any): string; + + /** + * Undocumented. Visit an object and executes callback on each properties. + * + * @param rootObject The root object to visit. + * @param callback The callback which is executed on each properties. + * @param options The options for the visiting. + */ + export function visitModel(rootObject: Object, callback: (propertyValue: any, parentName: string) => any, options?: VisitModelOptions): T; + } + + export interface ObservableArrayFunctions { + mappedCreate(item: T): T; + + mappedRemove(item: T): T[]; + mappedRemove(removeFunction: (item: T) => boolean): T[]; + + mappedRemoveAll(): T[]; + mappedRemoveAll(items: T[]): T[]; + + mappedDestroy(item: T): void; + mappedDestroy(destroyFunction: (item: T) => boolean): void; + + mappedDestroyAll(): void; + mappedDestroyAll(items: T[]): void; + } +} + +export = ko.mapping; diff --git a/types/knockout.mapping.spec.ts b/types/knockout.mapping.spec.ts new file mode 100644 index 0000000..0a866a2 --- /dev/null +++ b/types/knockout.mapping.spec.ts @@ -0,0 +1,129 @@ +import "./knockout.mapping"; +import * as ko from "knockout"; + +/* + * Basic Usage + */ + +interface SimpleObject { + serverTime: string; + numUsers: number; +} + +const simpleData: SimpleObject = { + serverTime: "2010-01-07", + numUsers: 3 +}; + +const simpleVM = ko.mapping.fromJS(simpleData); +simpleVM.serverTime("2010-01-08"); +simpleVM.numUsers(5); + +ko.mapping.fromJS(simpleData, simpleVM); + +/* + * Advanced Usage + */ + +interface AdvancedData { + name: string; + children: AdvancedDataChild[]; +} +interface AdvancedDataChild { + id: number; + name: string; +} + +interface AdvancedVM extends ko.mapping.MappedObservable { +} + +const advancedData: AdvancedData = { + name: "Scott", + children: [ + { id: 1, name: "Alice" } + ] +}; + +const advancedVM = ko.mapping.fromJS(advancedData); +advancedVM.name("test"); +advancedVM.children()[0].id(2); + +const advancedMapping: ko.mapping.MappingOptions = { + children: { + key(data) { + return ko.unwrap(data.id); + } + } +}; + +const advancedVM2 = ko.mapping.fromJS(advancedData, advancedMapping); +advancedVM2.name("test"); +advancedVM2.children()[0].id(2); + +class AdvancedVMChild { + id: ko.Observable; + name: ko.Observable; + + constructor(data: AdvancedDataChild) { + this.id = ko.observable(data.id); + this.name = ko.observable(data.name); + } +} + +interface AdvancedVMWithChildren { + name: ko.Observable; + children: ko.ObservableArray; +} + +const advancedMappingChildren: ko.mapping.MappingOptions = { + children: { + key(data) { + return data.id; + }, + create(opts) { + return new AdvancedVMChild(opts.data); + } + } +}; + +const advancedVMChildren = ko.mapping.fromJS(advancedData, advancedMappingChildren); +advancedVMChildren.name("test"); +advancedVMChildren.children()[0].id(5); + +const advancedMappingUpdate: ko.mapping.MappingOptions = { + name: { + update(opts) { + return opts.data + " updated!"; + } + } +}; + +const advancedVMUpdate = ko.mapping.fromJS(advancedData, advancedMappingUpdate); + +const advancedMappingOptions: ko.mapping.MappingOptions = { + ignore: ["name"], + include: ["children"], + copy: ["children"], + observe: ["name"] +} + +/* + * Automatic Mapping + */ + +const autoMapped = ko.mapping.fromJS(simpleData); +autoMapped.numUsers(); +autoMapped.serverTime(); + +const autoMappedWithOptions = ko.mapping.fromJS(simpleData, {}); +autoMappedWithOptions.numUsers(); +autoMappedWithOptions.serverTime(); + +const simpleDataArray = [simpleData]; +const autoMappedArray = ko.mapping.fromJS(simpleDataArray); +autoMappedArray()[0].numUsers(); +autoMappedArray()[0].serverTime(); + +const autoMappedArrayWithOptions = ko.mapping.fromJS(simpleDataArray, {}); +autoMappedArrayWithOptions()[0].numUsers(); +autoMappedArrayWithOptions()[0].serverTime(); diff --git a/types/tsconfig.json b/types/tsconfig.json new file mode 100644 index 0000000..f1d627e --- /dev/null +++ b/types/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "umd", + "moduleResolution": "node", + "strict": true + } +} \ No newline at end of file