Comparing version 1.6.0 to 1.7.0-rc1
@@ -19,2 +19,3 @@ "use strict"; | ||
this.deserializationStrategy = new Map([ | ||
[type_descriptor_1.AnyT.ctor, helpers_1.identity], | ||
[Number, deserializeDirectly], | ||
@@ -162,2 +163,3 @@ [String, deserializeDirectly], | ||
if (sourceObjectMetadata !== undefined) { | ||
sourceObjectMetadata.processDeferredKnownTypes(); | ||
knownTypeConstructors = deserializer.mergeKnownTypes(knownTypeConstructors, deserializer.createKnownTypesMap(sourceObjectMetadata.knownTypes)); | ||
@@ -195,3 +197,3 @@ if (sourceObjectMetadata.typeResolver != null) { | ||
else { | ||
revivedValue = deserializer.convertSingleValue(objMemberValue, objMemberMetadata.type, knownTypeConstructors, objMemberDebugName, objMemberOptions); | ||
revivedValue = deserializer.convertSingleValue(objMemberValue, objMemberMetadata.type(), knownTypeConstructors, objMemberDebugName, objMemberOptions); | ||
} | ||
@@ -198,0 +200,0 @@ if (helpers_1.isValueDefined(revivedValue) |
@@ -10,5 +10,7 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.identity = exports.nameof = exports.isReflectMetadataSupported = exports.isInstanceOf = exports.isValueDefined = exports.logWarning = exports.logMessage = exports.logError = exports.isSubtypeOf = exports.parseToJSObject = exports.shouldOmitParseString = exports.isObject = exports.isTypeTypedArray = exports.isDirectlyDeserializableNativeType = exports.isDirectlySerializableNativeType = exports.MISSING_REFLECT_CONF_MSG = void 0; | ||
exports.MISSING_REFLECT_CONF_MSG = 'Are you sure, that you have both "experimentalDecorators"' | ||
+ ' and "emitDecoratorMetadata" in your tsconfig.json?'; | ||
exports.identity = exports.nameof = exports.isReflectMetadataSupported = exports.isInstanceOf = exports.isValueDefined = exports.logWarning = exports.logMessage = exports.logError = exports.isSubtypeOf = exports.parseToJSObject = exports.shouldOmitParseString = exports.isObject = exports.isTypeTypedArray = exports.isDirectlyDeserializableNativeType = exports.isDirectlySerializableNativeType = exports.MISSING_REFLECT_CONF_MSG = exports.LAZY_TYPE_EXPLANATION = void 0; | ||
var type_descriptor_1 = require("./type-descriptor"); | ||
exports.LAZY_TYPE_EXPLANATION = "If the type is not yet defined, for example due to circular references, add '() => ' before it. E.g. @jsonMember(() => Foo)"; | ||
exports.MISSING_REFLECT_CONF_MSG = 'Make sure that you have both "experimentalDecorators"' | ||
+ ' and "emitDecoratorMetadata" enabled in your tsconfig.json'; | ||
function isDirectlySerializableNativeType(type) { | ||
@@ -106,3 +108,6 @@ return [Date, Number, String, Boolean].indexOf(type) !== -1; | ||
function isInstanceOf(value, constructor) { | ||
if (typeof value === 'number') { | ||
if (constructor === type_descriptor_1.AnyT.ctor) { | ||
return true; | ||
} | ||
else if (typeof value === 'number') { | ||
return constructor === Number; | ||
@@ -109,0 +114,0 @@ } |
@@ -33,2 +33,3 @@ "use strict"; | ||
Object.defineProperty(exports, "ArrayT", { enumerable: true, get: function () { return type_descriptor_1.ArrayT; } }); | ||
Object.defineProperty(exports, "AnyT", { enumerable: true, get: function () { return type_descriptor_1.AnyT; } }); | ||
Object.defineProperty(exports, "SetT", { enumerable: true, get: function () { return type_descriptor_1.SetT; } }); | ||
@@ -35,0 +36,0 @@ Object.defineProperty(exports, "MapT", { enumerable: true, get: function () { return type_descriptor_1.MapT; } }); |
@@ -8,3 +8,3 @@ "use strict"; | ||
var type_descriptor_1 = require("./type-descriptor"); | ||
function jsonArrayMember(elementConstructor, options) { | ||
function jsonArrayMember(maybeTypeThunk, options) { | ||
if (options === void 0) { options = {}; } | ||
@@ -14,6 +14,3 @@ return function (target, propKey) { | ||
var decoratorName = "@jsonArrayMember on " + helpers_1.nameof(target.constructor) + "." + String(propKey); | ||
if (!type_descriptor_1.isTypelike(elementConstructor)) { | ||
helpers_1.logError(decoratorName + ": could not resolve constructor of array elements at runtime."); | ||
return; | ||
} | ||
var typeThunk = type_descriptor_1.ensureTypeThunk(maybeTypeThunk, decoratorName); | ||
var dimensions = options.dimensions == null ? 1 : options.dimensions; | ||
@@ -30,3 +27,3 @@ if (!isNaN(dimensions) && dimensions < 1) { | ||
metadata_1.injectMetadataInformation(target, propKey, { | ||
type: createArrayType(type_descriptor_1.ensureTypeDescriptor(elementConstructor), dimensions), | ||
type: function () { return createArrayType(type_descriptor_1.ensureTypeDescriptor(typeThunk()), dimensions); }, | ||
emitDefaultValue: options.emitDefaultValue, | ||
@@ -33,0 +30,0 @@ isRequired: options.isRequired, |
@@ -8,3 +8,3 @@ "use strict"; | ||
var type_descriptor_1 = require("./type-descriptor"); | ||
function jsonMapMember(keyConstructor, valueConstructor, options) { | ||
function jsonMapMember(maybeKeyThunk, maybeValueThunk, options) { | ||
if (options === void 0) { options = {}; } | ||
@@ -14,10 +14,4 @@ return function (target, propKey) { | ||
var decoratorName = "@jsonMapMember on " + helpers_1.nameof(target.constructor) + "." + String(propKey); | ||
if (!type_descriptor_1.isTypelike(keyConstructor)) { | ||
helpers_1.logError(decoratorName + ": could not resolve constructor of map keys at runtime."); | ||
return; | ||
} | ||
if (!type_descriptor_1.isTypelike(valueConstructor)) { | ||
helpers_1.logError(decoratorName + ": could not resolve constructor of map values at runtime."); | ||
return; | ||
} | ||
var keyThunk = type_descriptor_1.ensureTypeThunk(maybeKeyThunk, decoratorName); | ||
var valueThunk = type_descriptor_1.ensureTypeThunk(maybeValueThunk, decoratorName); | ||
if (helpers_1.isReflectMetadataSupported | ||
@@ -29,3 +23,3 @@ && Reflect.getMetadata('design:type', target, propKey) !== Map) { | ||
metadata_1.injectMetadataInformation(target, propKey, { | ||
type: type_descriptor_1.MapT(keyConstructor, valueConstructor, { shape: options.shape }), | ||
type: function () { return type_descriptor_1.MapT(keyThunk(), valueThunk(), { shape: options.shape }); }, | ||
emitDefaultValue: options.emitDefaultValue, | ||
@@ -32,0 +26,0 @@ isRequired: options.isRequired, |
@@ -8,79 +8,94 @@ "use strict"; | ||
var type_descriptor_1 = require("./type-descriptor"); | ||
function jsonMember(optionsOrPrototype, propKey) { | ||
if (propKey !== undefined | ||
&& (typeof propKey === 'string' || typeof propKey === 'symbol')) { | ||
function jsonMember(optionsOrPrototype, propertyKeyOrOptions) { | ||
if (propertyKeyOrOptions !== undefined | ||
&& (typeof propertyKeyOrOptions === 'string' || typeof propertyKeyOrOptions === 'symbol')) { | ||
var property = propertyKeyOrOptions; | ||
var prototype = optionsOrPrototype; | ||
var decoratorName = "@jsonMember on " + helpers_1.nameof(prototype.constructor) + "." + String(propKey); | ||
var decoratorName = "@jsonMember on " + helpers_1.nameof(prototype.constructor) + "." + String(property); | ||
if (!helpers_1.isReflectMetadataSupported) { | ||
helpers_1.logError(decoratorName + ": ReflectDecorators is required if no 'constructor' option is" | ||
+ " specified."); | ||
helpers_1.logError(decoratorName + ": ReflectDecorators is required if the type is not explicitly provided with e.g. @jsonMember(Number)"); | ||
return; | ||
} | ||
var reflectPropCtor = Reflect.getMetadata('design:type', prototype, propKey); | ||
var reflectPropCtor = Reflect.getMetadata('design:type', prototype, property); | ||
if (reflectPropCtor == null) { | ||
helpers_1.logError(decoratorName + ": could not resolve detected property constructor at runtime." + helpers_1.MISSING_REFLECT_CONF_MSG); | ||
helpers_1.logError(decoratorName + ": could not resolve detected property constructor at runtime. Potential solutions:\n - " + helpers_1.LAZY_TYPE_EXPLANATION + "\n - " + helpers_1.MISSING_REFLECT_CONF_MSG); | ||
return; | ||
} | ||
var typeDescriptor = type_descriptor_1.ensureTypeDescriptor(reflectPropCtor); | ||
if (isSpecialPropertyType(decoratorName, typeDescriptor)) { | ||
var typeDescriptor_1 = type_descriptor_1.ensureTypeDescriptor(reflectPropCtor); | ||
if (isSpecialPropertyType(decoratorName, typeDescriptor_1)) { | ||
return; | ||
} | ||
metadata_1.injectMetadataInformation(prototype, propKey, { | ||
type: typeDescriptor, | ||
key: propKey.toString(), | ||
name: propKey.toString(), | ||
metadata_1.injectMetadataInformation(prototype, property, { | ||
type: function () { return typeDescriptor_1; }, | ||
key: propertyKeyOrOptions.toString(), | ||
name: propertyKeyOrOptions.toString(), | ||
}); | ||
return; | ||
} | ||
else { | ||
return function (target, _propKey) { | ||
var _a, _b; | ||
var options = (_a = optionsOrPrototype) !== null && _a !== void 0 ? _a : {}; | ||
var typeDescriptor; | ||
var decoratorName = "@jsonMember on " + helpers_1.nameof(target.constructor) + "." + String(_propKey); | ||
if (options.hasOwnProperty('constructor')) { | ||
if (!helpers_1.isValueDefined(options.constructor)) { | ||
helpers_1.logError(decoratorName + ": cannot resolve specified property constructor at" | ||
+ ' runtime.'); | ||
return; | ||
} | ||
typeDescriptor = type_descriptor_1.ensureTypeDescriptor(options.constructor); | ||
if (helpers_1.isReflectMetadataSupported && !helpers_1.isSubtypeOf(typeDescriptor.ctor, Reflect.getMetadata('design:type', target, _propKey))) { | ||
helpers_1.logWarning(decoratorName + ": detected property type does not match" | ||
+ " 'constructor' option."); | ||
} | ||
return function (target, _propKey) { | ||
var _a, _b; | ||
var decoratorName = "@jsonMember on " + helpers_1.nameof(target.constructor) + "." + String(_propKey); | ||
var hasTypeThunk = typeof optionsOrPrototype === 'function'; | ||
var typeThunk = hasTypeThunk | ||
? type_descriptor_1.ensureTypeThunk(optionsOrPrototype, decoratorName) | ||
: undefined; | ||
var options = (_a = (hasTypeThunk | ||
? propertyKeyOrOptions | ||
: optionsOrPrototype)) !== null && _a !== void 0 ? _a : {}; | ||
var typeDescriptor; | ||
if (options.hasOwnProperty('constructor')) { | ||
if (hasTypeThunk) { | ||
throw new Error('Cannot both define constructor option and type. Only one allowed.'); | ||
} | ||
else if (helpers_1.isReflectMetadataSupported) { | ||
var reflectCtor = Reflect.getMetadata('design:type', target, _propKey); | ||
if (reflectCtor == null) { | ||
helpers_1.logError(decoratorName + ": cannot resolve detected property constructor at" | ||
+ " runtime."); | ||
return; | ||
} | ||
typeDescriptor = type_descriptor_1.ensureTypeDescriptor(reflectCtor); | ||
} | ||
else if (options.deserializer === undefined) { | ||
helpers_1.logError(decoratorName + ": ReflectDecorators is required if no 'constructor' option" | ||
+ " is specified."); | ||
if (!helpers_1.isValueDefined(options.constructor)) { | ||
helpers_1.logError(decoratorName + ": cannot resolve specified property constructor at runtime. " + helpers_1.LAZY_TYPE_EXPLANATION); | ||
return; | ||
} | ||
if (typeDescriptor !== undefined | ||
&& isSpecialPropertyType(decoratorName, typeDescriptor)) { | ||
var newTypeDescriptor_1 = type_descriptor_1.ensureTypeDescriptor(options.constructor); | ||
typeDescriptor = function () { return newTypeDescriptor_1; }; | ||
if (helpers_1.isReflectMetadataSupported && !helpers_1.isSubtypeOf(newTypeDescriptor_1.ctor, Reflect.getMetadata('design:type', target, _propKey))) { | ||
helpers_1.logWarning(decoratorName + ": detected property type does not match" | ||
+ " 'constructor' option."); | ||
} | ||
} | ||
else if (hasTypeThunk) { | ||
typeDescriptor = typeThunk; | ||
} | ||
else if (helpers_1.isReflectMetadataSupported) { | ||
var reflectCtor_1 = Reflect.getMetadata('design:type', target, _propKey); | ||
if (reflectCtor_1 == null) { | ||
helpers_1.logError(decoratorName + ": cannot resolve detected property constructor atruntime. " + helpers_1.LAZY_TYPE_EXPLANATION); | ||
return; | ||
} | ||
metadata_1.injectMetadataInformation(target, _propKey, { | ||
type: typeDescriptor, | ||
emitDefaultValue: options.emitDefaultValue, | ||
isRequired: options.isRequired, | ||
options: options_base_1.extractOptionBase(options), | ||
key: _propKey.toString(), | ||
name: (_b = options.name) !== null && _b !== void 0 ? _b : _propKey.toString(), | ||
deserializer: options.deserializer, | ||
serializer: options.serializer, | ||
}); | ||
}; | ||
} | ||
typeDescriptor = function () { return type_descriptor_1.ensureTypeDescriptor(reflectCtor_1); }; | ||
} | ||
else if (options.deserializer === undefined) { | ||
helpers_1.logError(decoratorName + ": Cannot determine type"); | ||
return; | ||
} | ||
var typeToTest = typeDescriptor === null || typeDescriptor === void 0 ? void 0 : typeDescriptor(); | ||
if (typeToTest !== undefined && isSpecialPropertyType(decoratorName, typeToTest)) { | ||
return; | ||
} | ||
metadata_1.injectMetadataInformation(target, _propKey, { | ||
type: typeDescriptor === undefined | ||
? undefined | ||
: function () { return type_descriptor_1.ensureTypeDescriptor(typeDescriptor()); }, | ||
emitDefaultValue: options.emitDefaultValue, | ||
isRequired: options.isRequired, | ||
options: options_base_1.extractOptionBase(options), | ||
key: _propKey.toString(), | ||
name: (_b = options.name) !== null && _b !== void 0 ? _b : _propKey.toString(), | ||
deserializer: options.deserializer, | ||
serializer: options.serializer, | ||
}); | ||
}; | ||
} | ||
exports.jsonMember = jsonMember; | ||
function isConstructorEqual(type, constructor) { | ||
return type instanceof type_descriptor_1.TypeDescriptor ? type.ctor === constructor : type === constructor; | ||
} | ||
function isSpecialPropertyType(decoratorName, typeDescriptor) { | ||
if (!(typeDescriptor instanceof type_descriptor_1.ArrayTypeDescriptor) && typeDescriptor.ctor === Array) { | ||
if (!(typeDescriptor instanceof type_descriptor_1.ArrayTypeDescriptor) | ||
&& isConstructorEqual(typeDescriptor, Array)) { | ||
helpers_1.logError(decoratorName + ": property is an Array. Use the jsonArrayMember decorator to" | ||
@@ -90,3 +105,3 @@ + " serialize this property."); | ||
} | ||
if (!(typeDescriptor instanceof type_descriptor_1.SetTypeDescriptor) && typeDescriptor.ctor === Set) { | ||
if (!(typeDescriptor instanceof type_descriptor_1.SetTypeDescriptor) && isConstructorEqual(typeDescriptor, Set)) { | ||
helpers_1.logError(decoratorName + ": property is a Set. Use the jsonSetMember decorator to" | ||
@@ -96,3 +111,3 @@ + " serialize this property."); | ||
} | ||
if (!(typeDescriptor instanceof type_descriptor_1.MapTypeDescriptor) && typeDescriptor.ctor === Map) { | ||
if (!(typeDescriptor instanceof type_descriptor_1.MapTypeDescriptor) && isConstructorEqual(typeDescriptor, Map)) { | ||
helpers_1.logError(decoratorName + ": property is a Map. Use the jsonMapMember decorator to" | ||
@@ -99,0 +114,0 @@ + " serialize this property."); |
@@ -8,3 +8,3 @@ "use strict"; | ||
var type_descriptor_1 = require("./type-descriptor"); | ||
function jsonSetMember(elementConstructor, options) { | ||
function jsonSetMember(maybeTypeThunk, options) { | ||
if (options === void 0) { options = {}; } | ||
@@ -14,6 +14,3 @@ return function (target, propKey) { | ||
var decoratorName = "@jsonSetMember on " + helpers_1.nameof(target.constructor) + "." + String(propKey); | ||
if (!type_descriptor_1.isTypelike(elementConstructor)) { | ||
helpers_1.logError(decoratorName + ": could not resolve constructor of set elements at runtime."); | ||
return; | ||
} | ||
var typeThunk = type_descriptor_1.ensureTypeThunk(maybeTypeThunk, decoratorName); | ||
if (helpers_1.isReflectMetadataSupported | ||
@@ -25,3 +22,3 @@ && Reflect.getMetadata('design:type', target, propKey) !== Set) { | ||
metadata_1.injectMetadataInformation(target, propKey, { | ||
type: type_descriptor_1.SetT(elementConstructor), | ||
type: function () { return type_descriptor_1.SetT(typeThunk()); }, | ||
emitDefaultValue: options.emitDefaultValue, | ||
@@ -28,0 +25,0 @@ isRequired: options.isRequired, |
@@ -10,2 +10,3 @@ "use strict"; | ||
this.knownTypes = new Set(); | ||
this.knownTypesDeferred = []; | ||
this.isExplicitlyMarked = false; | ||
@@ -69,2 +70,9 @@ this.isHandledWithoutAnnotation = false; | ||
}; | ||
JsonObjectMetadata.prototype.processDeferredKnownTypes = function () { | ||
var _this = this; | ||
this.knownTypesDeferred.forEach(function (typeThunk) { | ||
typeThunk().getTypes().forEach(function (ctor) { return _this.knownTypes.add(ctor); }); | ||
}); | ||
this.knownTypesDeferred = []; | ||
}; | ||
return JsonObjectMetadata; | ||
@@ -90,3 +98,3 @@ }()); | ||
if (metadata.deserializer === undefined) { | ||
metadata.type.getTypes().forEach(function (ctor) { return objectMetadata.knownTypes.add(ctor); }); | ||
objectMetadata.knownTypesDeferred.push(metadata.type); | ||
} | ||
@@ -93,0 +101,0 @@ Object.keys(metadata) |
@@ -144,2 +144,3 @@ "use strict"; | ||
if (rootMetadata !== undefined) { | ||
rootMetadata.processDeferredKnownTypes(); | ||
rootMetadata.knownTypes.forEach(function (knownTypeCtor) { | ||
@@ -146,0 +147,0 @@ knownTypes.set(_this.nameResolver(knownTypeCtor), knownTypeCtor); |
@@ -31,2 +31,3 @@ "use strict"; | ||
this.serializationStrategy = new Map([ | ||
[type_descriptor_1.AnyT.ctor, helpers_1.identity], | ||
[Date, helpers_1.identity], | ||
@@ -154,3 +155,3 @@ [Number, helpers_1.identity], | ||
else { | ||
serialized = serializer.convertSingleValue(sourceObject[objMemberMetadata.key], objMemberMetadata.type, helpers_1.nameof(sourceMeta_1.classType) + "." + objMemberMetadata.key, objMemberOptions); | ||
serialized = serializer.convertSingleValue(sourceObject[objMemberMetadata.key], objMemberMetadata.type(), helpers_1.nameof(sourceMeta_1.classType) + "." + objMemberMetadata.key, objMemberOptions); | ||
} | ||
@@ -157,0 +158,0 @@ if ((serializer.retrievePreserveNull(objMemberOptions) && serialized === null) |
@@ -16,3 +16,4 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.ensureTypeDescriptor = exports.isTypelike = exports.MapT = exports.MapTypeDescriptor = exports.SetT = exports.SetTypeDescriptor = exports.ArrayT = exports.ArrayTypeDescriptor = exports.GenericTypeDescriptor = exports.ConcreteTypeDescriptor = exports.TypeDescriptor = void 0; | ||
exports.ensureTypeThunk = exports.ensureTypeDescriptor = exports.isTypelike = exports.AnyT = exports.MapT = exports.MapTypeDescriptor = exports.SetT = exports.SetTypeDescriptor = exports.ArrayT = exports.ArrayTypeDescriptor = exports.GenericTypeDescriptor = exports.ConcreteTypeDescriptor = exports.TypeDescriptor = void 0; | ||
var helpers_1 = require("./helpers"); | ||
var TypeDescriptor = (function () { | ||
@@ -106,2 +107,3 @@ function TypeDescriptor(ctor) { | ||
exports.MapT = MapT; | ||
exports.AnyT = new ConcreteTypeDescriptor(function () { return undefined; }); | ||
function isTypelike(type) { | ||
@@ -115,2 +117,15 @@ return type != null && (typeof type === 'function' || type instanceof TypeDescriptor); | ||
exports.ensureTypeDescriptor = ensureTypeDescriptor; | ||
function ensureTypeThunk(typeThunkOrSerializable, decoratorName) { | ||
if (typeThunkOrSerializable == null) { | ||
throw new Error("No type given on " + decoratorName + ". " + helpers_1.LAZY_TYPE_EXPLANATION); | ||
} | ||
if (typeThunkOrSerializable instanceof TypeDescriptor) { | ||
return function () { return typeThunkOrSerializable; }; | ||
} | ||
if (typeThunkOrSerializable.name !== '') { | ||
return function () { return typeThunkOrSerializable; }; | ||
} | ||
return typeThunkOrSerializable; | ||
} | ||
exports.ensureTypeThunk = ensureTypeThunk; | ||
//# sourceMappingURL=type-descriptor.js.map |
@@ -1,5 +0,5 @@ | ||
import { isSubtypeOf, isValueDefined, logError, nameof } from './helpers'; | ||
import { identity, isSubtypeOf, isValueDefined, logError, nameof } from './helpers'; | ||
import { JsonObjectMetadata } from './metadata'; | ||
import { getOptionValue, mergeOptions } from './options-base'; | ||
import { ArrayTypeDescriptor, ConcreteTypeDescriptor, MapTypeDescriptor, SetTypeDescriptor, } from './type-descriptor'; | ||
import { AnyT, ArrayTypeDescriptor, ConcreteTypeDescriptor, MapTypeDescriptor, SetTypeDescriptor, } from './type-descriptor'; | ||
export function defaultTypeResolver(sourceObject, knownTypes) { | ||
@@ -15,2 +15,3 @@ if (sourceObject.__type != null) { | ||
this.deserializationStrategy = new Map([ | ||
[AnyT.ctor, identity], | ||
[Number, deserializeDirectly], | ||
@@ -149,2 +150,3 @@ [String, deserializeDirectly], | ||
if (sourceObjectMetadata !== undefined) { | ||
sourceObjectMetadata.processDeferredKnownTypes(); | ||
knownTypeConstructors = deserializer.mergeKnownTypes(knownTypeConstructors, deserializer.createKnownTypesMap(sourceObjectMetadata.knownTypes)); | ||
@@ -182,3 +184,3 @@ if (sourceObjectMetadata.typeResolver != null) { | ||
else { | ||
revivedValue = deserializer.convertSingleValue(objMemberValue, objMemberMetadata.type, knownTypeConstructors, objMemberDebugName, objMemberOptions); | ||
revivedValue = deserializer.convertSingleValue(objMemberValue, objMemberMetadata.type(), knownTypeConstructors, objMemberDebugName, objMemberOptions); | ||
} | ||
@@ -185,0 +187,0 @@ if (isValueDefined(revivedValue) |
@@ -1,3 +0,6 @@ | ||
export const MISSING_REFLECT_CONF_MSG = 'Are you sure, that you have both "experimentalDecorators"' | ||
+ ' and "emitDecoratorMetadata" in your tsconfig.json?'; | ||
import { AnyT } from './type-descriptor'; | ||
export const LAZY_TYPE_EXPLANATION = `If the type is not yet defined, for example due to circular \ | ||
references, add '() => ' before it. E.g. @jsonMember(() => Foo)`; | ||
export const MISSING_REFLECT_CONF_MSG = 'Make sure that you have both "experimentalDecorators"' | ||
+ ' and "emitDecoratorMetadata" enabled in your tsconfig.json'; | ||
export function isDirectlySerializableNativeType(type) { | ||
@@ -72,3 +75,6 @@ return [Date, Number, String, Boolean].indexOf(type) !== -1; | ||
export function isInstanceOf(value, constructor) { | ||
if (typeof value === 'number') { | ||
if (constructor === AnyT.ctor) { | ||
return true; | ||
} | ||
else if (typeof value === 'number') { | ||
return constructor === Number; | ||
@@ -75,0 +81,0 @@ } |
@@ -9,4 +9,4 @@ export { TypedJSON, defaultTypeResolver, defaultTypeEmitter, } from './parser'; | ||
export { toJson } from './to-json'; | ||
export { ArrayT, SetT, MapT, SetTypeDescriptor, ArrayTypeDescriptor, MapTypeDescriptor, } from './type-descriptor'; | ||
export { ArrayT, AnyT, SetT, MapT, SetTypeDescriptor, ArrayTypeDescriptor, MapTypeDescriptor, } from './type-descriptor'; | ||
export * from './types'; | ||
//# sourceMappingURL=index.js.map |
import { isReflectMetadataSupported, logError, MISSING_REFLECT_CONF_MSG, nameof } from './helpers'; | ||
import { injectMetadataInformation } from './metadata'; | ||
import { extractOptionBase } from './options-base'; | ||
import { ArrayTypeDescriptor, ensureTypeDescriptor, isTypelike, } from './type-descriptor'; | ||
export function jsonArrayMember(elementConstructor, options = {}) { | ||
import { ArrayTypeDescriptor, ensureTypeDescriptor, ensureTypeThunk, } from './type-descriptor'; | ||
export function jsonArrayMember(maybeTypeThunk, options = {}) { | ||
return (target, propKey) => { | ||
var _a; | ||
const decoratorName = `@jsonArrayMember on ${nameof(target.constructor)}.${String(propKey)}`; | ||
if (!isTypelike(elementConstructor)) { | ||
logError(`${decoratorName}: could not resolve constructor of array elements at runtime.`); | ||
return; | ||
} | ||
const typeThunk = ensureTypeThunk(maybeTypeThunk, decoratorName); | ||
const dimensions = options.dimensions == null ? 1 : options.dimensions; | ||
@@ -24,3 +21,3 @@ if (!isNaN(dimensions) && dimensions < 1) { | ||
injectMetadataInformation(target, propKey, { | ||
type: createArrayType(ensureTypeDescriptor(elementConstructor), dimensions), | ||
type: () => createArrayType(ensureTypeDescriptor(typeThunk()), dimensions), | ||
emitDefaultValue: options.emitDefaultValue, | ||
@@ -27,0 +24,0 @@ isRequired: options.isRequired, |
import { isReflectMetadataSupported, logError, MISSING_REFLECT_CONF_MSG, nameof } from './helpers'; | ||
import { injectMetadataInformation } from './metadata'; | ||
import { extractOptionBase } from './options-base'; | ||
import { isTypelike, MapT } from './type-descriptor'; | ||
export function jsonMapMember(keyConstructor, valueConstructor, options = {}) { | ||
import { ensureTypeThunk, MapT } from './type-descriptor'; | ||
export function jsonMapMember(maybeKeyThunk, maybeValueThunk, options = {}) { | ||
return (target, propKey) => { | ||
var _a; | ||
const decoratorName = `@jsonMapMember on ${nameof(target.constructor)}.${String(propKey)}`; | ||
if (!isTypelike(keyConstructor)) { | ||
logError(`${decoratorName}: could not resolve constructor of map keys at runtime.`); | ||
return; | ||
} | ||
if (!isTypelike(valueConstructor)) { | ||
logError(`${decoratorName}: could not resolve constructor of map values at runtime.`); | ||
return; | ||
} | ||
const keyThunk = ensureTypeThunk(maybeKeyThunk, decoratorName); | ||
const valueThunk = ensureTypeThunk(maybeValueThunk, decoratorName); | ||
if (isReflectMetadataSupported | ||
@@ -23,3 +17,3 @@ && Reflect.getMetadata('design:type', target, propKey) !== Map) { | ||
injectMetadataInformation(target, propKey, { | ||
type: MapT(keyConstructor, valueConstructor, { shape: options.shape }), | ||
type: () => MapT(keyThunk(), valueThunk(), { shape: options.shape }), | ||
emitDefaultValue: options.emitDefaultValue, | ||
@@ -26,0 +20,0 @@ isRequired: options.isRequired, |
@@ -1,18 +0,22 @@ | ||
import { isReflectMetadataSupported, isSubtypeOf, isValueDefined, logError, logWarning, MISSING_REFLECT_CONF_MSG, nameof, } from './helpers'; | ||
import { isReflectMetadataSupported, isSubtypeOf, isValueDefined, LAZY_TYPE_EXPLANATION, logError, logWarning, MISSING_REFLECT_CONF_MSG, nameof, } from './helpers'; | ||
import { injectMetadataInformation } from './metadata'; | ||
import { extractOptionBase } from './options-base'; | ||
import { ArrayTypeDescriptor, ensureTypeDescriptor, MapTypeDescriptor, SetTypeDescriptor, } from './type-descriptor'; | ||
export function jsonMember(optionsOrPrototype, propKey) { | ||
if (propKey !== undefined | ||
&& (typeof propKey === 'string' || typeof propKey === 'symbol')) { | ||
import { ArrayTypeDescriptor, ensureTypeDescriptor, ensureTypeThunk, MapTypeDescriptor, SetTypeDescriptor, TypeDescriptor, } from './type-descriptor'; | ||
export function jsonMember(optionsOrPrototype, propertyKeyOrOptions) { | ||
if (propertyKeyOrOptions !== undefined | ||
&& (typeof propertyKeyOrOptions === 'string' || typeof propertyKeyOrOptions === 'symbol')) { | ||
const property = propertyKeyOrOptions; | ||
const prototype = optionsOrPrototype; | ||
const decoratorName = `@jsonMember on ${nameof(prototype.constructor)}.${String(propKey)}`; | ||
const decoratorName = `@jsonMember on ${nameof(prototype.constructor)}.${String(property)}`; | ||
if (!isReflectMetadataSupported) { | ||
logError(`${decoratorName}: ReflectDecorators is required if no 'constructor' option is` | ||
+ ` specified.`); | ||
logError(`${decoratorName}: ReflectDecorators is required if the type is not \ | ||
explicitly provided with e.g. @jsonMember(Number)`); | ||
return; | ||
} | ||
const reflectPropCtor = Reflect.getMetadata('design:type', prototype, propKey); | ||
const reflectPropCtor = Reflect.getMetadata('design:type', prototype, property); | ||
if (reflectPropCtor == null) { | ||
logError(`${decoratorName}: could not resolve detected property constructor at runtime.${MISSING_REFLECT_CONF_MSG}`); | ||
logError(`${decoratorName}: could not resolve detected property constructor at \ | ||
runtime. Potential solutions: | ||
- ${LAZY_TYPE_EXPLANATION} | ||
- ${MISSING_REFLECT_CONF_MSG}`); | ||
return; | ||
@@ -24,59 +28,76 @@ } | ||
} | ||
injectMetadataInformation(prototype, propKey, { | ||
type: typeDescriptor, | ||
key: propKey.toString(), | ||
name: propKey.toString(), | ||
injectMetadataInformation(prototype, property, { | ||
type: () => typeDescriptor, | ||
key: propertyKeyOrOptions.toString(), | ||
name: propertyKeyOrOptions.toString(), | ||
}); | ||
return; | ||
} | ||
else { | ||
return (target, _propKey) => { | ||
var _a, _b; | ||
const options = (_a = optionsOrPrototype) !== null && _a !== void 0 ? _a : {}; | ||
let typeDescriptor; | ||
const decoratorName = `@jsonMember on ${nameof(target.constructor)}.${String(_propKey)}`; | ||
if (options.hasOwnProperty('constructor')) { | ||
if (!isValueDefined(options.constructor)) { | ||
logError(`${decoratorName}: cannot resolve specified property constructor at` | ||
+ ' runtime.'); | ||
return; | ||
} | ||
typeDescriptor = ensureTypeDescriptor(options.constructor); | ||
if (isReflectMetadataSupported && !isSubtypeOf(typeDescriptor.ctor, Reflect.getMetadata('design:type', target, _propKey))) { | ||
logWarning(`${decoratorName}: detected property type does not match` | ||
+ ` 'constructor' option.`); | ||
} | ||
return (target, _propKey) => { | ||
var _a, _b; | ||
const decoratorName = `@jsonMember on ${nameof(target.constructor)}.${String(_propKey)}`; | ||
const hasTypeThunk = typeof optionsOrPrototype === 'function'; | ||
const typeThunk = hasTypeThunk | ||
? ensureTypeThunk(optionsOrPrototype, decoratorName) | ||
: undefined; | ||
const options = (_a = (hasTypeThunk | ||
? propertyKeyOrOptions | ||
: optionsOrPrototype)) !== null && _a !== void 0 ? _a : {}; | ||
let typeDescriptor; | ||
if (options.hasOwnProperty('constructor')) { | ||
if (hasTypeThunk) { | ||
throw new Error('Cannot both define constructor option and type. Only one allowed.'); | ||
} | ||
else if (isReflectMetadataSupported) { | ||
const reflectCtor = Reflect.getMetadata('design:type', target, _propKey); | ||
if (reflectCtor == null) { | ||
logError(`${decoratorName}: cannot resolve detected property constructor at` | ||
+ ` runtime.`); | ||
return; | ||
} | ||
typeDescriptor = ensureTypeDescriptor(reflectCtor); | ||
} | ||
else if (options.deserializer === undefined) { | ||
logError(`${decoratorName}: ReflectDecorators is required if no 'constructor' option` | ||
+ ` is specified.`); | ||
if (!isValueDefined(options.constructor)) { | ||
logError(`${decoratorName}: cannot resolve specified property constructor at \ | ||
runtime. ${LAZY_TYPE_EXPLANATION}`); | ||
return; | ||
} | ||
if (typeDescriptor !== undefined | ||
&& isSpecialPropertyType(decoratorName, typeDescriptor)) { | ||
const newTypeDescriptor = ensureTypeDescriptor(options.constructor); | ||
typeDescriptor = () => newTypeDescriptor; | ||
if (isReflectMetadataSupported && !isSubtypeOf(newTypeDescriptor.ctor, Reflect.getMetadata('design:type', target, _propKey))) { | ||
logWarning(`${decoratorName}: detected property type does not match` | ||
+ ` 'constructor' option.`); | ||
} | ||
} | ||
else if (hasTypeThunk) { | ||
typeDescriptor = typeThunk; | ||
} | ||
else if (isReflectMetadataSupported) { | ||
const reflectCtor = Reflect.getMetadata('design:type', target, _propKey); | ||
if (reflectCtor == null) { | ||
logError(`${decoratorName}: cannot resolve detected property constructor at\ | ||
runtime. ${LAZY_TYPE_EXPLANATION}`); | ||
return; | ||
} | ||
injectMetadataInformation(target, _propKey, { | ||
type: typeDescriptor, | ||
emitDefaultValue: options.emitDefaultValue, | ||
isRequired: options.isRequired, | ||
options: extractOptionBase(options), | ||
key: _propKey.toString(), | ||
name: (_b = options.name) !== null && _b !== void 0 ? _b : _propKey.toString(), | ||
deserializer: options.deserializer, | ||
serializer: options.serializer, | ||
}); | ||
}; | ||
} | ||
typeDescriptor = () => ensureTypeDescriptor(reflectCtor); | ||
} | ||
else if (options.deserializer === undefined) { | ||
logError(`${decoratorName}: Cannot determine type`); | ||
return; | ||
} | ||
const typeToTest = typeDescriptor === null || typeDescriptor === void 0 ? void 0 : typeDescriptor(); | ||
if (typeToTest !== undefined && isSpecialPropertyType(decoratorName, typeToTest)) { | ||
return; | ||
} | ||
injectMetadataInformation(target, _propKey, { | ||
type: typeDescriptor === undefined | ||
? undefined | ||
: () => ensureTypeDescriptor(typeDescriptor()), | ||
emitDefaultValue: options.emitDefaultValue, | ||
isRequired: options.isRequired, | ||
options: extractOptionBase(options), | ||
key: _propKey.toString(), | ||
name: (_b = options.name) !== null && _b !== void 0 ? _b : _propKey.toString(), | ||
deserializer: options.deserializer, | ||
serializer: options.serializer, | ||
}); | ||
}; | ||
} | ||
function isConstructorEqual(type, constructor) { | ||
return type instanceof TypeDescriptor ? type.ctor === constructor : type === constructor; | ||
} | ||
function isSpecialPropertyType(decoratorName, typeDescriptor) { | ||
if (!(typeDescriptor instanceof ArrayTypeDescriptor) && typeDescriptor.ctor === Array) { | ||
if (!(typeDescriptor instanceof ArrayTypeDescriptor) | ||
&& isConstructorEqual(typeDescriptor, Array)) { | ||
logError(`${decoratorName}: property is an Array. Use the jsonArrayMember decorator to` | ||
@@ -86,3 +107,3 @@ + ` serialize this property.`); | ||
} | ||
if (!(typeDescriptor instanceof SetTypeDescriptor) && typeDescriptor.ctor === Set) { | ||
if (!(typeDescriptor instanceof SetTypeDescriptor) && isConstructorEqual(typeDescriptor, Set)) { | ||
logError(`${decoratorName}: property is a Set. Use the jsonSetMember decorator to` | ||
@@ -92,3 +113,3 @@ + ` serialize this property.`); | ||
} | ||
if (!(typeDescriptor instanceof MapTypeDescriptor) && typeDescriptor.ctor === Map) { | ||
if (!(typeDescriptor instanceof MapTypeDescriptor) && isConstructorEqual(typeDescriptor, Map)) { | ||
logError(`${decoratorName}: property is a Map. Use the jsonMapMember decorator to` | ||
@@ -95,0 +116,0 @@ + ` serialize this property.`); |
import { isReflectMetadataSupported, logError, MISSING_REFLECT_CONF_MSG, nameof } from './helpers'; | ||
import { injectMetadataInformation } from './metadata'; | ||
import { extractOptionBase } from './options-base'; | ||
import { isTypelike, SetT } from './type-descriptor'; | ||
export function jsonSetMember(elementConstructor, options = {}) { | ||
import { ensureTypeThunk, SetT } from './type-descriptor'; | ||
export function jsonSetMember(maybeTypeThunk, options = {}) { | ||
return (target, propKey) => { | ||
var _a; | ||
const decoratorName = `@jsonSetMember on ${nameof(target.constructor)}.${String(propKey)}`; | ||
if (!isTypelike(elementConstructor)) { | ||
logError(`${decoratorName}: could not resolve constructor of set elements at runtime.`); | ||
return; | ||
} | ||
const typeThunk = ensureTypeThunk(maybeTypeThunk, decoratorName); | ||
if (isReflectMetadataSupported | ||
@@ -19,3 +16,3 @@ && Reflect.getMetadata('design:type', target, propKey) !== Set) { | ||
injectMetadataInformation(target, propKey, { | ||
type: SetT(elementConstructor), | ||
type: () => SetT(typeThunk()), | ||
emitDefaultValue: options.emitDefaultValue, | ||
@@ -22,0 +19,0 @@ isRequired: options.isRequired, |
@@ -7,2 +7,3 @@ import { isDirectlySerializableNativeType, isTypeTypedArray, logError, nameof } from './helpers'; | ||
this.knownTypes = new Set(); | ||
this.knownTypesDeferred = []; | ||
this.isExplicitlyMarked = false; | ||
@@ -66,2 +67,8 @@ this.isHandledWithoutAnnotation = false; | ||
} | ||
processDeferredKnownTypes() { | ||
this.knownTypesDeferred.forEach(typeThunk => { | ||
typeThunk().getTypes().forEach(ctor => this.knownTypes.add(ctor)); | ||
}); | ||
this.knownTypesDeferred = []; | ||
} | ||
} | ||
@@ -85,3 +92,3 @@ export function injectMetadataInformation(prototype, propKey, metadata) { | ||
if (metadata.deserializer === undefined) { | ||
metadata.type.getTypes().forEach(ctor => objectMetadata.knownTypes.add(ctor)); | ||
objectMetadata.knownTypesDeferred.push(metadata.type); | ||
} | ||
@@ -88,0 +95,0 @@ Object.keys(metadata) |
@@ -127,2 +127,3 @@ import { defaultTypeResolver, Deserializer } from './deserializer'; | ||
if (rootMetadata !== undefined) { | ||
rootMetadata.processDeferredKnownTypes(); | ||
rootMetadata.knownTypes.forEach(knownTypeCtor => { | ||
@@ -129,0 +130,0 @@ knownTypes.set(this.nameResolver(knownTypeCtor), knownTypeCtor); |
import { identity, isInstanceOf, isValueDefined, logError, nameof, } from './helpers'; | ||
import { JsonObjectMetadata } from './metadata'; | ||
import { getOptionValue, mergeOptions } from './options-base'; | ||
import { ArrayTypeDescriptor, MapTypeDescriptor, SetTypeDescriptor, } from './type-descriptor'; | ||
import { AnyT, ArrayTypeDescriptor, MapTypeDescriptor, SetTypeDescriptor, } from './type-descriptor'; | ||
export function defaultTypeEmitter(targetObject, sourceObject, expectedSourceType, sourceTypeMetadata) { | ||
@@ -16,2 +16,3 @@ var _a; | ||
this.serializationStrategy = new Map([ | ||
[AnyT.ctor, identity], | ||
[Date, identity], | ||
@@ -136,3 +137,3 @@ [Number, identity], | ||
else { | ||
serialized = serializer.convertSingleValue(sourceObject[objMemberMetadata.key], objMemberMetadata.type, `${nameof(sourceMeta.classType)}.${objMemberMetadata.key}`, objMemberOptions); | ||
serialized = serializer.convertSingleValue(sourceObject[objMemberMetadata.key], objMemberMetadata.type(), `${nameof(sourceMeta.classType)}.${objMemberMetadata.key}`, objMemberOptions); | ||
} | ||
@@ -139,0 +140,0 @@ if ((serializer.retrievePreserveNull(objMemberOptions) && serialized === null) |
@@ -0,1 +1,2 @@ | ||
import { LAZY_TYPE_EXPLANATION } from './helpers'; | ||
export class TypeDescriptor { | ||
@@ -66,2 +67,3 @@ constructor(ctor) { | ||
} | ||
export const AnyT = new ConcreteTypeDescriptor(() => undefined); | ||
export function isTypelike(type) { | ||
@@ -73,2 +75,14 @@ return type != null && (typeof type === 'function' || type instanceof TypeDescriptor); | ||
} | ||
export function ensureTypeThunk(typeThunkOrSerializable, decoratorName) { | ||
if (typeThunkOrSerializable == null) { | ||
throw new Error(`No type given on ${decoratorName}. ${LAZY_TYPE_EXPLANATION}`); | ||
} | ||
if (typeThunkOrSerializable instanceof TypeDescriptor) { | ||
return () => typeThunkOrSerializable; | ||
} | ||
if (typeThunkOrSerializable.name !== '') { | ||
return () => typeThunkOrSerializable; | ||
} | ||
return typeThunkOrSerializable; | ||
} | ||
//# sourceMappingURL=type-descriptor.js.map |
@@ -1,5 +0,5 @@ | ||
import { isSubtypeOf, isValueDefined, logError, nameof } from './helpers'; | ||
import { identity, isSubtypeOf, isValueDefined, logError, nameof } from './helpers'; | ||
import { JsonObjectMetadata } from './metadata'; | ||
import { getOptionValue, mergeOptions } from './options-base'; | ||
import { ArrayTypeDescriptor, ConcreteTypeDescriptor, MapTypeDescriptor, SetTypeDescriptor, } from './type-descriptor'; | ||
import { AnyT, ArrayTypeDescriptor, ConcreteTypeDescriptor, MapTypeDescriptor, SetTypeDescriptor, } from './type-descriptor'; | ||
export function defaultTypeResolver(sourceObject, knownTypes) { | ||
@@ -15,2 +15,3 @@ if (sourceObject.__type != null) { | ||
this.deserializationStrategy = new Map([ | ||
[AnyT.ctor, identity], | ||
[Number, deserializeDirectly], | ||
@@ -158,2 +159,3 @@ [String, deserializeDirectly], | ||
if (sourceObjectMetadata !== undefined) { | ||
sourceObjectMetadata.processDeferredKnownTypes(); | ||
knownTypeConstructors = deserializer.mergeKnownTypes(knownTypeConstructors, deserializer.createKnownTypesMap(sourceObjectMetadata.knownTypes)); | ||
@@ -191,3 +193,3 @@ if (sourceObjectMetadata.typeResolver != null) { | ||
else { | ||
revivedValue = deserializer.convertSingleValue(objMemberValue, objMemberMetadata.type, knownTypeConstructors, objMemberDebugName, objMemberOptions); | ||
revivedValue = deserializer.convertSingleValue(objMemberValue, objMemberMetadata.type(), knownTypeConstructors, objMemberDebugName, objMemberOptions); | ||
} | ||
@@ -194,0 +196,0 @@ if (isValueDefined(revivedValue) |
import { __spreadArrays } from "tslib"; | ||
export var MISSING_REFLECT_CONF_MSG = 'Are you sure, that you have both "experimentalDecorators"' | ||
+ ' and "emitDecoratorMetadata" in your tsconfig.json?'; | ||
import { AnyT } from './type-descriptor'; | ||
export var LAZY_TYPE_EXPLANATION = "If the type is not yet defined, for example due to circular references, add '() => ' before it. E.g. @jsonMember(() => Foo)"; | ||
export var MISSING_REFLECT_CONF_MSG = 'Make sure that you have both "experimentalDecorators"' | ||
+ ' and "emitDecoratorMetadata" enabled in your tsconfig.json'; | ||
export function isDirectlySerializableNativeType(type) { | ||
@@ -85,3 +87,6 @@ return [Date, Number, String, Boolean].indexOf(type) !== -1; | ||
export function isInstanceOf(value, constructor) { | ||
if (typeof value === 'number') { | ||
if (constructor === AnyT.ctor) { | ||
return true; | ||
} | ||
else if (typeof value === 'number') { | ||
return constructor === Number; | ||
@@ -88,0 +93,0 @@ } |
@@ -9,4 +9,4 @@ export { TypedJSON, defaultTypeResolver, defaultTypeEmitter, } from './parser'; | ||
export { toJson } from './to-json'; | ||
export { ArrayT, SetT, MapT, SetTypeDescriptor, ArrayTypeDescriptor, MapTypeDescriptor, } from './type-descriptor'; | ||
export { ArrayT, AnyT, SetT, MapT, SetTypeDescriptor, ArrayTypeDescriptor, MapTypeDescriptor, } from './type-descriptor'; | ||
export * from './types'; | ||
//# sourceMappingURL=index.js.map |
import { isReflectMetadataSupported, logError, MISSING_REFLECT_CONF_MSG, nameof } from './helpers'; | ||
import { injectMetadataInformation } from './metadata'; | ||
import { extractOptionBase } from './options-base'; | ||
import { ArrayTypeDescriptor, ensureTypeDescriptor, isTypelike, } from './type-descriptor'; | ||
export function jsonArrayMember(elementConstructor, options) { | ||
import { ArrayTypeDescriptor, ensureTypeDescriptor, ensureTypeThunk, } from './type-descriptor'; | ||
export function jsonArrayMember(maybeTypeThunk, options) { | ||
if (options === void 0) { options = {}; } | ||
@@ -10,6 +10,3 @@ return function (target, propKey) { | ||
var decoratorName = "@jsonArrayMember on " + nameof(target.constructor) + "." + String(propKey); | ||
if (!isTypelike(elementConstructor)) { | ||
logError(decoratorName + ": could not resolve constructor of array elements at runtime."); | ||
return; | ||
} | ||
var typeThunk = ensureTypeThunk(maybeTypeThunk, decoratorName); | ||
var dimensions = options.dimensions == null ? 1 : options.dimensions; | ||
@@ -26,3 +23,3 @@ if (!isNaN(dimensions) && dimensions < 1) { | ||
injectMetadataInformation(target, propKey, { | ||
type: createArrayType(ensureTypeDescriptor(elementConstructor), dimensions), | ||
type: function () { return createArrayType(ensureTypeDescriptor(typeThunk()), dimensions); }, | ||
emitDefaultValue: options.emitDefaultValue, | ||
@@ -29,0 +26,0 @@ isRequired: options.isRequired, |
import { isReflectMetadataSupported, logError, MISSING_REFLECT_CONF_MSG, nameof } from './helpers'; | ||
import { injectMetadataInformation } from './metadata'; | ||
import { extractOptionBase } from './options-base'; | ||
import { isTypelike, MapT } from './type-descriptor'; | ||
export function jsonMapMember(keyConstructor, valueConstructor, options) { | ||
import { ensureTypeThunk, MapT } from './type-descriptor'; | ||
export function jsonMapMember(maybeKeyThunk, maybeValueThunk, options) { | ||
if (options === void 0) { options = {}; } | ||
@@ -10,10 +10,4 @@ return function (target, propKey) { | ||
var decoratorName = "@jsonMapMember on " + nameof(target.constructor) + "." + String(propKey); | ||
if (!isTypelike(keyConstructor)) { | ||
logError(decoratorName + ": could not resolve constructor of map keys at runtime."); | ||
return; | ||
} | ||
if (!isTypelike(valueConstructor)) { | ||
logError(decoratorName + ": could not resolve constructor of map values at runtime."); | ||
return; | ||
} | ||
var keyThunk = ensureTypeThunk(maybeKeyThunk, decoratorName); | ||
var valueThunk = ensureTypeThunk(maybeValueThunk, decoratorName); | ||
if (isReflectMetadataSupported | ||
@@ -25,3 +19,3 @@ && Reflect.getMetadata('design:type', target, propKey) !== Map) { | ||
injectMetadataInformation(target, propKey, { | ||
type: MapT(keyConstructor, valueConstructor, { shape: options.shape }), | ||
type: function () { return MapT(keyThunk(), valueThunk(), { shape: options.shape }); }, | ||
emitDefaultValue: options.emitDefaultValue, | ||
@@ -28,0 +22,0 @@ isRequired: options.isRequired, |
@@ -1,81 +0,96 @@ | ||
import { isReflectMetadataSupported, isSubtypeOf, isValueDefined, logError, logWarning, MISSING_REFLECT_CONF_MSG, nameof, } from './helpers'; | ||
import { isReflectMetadataSupported, isSubtypeOf, isValueDefined, LAZY_TYPE_EXPLANATION, logError, logWarning, MISSING_REFLECT_CONF_MSG, nameof, } from './helpers'; | ||
import { injectMetadataInformation } from './metadata'; | ||
import { extractOptionBase } from './options-base'; | ||
import { ArrayTypeDescriptor, ensureTypeDescriptor, MapTypeDescriptor, SetTypeDescriptor, } from './type-descriptor'; | ||
export function jsonMember(optionsOrPrototype, propKey) { | ||
if (propKey !== undefined | ||
&& (typeof propKey === 'string' || typeof propKey === 'symbol')) { | ||
import { ArrayTypeDescriptor, ensureTypeDescriptor, ensureTypeThunk, MapTypeDescriptor, SetTypeDescriptor, TypeDescriptor, } from './type-descriptor'; | ||
export function jsonMember(optionsOrPrototype, propertyKeyOrOptions) { | ||
if (propertyKeyOrOptions !== undefined | ||
&& (typeof propertyKeyOrOptions === 'string' || typeof propertyKeyOrOptions === 'symbol')) { | ||
var property = propertyKeyOrOptions; | ||
var prototype = optionsOrPrototype; | ||
var decoratorName = "@jsonMember on " + nameof(prototype.constructor) + "." + String(propKey); | ||
var decoratorName = "@jsonMember on " + nameof(prototype.constructor) + "." + String(property); | ||
if (!isReflectMetadataSupported) { | ||
logError(decoratorName + ": ReflectDecorators is required if no 'constructor' option is" | ||
+ " specified."); | ||
logError(decoratorName + ": ReflectDecorators is required if the type is not explicitly provided with e.g. @jsonMember(Number)"); | ||
return; | ||
} | ||
var reflectPropCtor = Reflect.getMetadata('design:type', prototype, propKey); | ||
var reflectPropCtor = Reflect.getMetadata('design:type', prototype, property); | ||
if (reflectPropCtor == null) { | ||
logError(decoratorName + ": could not resolve detected property constructor at runtime." + MISSING_REFLECT_CONF_MSG); | ||
logError(decoratorName + ": could not resolve detected property constructor at runtime. Potential solutions:\n - " + LAZY_TYPE_EXPLANATION + "\n - " + MISSING_REFLECT_CONF_MSG); | ||
return; | ||
} | ||
var typeDescriptor = ensureTypeDescriptor(reflectPropCtor); | ||
if (isSpecialPropertyType(decoratorName, typeDescriptor)) { | ||
var typeDescriptor_1 = ensureTypeDescriptor(reflectPropCtor); | ||
if (isSpecialPropertyType(decoratorName, typeDescriptor_1)) { | ||
return; | ||
} | ||
injectMetadataInformation(prototype, propKey, { | ||
type: typeDescriptor, | ||
key: propKey.toString(), | ||
name: propKey.toString(), | ||
injectMetadataInformation(prototype, property, { | ||
type: function () { return typeDescriptor_1; }, | ||
key: propertyKeyOrOptions.toString(), | ||
name: propertyKeyOrOptions.toString(), | ||
}); | ||
return; | ||
} | ||
else { | ||
return function (target, _propKey) { | ||
var _a, _b; | ||
var options = (_a = optionsOrPrototype) !== null && _a !== void 0 ? _a : {}; | ||
var typeDescriptor; | ||
var decoratorName = "@jsonMember on " + nameof(target.constructor) + "." + String(_propKey); | ||
if (options.hasOwnProperty('constructor')) { | ||
if (!isValueDefined(options.constructor)) { | ||
logError(decoratorName + ": cannot resolve specified property constructor at" | ||
+ ' runtime.'); | ||
return; | ||
} | ||
typeDescriptor = ensureTypeDescriptor(options.constructor); | ||
if (isReflectMetadataSupported && !isSubtypeOf(typeDescriptor.ctor, Reflect.getMetadata('design:type', target, _propKey))) { | ||
logWarning(decoratorName + ": detected property type does not match" | ||
+ " 'constructor' option."); | ||
} | ||
return function (target, _propKey) { | ||
var _a, _b; | ||
var decoratorName = "@jsonMember on " + nameof(target.constructor) + "." + String(_propKey); | ||
var hasTypeThunk = typeof optionsOrPrototype === 'function'; | ||
var typeThunk = hasTypeThunk | ||
? ensureTypeThunk(optionsOrPrototype, decoratorName) | ||
: undefined; | ||
var options = (_a = (hasTypeThunk | ||
? propertyKeyOrOptions | ||
: optionsOrPrototype)) !== null && _a !== void 0 ? _a : {}; | ||
var typeDescriptor; | ||
if (options.hasOwnProperty('constructor')) { | ||
if (hasTypeThunk) { | ||
throw new Error('Cannot both define constructor option and type. Only one allowed.'); | ||
} | ||
else if (isReflectMetadataSupported) { | ||
var reflectCtor = Reflect.getMetadata('design:type', target, _propKey); | ||
if (reflectCtor == null) { | ||
logError(decoratorName + ": cannot resolve detected property constructor at" | ||
+ " runtime."); | ||
return; | ||
} | ||
typeDescriptor = ensureTypeDescriptor(reflectCtor); | ||
} | ||
else if (options.deserializer === undefined) { | ||
logError(decoratorName + ": ReflectDecorators is required if no 'constructor' option" | ||
+ " is specified."); | ||
if (!isValueDefined(options.constructor)) { | ||
logError(decoratorName + ": cannot resolve specified property constructor at runtime. " + LAZY_TYPE_EXPLANATION); | ||
return; | ||
} | ||
if (typeDescriptor !== undefined | ||
&& isSpecialPropertyType(decoratorName, typeDescriptor)) { | ||
var newTypeDescriptor_1 = ensureTypeDescriptor(options.constructor); | ||
typeDescriptor = function () { return newTypeDescriptor_1; }; | ||
if (isReflectMetadataSupported && !isSubtypeOf(newTypeDescriptor_1.ctor, Reflect.getMetadata('design:type', target, _propKey))) { | ||
logWarning(decoratorName + ": detected property type does not match" | ||
+ " 'constructor' option."); | ||
} | ||
} | ||
else if (hasTypeThunk) { | ||
typeDescriptor = typeThunk; | ||
} | ||
else if (isReflectMetadataSupported) { | ||
var reflectCtor_1 = Reflect.getMetadata('design:type', target, _propKey); | ||
if (reflectCtor_1 == null) { | ||
logError(decoratorName + ": cannot resolve detected property constructor atruntime. " + LAZY_TYPE_EXPLANATION); | ||
return; | ||
} | ||
injectMetadataInformation(target, _propKey, { | ||
type: typeDescriptor, | ||
emitDefaultValue: options.emitDefaultValue, | ||
isRequired: options.isRequired, | ||
options: extractOptionBase(options), | ||
key: _propKey.toString(), | ||
name: (_b = options.name) !== null && _b !== void 0 ? _b : _propKey.toString(), | ||
deserializer: options.deserializer, | ||
serializer: options.serializer, | ||
}); | ||
}; | ||
} | ||
typeDescriptor = function () { return ensureTypeDescriptor(reflectCtor_1); }; | ||
} | ||
else if (options.deserializer === undefined) { | ||
logError(decoratorName + ": Cannot determine type"); | ||
return; | ||
} | ||
var typeToTest = typeDescriptor === null || typeDescriptor === void 0 ? void 0 : typeDescriptor(); | ||
if (typeToTest !== undefined && isSpecialPropertyType(decoratorName, typeToTest)) { | ||
return; | ||
} | ||
injectMetadataInformation(target, _propKey, { | ||
type: typeDescriptor === undefined | ||
? undefined | ||
: function () { return ensureTypeDescriptor(typeDescriptor()); }, | ||
emitDefaultValue: options.emitDefaultValue, | ||
isRequired: options.isRequired, | ||
options: extractOptionBase(options), | ||
key: _propKey.toString(), | ||
name: (_b = options.name) !== null && _b !== void 0 ? _b : _propKey.toString(), | ||
deserializer: options.deserializer, | ||
serializer: options.serializer, | ||
}); | ||
}; | ||
} | ||
function isConstructorEqual(type, constructor) { | ||
return type instanceof TypeDescriptor ? type.ctor === constructor : type === constructor; | ||
} | ||
function isSpecialPropertyType(decoratorName, typeDescriptor) { | ||
if (!(typeDescriptor instanceof ArrayTypeDescriptor) && typeDescriptor.ctor === Array) { | ||
if (!(typeDescriptor instanceof ArrayTypeDescriptor) | ||
&& isConstructorEqual(typeDescriptor, Array)) { | ||
logError(decoratorName + ": property is an Array. Use the jsonArrayMember decorator to" | ||
@@ -85,3 +100,3 @@ + " serialize this property."); | ||
} | ||
if (!(typeDescriptor instanceof SetTypeDescriptor) && typeDescriptor.ctor === Set) { | ||
if (!(typeDescriptor instanceof SetTypeDescriptor) && isConstructorEqual(typeDescriptor, Set)) { | ||
logError(decoratorName + ": property is a Set. Use the jsonSetMember decorator to" | ||
@@ -91,3 +106,3 @@ + " serialize this property."); | ||
} | ||
if (!(typeDescriptor instanceof MapTypeDescriptor) && typeDescriptor.ctor === Map) { | ||
if (!(typeDescriptor instanceof MapTypeDescriptor) && isConstructorEqual(typeDescriptor, Map)) { | ||
logError(decoratorName + ": property is a Map. Use the jsonMapMember decorator to" | ||
@@ -94,0 +109,0 @@ + " serialize this property."); |
import { isReflectMetadataSupported, logError, MISSING_REFLECT_CONF_MSG, nameof } from './helpers'; | ||
import { injectMetadataInformation } from './metadata'; | ||
import { extractOptionBase } from './options-base'; | ||
import { isTypelike, SetT } from './type-descriptor'; | ||
export function jsonSetMember(elementConstructor, options) { | ||
import { ensureTypeThunk, SetT } from './type-descriptor'; | ||
export function jsonSetMember(maybeTypeThunk, options) { | ||
if (options === void 0) { options = {}; } | ||
@@ -10,6 +10,3 @@ return function (target, propKey) { | ||
var decoratorName = "@jsonSetMember on " + nameof(target.constructor) + "." + String(propKey); | ||
if (!isTypelike(elementConstructor)) { | ||
logError(decoratorName + ": could not resolve constructor of set elements at runtime."); | ||
return; | ||
} | ||
var typeThunk = ensureTypeThunk(maybeTypeThunk, decoratorName); | ||
if (isReflectMetadataSupported | ||
@@ -21,3 +18,3 @@ && Reflect.getMetadata('design:type', target, propKey) !== Set) { | ||
injectMetadataInformation(target, propKey, { | ||
type: SetT(elementConstructor), | ||
type: function () { return SetT(typeThunk()); }, | ||
emitDefaultValue: options.emitDefaultValue, | ||
@@ -24,0 +21,0 @@ isRequired: options.isRequired, |
@@ -7,2 +7,3 @@ import { isDirectlySerializableNativeType, isTypeTypedArray, logError, nameof } from './helpers'; | ||
this.knownTypes = new Set(); | ||
this.knownTypesDeferred = []; | ||
this.isExplicitlyMarked = false; | ||
@@ -66,2 +67,9 @@ this.isHandledWithoutAnnotation = false; | ||
}; | ||
JsonObjectMetadata.prototype.processDeferredKnownTypes = function () { | ||
var _this = this; | ||
this.knownTypesDeferred.forEach(function (typeThunk) { | ||
typeThunk().getTypes().forEach(function (ctor) { return _this.knownTypes.add(ctor); }); | ||
}); | ||
this.knownTypesDeferred = []; | ||
}; | ||
return JsonObjectMetadata; | ||
@@ -87,3 +95,3 @@ }()); | ||
if (metadata.deserializer === undefined) { | ||
metadata.type.getTypes().forEach(function (ctor) { return objectMetadata.knownTypes.add(ctor); }); | ||
objectMetadata.knownTypesDeferred.push(metadata.type); | ||
} | ||
@@ -90,0 +98,0 @@ Object.keys(metadata) |
@@ -130,2 +130,3 @@ import { __assign } from "tslib"; | ||
if (rootMetadata !== undefined) { | ||
rootMetadata.processDeferredKnownTypes(); | ||
rootMetadata.knownTypes.forEach(function (knownTypeCtor) { | ||
@@ -132,0 +133,0 @@ knownTypes.set(_this.nameResolver(knownTypeCtor), knownTypeCtor); |
@@ -5,3 +5,3 @@ import { __assign } from "tslib"; | ||
import { getOptionValue, mergeOptions } from './options-base'; | ||
import { ArrayTypeDescriptor, MapTypeDescriptor, SetTypeDescriptor, } from './type-descriptor'; | ||
import { AnyT, ArrayTypeDescriptor, MapTypeDescriptor, SetTypeDescriptor, } from './type-descriptor'; | ||
export function defaultTypeEmitter(targetObject, sourceObject, expectedSourceType, sourceTypeMetadata) { | ||
@@ -18,2 +18,3 @@ var _a; | ||
this.serializationStrategy = new Map([ | ||
[AnyT.ctor, identity], | ||
[Date, identity], | ||
@@ -141,3 +142,3 @@ [Number, identity], | ||
else { | ||
serialized = serializer.convertSingleValue(sourceObject[objMemberMetadata.key], objMemberMetadata.type, nameof(sourceMeta_1.classType) + "." + objMemberMetadata.key, objMemberOptions); | ||
serialized = serializer.convertSingleValue(sourceObject[objMemberMetadata.key], objMemberMetadata.type(), nameof(sourceMeta_1.classType) + "." + objMemberMetadata.key, objMemberOptions); | ||
} | ||
@@ -144,0 +145,0 @@ if ((serializer.retrievePreserveNull(objMemberOptions) && serialized === null) |
import { __extends } from "tslib"; | ||
import { LAZY_TYPE_EXPLANATION } from './helpers'; | ||
var TypeDescriptor = (function () { | ||
@@ -87,2 +88,3 @@ function TypeDescriptor(ctor) { | ||
} | ||
export var AnyT = new ConcreteTypeDescriptor(function () { return undefined; }); | ||
export function isTypelike(type) { | ||
@@ -94,2 +96,14 @@ return type != null && (typeof type === 'function' || type instanceof TypeDescriptor); | ||
} | ||
export function ensureTypeThunk(typeThunkOrSerializable, decoratorName) { | ||
if (typeThunkOrSerializable == null) { | ||
throw new Error("No type given on " + decoratorName + ". " + LAZY_TYPE_EXPLANATION); | ||
} | ||
if (typeThunkOrSerializable instanceof TypeDescriptor) { | ||
return function () { return typeThunkOrSerializable; }; | ||
} | ||
if (typeThunkOrSerializable.name !== '') { | ||
return function () { return typeThunkOrSerializable; }; | ||
} | ||
return typeThunkOrSerializable; | ||
} | ||
//# sourceMappingURL=type-descriptor.js.map |
import { Serializable } from './types'; | ||
export declare const LAZY_TYPE_EXPLANATION = "If the type is not yet defined, for example due to circular references, add '() => ' before it. E.g. @jsonMember(() => Foo)"; | ||
export declare const MISSING_REFLECT_CONF_MSG: string; | ||
@@ -3,0 +4,0 @@ /** |
@@ -9,4 +9,4 @@ export { TypedJSON, ITypedJSONSettings, JsonTypes, defaultTypeResolver, defaultTypeEmitter, } from './parser'; | ||
export { toJson, IToJsonOptions } from './to-json'; | ||
export { ArrayT, SetT, MapT, Typelike, MapOptions, SetTypeDescriptor, ArrayTypeDescriptor, MapTypeDescriptor, } from './type-descriptor'; | ||
export { ArrayT, AnyT, SetT, MapT, Typelike, MapOptions, SetTypeDescriptor, ArrayTypeDescriptor, MapTypeDescriptor, } from './type-descriptor'; | ||
export * from './types'; | ||
//# sourceMappingURL=index.d.ts.map |
import { OptionsBase } from './options-base'; | ||
import { ArrayTypeDescriptor, TypeDescriptor } from './type-descriptor'; | ||
import { MaybeTypeThunk } from './types'; | ||
export interface IJsonArrayMemberOptions extends OptionsBase { | ||
@@ -22,8 +23,8 @@ /** When set, indicates that the member must be present when deserializing. */ | ||
* Specifies that a property, of type array, is part of an object when serializing. | ||
* @param elementConstructor Constructor of array elements (e.g. 'Number' for 'number[]', or 'Date' | ||
* @param maybeTypeThunk Constructor of array elements (e.g. 'Number' for 'number[]', or 'Date' | ||
* for 'Date[]'). | ||
* @param options Additional options. | ||
*/ | ||
export declare function jsonArrayMember(elementConstructor: Function | TypeDescriptor, options?: IJsonArrayMemberOptions): (target: Object, propKey: string | symbol) => void; | ||
export declare function jsonArrayMember(maybeTypeThunk: MaybeTypeThunk, options?: IJsonArrayMemberOptions): (target: Object, propKey: string | symbol) => void; | ||
export declare function createArrayType(elementType: TypeDescriptor, dimensions: number): ArrayTypeDescriptor; | ||
//# sourceMappingURL=json-array-member.d.ts.map |
import { OptionsBase } from './options-base'; | ||
import { MapOptions, TypeDescriptor } from './type-descriptor'; | ||
import { MapOptions } from './type-descriptor'; | ||
import { MaybeTypeThunk } from './types'; | ||
export interface IJsonMapMemberOptions extends OptionsBase, Partial<MapOptions> { | ||
@@ -21,7 +22,7 @@ /** When set, indicates that the member must be present when deserializing. */ | ||
* Use this decorator on properties of type Map<K, V>. | ||
* @param keyConstructor Constructor of map keys (e.g. 'Number' for 'Map<number, Date>'). | ||
* @param valueConstructor Constructor of map values (e.g. 'Date' for 'Map<number, Date>'). | ||
* @param maybeKeyThunk Constructor of map keys (e.g. 'Number' for 'Map<number, Date>'). | ||
* @param maybeValueThunk Constructor of map values (e.g. 'Date' for 'Map<number, Date>'). | ||
* @param options Additional options. | ||
*/ | ||
export declare function jsonMapMember(keyConstructor: Function | TypeDescriptor, valueConstructor: Function | TypeDescriptor, options?: IJsonMapMemberOptions): (target: Object, propKey: string | symbol) => void; | ||
export declare function jsonMapMember(maybeKeyThunk: MaybeTypeThunk, maybeValueThunk: MaybeTypeThunk, options?: IJsonMapMemberOptions): (target: Object, propKey: string | symbol) => void; | ||
//# sourceMappingURL=json-map-member.d.ts.map |
import { OptionsBase } from './options-base'; | ||
import { TypeDescriptor } from './type-descriptor'; | ||
import { IndexedObject } from './types'; | ||
import { IndexedObject, MaybeTypeThunk } from './types'; | ||
export interface IJsonMemberOptions extends OptionsBase { | ||
@@ -25,14 +25,16 @@ /** | ||
/** | ||
* Specifies that a property is part of the object when serializing. | ||
* Requires ReflectDecorators. | ||
*/ | ||
export declare function jsonMember<T extends Function>(prototype: IndexedObject, propertyKey: string | symbol): void; | ||
/** | ||
* Specifies that a property is part of the object when serializing, with additional options. | ||
* Omitting the 'constructor' option requires ReflectDecorators and that the property type is always | ||
* explicitly declared. | ||
* @param options Additional options. | ||
* Requires ReflectDecorators. | ||
*/ | ||
export declare function jsonMember(options: IJsonMemberOptions): PropertyDecorator; | ||
/** | ||
* Specifies that a property is part of the object when serializing. | ||
* This call signature requires ReflectDecorators and that the property type is always explicitly | ||
* declared. | ||
* Specifies that a property is part of the object when serializing, with a defined type and extra | ||
* options. | ||
*/ | ||
export declare function jsonMember<T extends Function>(prototype: IndexedObject, propertyKey: string | symbol): void; | ||
export declare function jsonMember(type: MaybeTypeThunk, options?: IJsonMemberOptions): PropertyDecorator; | ||
//# sourceMappingURL=json-member.d.ts.map |
import { OptionsBase } from './options-base'; | ||
import { MaybeTypeThunk } from './types'; | ||
export interface IJsonSetMemberOptions extends OptionsBase { | ||
@@ -20,7 +21,7 @@ /** When set, indicates that the member must be present when deserializing. */ | ||
* Use this decorator on properties of type Set<T>. | ||
* @param elementConstructor Constructor of set elements (e.g. 'Number' for Set<number> or 'Date' | ||
* @param maybeTypeThunk Constructor of set elements (e.g. 'Number' for Set<number> or 'Date' | ||
* for Set<Date>). | ||
* @param options Additional options. | ||
*/ | ||
export declare function jsonSetMember(elementConstructor: Function, options?: IJsonSetMemberOptions): (target: Object, propKey: string | symbol) => void; | ||
export declare function jsonSetMember(maybeTypeThunk: MaybeTypeThunk, options?: IJsonSetMemberOptions): (target: Object, propKey: string | symbol) => void; | ||
//# sourceMappingURL=json-set-member.d.ts.map |
@@ -15,3 +15,3 @@ import { OptionsBase } from './options-base'; | ||
/** Type descriptor of the member. */ | ||
type?: TypeDescriptor | null; | ||
type?: (() => TypeDescriptor) | null; | ||
/** If set, indicates that the member must be present when deserializing. */ | ||
@@ -29,2 +29,4 @@ isRequired?: boolean | null; | ||
knownTypes: Set<Serializable<any>>; | ||
/** Known types to be evaluated when (de)serialization occurs */ | ||
knownTypesDeferred: Array<() => TypeDescriptor>; | ||
/** If present override the global function */ | ||
@@ -70,4 +72,5 @@ typeHintEmitter?: TypeHintEmitter | null; | ||
private static doesHandleWithoutAnnotation; | ||
processDeferredKnownTypes(): void; | ||
} | ||
export declare function injectMetadataInformation(prototype: IndexedObject, propKey: string | symbol, metadata: JsonMemberMetadata): void; | ||
//# sourceMappingURL=metadata.d.ts.map |
@@ -0,1 +1,2 @@ | ||
import { MaybeTypeThunk, TypeThunk } from './types'; | ||
export declare abstract class TypeDescriptor { | ||
@@ -51,4 +52,6 @@ readonly ctor: Function; | ||
export declare function MapT(keyType: Typelike, valueType: Typelike, options?: Partial<MapOptions>): MapTypeDescriptor; | ||
export declare const AnyT: ConcreteTypeDescriptor; | ||
export declare function isTypelike(type: any): type is Typelike; | ||
export declare function ensureTypeDescriptor(type: Typelike): TypeDescriptor; | ||
export declare function ensureTypeThunk(typeThunkOrSerializable: MaybeTypeThunk | null | undefined, decoratorName: string): TypeThunk; | ||
//# sourceMappingURL=type-descriptor.d.ts.map |
@@ -0,4 +1,7 @@ | ||
import { TypeDescriptor } from './type-descriptor'; | ||
export declare type IndexedObject = Object & { | ||
[key: string]: any; | ||
}; | ||
export declare type TypeThunk = () => Serializable<any> | TypeDescriptor; | ||
export declare type MaybeTypeThunk = Serializable<any> | TypeDescriptor | TypeThunk; | ||
export interface AbstractType<T> extends Function { | ||
@@ -5,0 +8,0 @@ prototype: T; |
{ | ||
"name": "typedjson", | ||
"version": "1.6.0", | ||
"version": "1.7.0-rc1", | ||
"description": "Typed JSON parsing and serializing for TypeScript that preserves type information, using decorators. Parse JSON into actual class instances.", | ||
@@ -5,0 +5,0 @@ "main": "./lib/cjs/index.js", |
@@ -70,2 +70,37 @@ [![npm version](https://img.shields.io/npm/v/typedjson.svg?logo=npm&style=for-the-badge)](https://www.npmjs.com/package/typedjson) | ||
### Mapping types | ||
At times, you might find yourself using a custom type such as `Point`, `Decimal`, or `BigInt`. In this case, `TypedJSON.mapType` can be used to define serialization and deserialization functions to prevent the need of repeating on each member. Example: | ||
```typescript | ||
import {jsonObject, jsonMember, TypedJSON} from 'typedjson'; | ||
import * as Decimal from 'decimal.js'; // Or any other library your type originates from | ||
TypedJSON.mapType(BigInt, { | ||
deserializer: json => value == null ? value : new BigInt(json), | ||
serializer: value => value == null ? value : value.toString(), | ||
}); | ||
TypedJSON.mapType(Decimal, { | ||
deserializer: json => value == null ? value : new Decimal(json), | ||
serializer: value => value == null ? value : value.toString(), | ||
}); | ||
@jsonObject | ||
class MappedTypes { | ||
@jsonMember | ||
cryptoKey: BigInt; | ||
@jsonMember | ||
money: Decimal; | ||
} | ||
const result = TypedJSON.parse({cryptoKey: '1234567890123456789', money: '12345.67'}, MappedTypes); | ||
console.log(result.money instanceof Decimal); // true | ||
console.log(typeof result.cryptoKey === 'bigint'); // true | ||
``` | ||
Do note that in order to prevent the values from being parsed as `Number`, losing precision in the process, they have to be strings. | ||
### Collections | ||
@@ -129,2 +164,15 @@ | ||
### Any type | ||
In case you don't want TypedJSON to make any conversion the `Any` type can be used. | ||
```typescript | ||
import {Any, jsonObject, jsonMember} from 'typedjson'; | ||
@jsonObject | ||
class Something { | ||
@jsonMember(Any) | ||
anythingGoes: any; | ||
} | ||
``` | ||
### Using without ReflectDecorators | ||
@@ -142,7 +190,7 @@ | ||
- @jsonMember | ||
+ @jsonMember({ constructor: Number }) | ||
+ @jsonMember(Number) | ||
public prop1: number; | ||
- @jsonMember | ||
+ @jsonMember({ constructor: MySecondDataClass }) | ||
+ @jsonMember(MySecondDataClass) | ||
public prop2: MySecondDataClass; | ||
@@ -229,2 +277,23 @@ } | ||
## Circular references and using types before they are defined | ||
In case you have to use a type before it is defined, or your find yourself in need a data structure with circular references, errors can occur. To resolve these errors, specify the type lazily by using an arrow function as follows: | ||
```diff | ||
import {jsonObject, jsonMember} from 'typedjson'; | ||
@jsonObject | ||
class Foo { | ||
- @jsonMember(Bar) | ||
+ @jsonMember(() => Bar) | ||
bar: Bar; | ||
} | ||
@jsonObject | ||
class Bar { | ||
- @jsonMember(Foo) | ||
+ @jsonMember(() => Foo) | ||
foo: Foo; | ||
} | ||
``` | ||
## Limitations | ||
@@ -269,25 +338,2 @@ | ||
### Class declaration order matters | ||
When referencing a class in a nested object structure, the referenced class must be declared in advance, e.g.: | ||
```typescript | ||
import 'reflect-metadata'; | ||
import { jsonObject, jsonMember, jsonArrayMember, TypedJSON } from 'typedjson'; | ||
@jsonObject | ||
class Employee | ||
{ | ||
@jsonMember | ||
public name: string; | ||
} | ||
@jsonObject | ||
class Company | ||
{ | ||
@jsonArrayMember(Employee) | ||
public employees: Employee[]; | ||
} | ||
``` | ||
### No inferred property types | ||
@@ -294,0 +340,0 @@ |
@@ -1,5 +0,6 @@ | ||
import {isSubtypeOf, isValueDefined, logError, nameof} from './helpers'; | ||
import {identity, isSubtypeOf, isValueDefined, logError, nameof} from './helpers'; | ||
import {JsonObjectMetadata, TypeResolver} from './metadata'; | ||
import {getOptionValue, mergeOptions, OptionsBase} from './options-base'; | ||
import { | ||
AnyT, | ||
ArrayTypeDescriptor, | ||
@@ -47,2 +48,3 @@ ConcreteTypeDescriptor, | ||
// primitives | ||
[AnyT.ctor, identity], | ||
[Number, deserializeDirectly], | ||
@@ -256,2 +258,4 @@ [String, deserializeDirectly], | ||
if (sourceObjectMetadata !== undefined) { | ||
sourceObjectMetadata.processDeferredKnownTypes(); | ||
// Merge known types received from "above" with known types defined on the current type. | ||
@@ -312,3 +316,3 @@ knownTypeConstructors = deserializer.mergeKnownTypes( | ||
objMemberValue, | ||
objMemberMetadata.type, | ||
objMemberMetadata.type(), | ||
knownTypeConstructors, | ||
@@ -315,0 +319,0 @@ objMemberDebugName, |
@@ -0,1 +1,2 @@ | ||
import {AnyT} from './type-descriptor'; | ||
import {Serializable} from './types'; | ||
@@ -7,5 +8,8 @@ | ||
export const MISSING_REFLECT_CONF_MSG = 'Are you sure, that you have both "experimentalDecorators"' | ||
+ ' and "emitDecoratorMetadata" in your tsconfig.json?'; | ||
export const LAZY_TYPE_EXPLANATION = `If the type is not yet defined, for example due to circular \ | ||
references, add '() => ' before it. E.g. @jsonMember(() => Foo)`; | ||
export const MISSING_REFLECT_CONF_MSG = 'Make sure that you have both "experimentalDecorators"' | ||
+ ' and "emitDecoratorMetadata" enabled in your tsconfig.json'; | ||
/** | ||
@@ -112,3 +116,5 @@ * Determines whether the specified type is a type that can be passed on "as-is" into | ||
export function isInstanceOf<T>(value: any, constructor: Function): boolean { | ||
if (typeof value === 'number') { | ||
if (constructor === AnyT.ctor) { | ||
return true; | ||
} else if (typeof value === 'number') { | ||
return constructor === Number; | ||
@@ -115,0 +121,0 @@ } else if (typeof value === 'string') { |
@@ -23,2 +23,3 @@ export { | ||
ArrayT, | ||
AnyT, | ||
SetT, | ||
@@ -25,0 +26,0 @@ MapT, |
@@ -7,5 +7,6 @@ import {isReflectMetadataSupported, logError, MISSING_REFLECT_CONF_MSG, nameof} from './helpers'; | ||
ensureTypeDescriptor, | ||
isTypelike, | ||
ensureTypeThunk, | ||
TypeDescriptor, | ||
} from './type-descriptor'; | ||
import {MaybeTypeThunk, TypeThunk} from './types'; | ||
@@ -41,3 +42,3 @@ declare abstract class Reflect { | ||
* Specifies that a property, of type array, is part of an object when serializing. | ||
* @param elementConstructor Constructor of array elements (e.g. 'Number' for 'number[]', or 'Date' | ||
* @param maybeTypeThunk Constructor of array elements (e.g. 'Number' for 'number[]', or 'Date' | ||
* for 'Date[]'). | ||
@@ -47,3 +48,3 @@ * @param options Additional options. | ||
export function jsonArrayMember( | ||
elementConstructor: Function | TypeDescriptor, | ||
maybeTypeThunk: MaybeTypeThunk, | ||
options: IJsonArrayMemberOptions = {}, | ||
@@ -54,10 +55,4 @@ ) { | ||
`@jsonArrayMember on ${nameof(target.constructor)}.${String(propKey)}`; | ||
const typeThunk: TypeThunk = ensureTypeThunk(maybeTypeThunk, decoratorName); | ||
if (!isTypelike(elementConstructor)) { | ||
logError( | ||
`${decoratorName}: could not resolve constructor of array elements at runtime.`, | ||
); | ||
return; | ||
} | ||
const dimensions = options.dimensions == null ? 1 : options.dimensions; | ||
@@ -78,3 +73,3 @@ if (!isNaN(dimensions) && dimensions < 1) { | ||
injectMetadataInformation(target, propKey, { | ||
type: createArrayType(ensureTypeDescriptor(elementConstructor), dimensions), | ||
type: () => createArrayType(ensureTypeDescriptor(typeThunk()), dimensions), | ||
emitDefaultValue: options.emitDefaultValue, | ||
@@ -81,0 +76,0 @@ isRequired: options.isRequired, |
import {isReflectMetadataSupported, logError, MISSING_REFLECT_CONF_MSG, nameof} from './helpers'; | ||
import {injectMetadataInformation} from './metadata'; | ||
import {extractOptionBase, OptionsBase} from './options-base'; | ||
import {isTypelike, MapOptions, MapT, TypeDescriptor} from './type-descriptor'; | ||
import {ensureTypeThunk, MapOptions, MapT} from './type-descriptor'; | ||
import {MaybeTypeThunk} from './types'; | ||
@@ -33,9 +34,9 @@ declare abstract class Reflect { | ||
* Use this decorator on properties of type Map<K, V>. | ||
* @param keyConstructor Constructor of map keys (e.g. 'Number' for 'Map<number, Date>'). | ||
* @param valueConstructor Constructor of map values (e.g. 'Date' for 'Map<number, Date>'). | ||
* @param maybeKeyThunk Constructor of map keys (e.g. 'Number' for 'Map<number, Date>'). | ||
* @param maybeValueThunk Constructor of map values (e.g. 'Date' for 'Map<number, Date>'). | ||
* @param options Additional options. | ||
*/ | ||
export function jsonMapMember( | ||
keyConstructor: Function | TypeDescriptor, | ||
valueConstructor: Function | TypeDescriptor, | ||
maybeKeyThunk: MaybeTypeThunk, | ||
maybeValueThunk: MaybeTypeThunk, | ||
options: IJsonMapMemberOptions = {}, | ||
@@ -46,13 +47,5 @@ ) { | ||
const decoratorName = `@jsonMapMember on ${nameof(target.constructor)}.${String(propKey)}`; | ||
const keyThunk = ensureTypeThunk(maybeKeyThunk, decoratorName); | ||
const valueThunk = ensureTypeThunk(maybeValueThunk, decoratorName); | ||
if (!isTypelike(keyConstructor)) { | ||
logError(`${decoratorName}: could not resolve constructor of map keys at runtime.`); | ||
return; | ||
} | ||
if (!isTypelike(valueConstructor)) { | ||
logError(`${decoratorName}: could not resolve constructor of map values at runtime.`); | ||
return; | ||
} | ||
// If ReflectDecorators is available, use it to check whether 'jsonMapMember' has been used | ||
@@ -67,3 +60,3 @@ // on a map. Warn if not. | ||
injectMetadataInformation(target, propKey, { | ||
type: MapT(keyConstructor, valueConstructor, {shape: options.shape}), | ||
type: () => MapT(keyThunk(), valueThunk(), {shape: options.shape}), | ||
emitDefaultValue: options.emitDefaultValue, | ||
@@ -70,0 +63,0 @@ isRequired: options.isRequired, |
@@ -5,2 +5,3 @@ import { | ||
isValueDefined, | ||
LAZY_TYPE_EXPLANATION, | ||
logError, | ||
@@ -16,2 +17,3 @@ logWarning, | ||
ensureTypeDescriptor, | ||
ensureTypeThunk, | ||
MapTypeDescriptor, | ||
@@ -21,3 +23,3 @@ SetTypeDescriptor, | ||
} from './type-descriptor'; | ||
import {IndexedObject} from './types'; | ||
import {Constructor, IndexedObject, MaybeTypeThunk, TypeThunk} from './types'; | ||
@@ -55,13 +57,4 @@ declare abstract class Reflect { | ||
/** | ||
* Specifies that a property is part of the object when serializing, with additional options. | ||
* Omitting the 'constructor' option requires ReflectDecorators and that the property type is always | ||
* explicitly declared. | ||
* @param options Additional options. | ||
*/ | ||
export function jsonMember(options: IJsonMemberOptions): PropertyDecorator; | ||
/** | ||
* Specifies that a property is part of the object when serializing. | ||
* This call signature requires ReflectDecorators and that the property type is always explicitly | ||
* declared. | ||
* Requires ReflectDecorators. | ||
*/ | ||
@@ -73,13 +66,27 @@ export function jsonMember<T extends Function>( | ||
/** | ||
* Specifies that a property is part of the object when serializing, with additional options. | ||
* Requires ReflectDecorators. | ||
*/ | ||
export function jsonMember(options: IJsonMemberOptions): PropertyDecorator; | ||
/** | ||
* Specifies that a property is part of the object when serializing, with a defined type and extra | ||
* options. | ||
*/ | ||
export function jsonMember( | ||
type: MaybeTypeThunk, | ||
options?: IJsonMemberOptions, | ||
): PropertyDecorator; | ||
export function jsonMember<T extends Function>( | ||
optionsOrPrototype?: IJsonMemberOptions | IndexedObject, | ||
propKey?: string | symbol, | ||
optionsOrPrototype?: IndexedObject | IJsonMemberOptions | MaybeTypeThunk, | ||
propertyKeyOrOptions?: string | symbol | IJsonMemberOptions, | ||
): PropertyDecorator | void { | ||
// @todo, why do we check if propkey is string or symbol? the type only allows symbol/string | ||
// The check is not required. | ||
if (propKey !== undefined | ||
&& (typeof propKey === 'string' || typeof propKey as any === 'symbol')) { | ||
if (propertyKeyOrOptions !== undefined | ||
&& (typeof propertyKeyOrOptions === 'string' || typeof propertyKeyOrOptions === 'symbol')) { | ||
const property = propertyKeyOrOptions as string; | ||
const prototype = optionsOrPrototype as IndexedObject; | ||
// For error messages. | ||
const decoratorName = `@jsonMember on ${nameof(prototype.constructor)}.${String(propKey)}`; | ||
const decoratorName = `@jsonMember on ${nameof(prototype.constructor)}.${String(property)}`; | ||
@@ -90,6 +97,4 @@ // jsonMember used directly, no additional information directly available besides target and | ||
if (!isReflectMetadataSupported) { | ||
logError( | ||
`${decoratorName}: ReflectDecorators is required if no 'constructor' option is` | ||
+ ` specified.`, | ||
); | ||
logError(`${decoratorName}: ReflectDecorators is required if the type is not \ | ||
explicitly provided with e.g. @jsonMember(Number)`); | ||
return; | ||
@@ -99,9 +104,9 @@ } | ||
const reflectPropCtor: Function | null | undefined = | ||
Reflect.getMetadata('design:type', prototype, propKey); | ||
Reflect.getMetadata('design:type', prototype, property); | ||
if (reflectPropCtor == null) { | ||
logError( | ||
`${decoratorName}: could not resolve detected property constructor at runtime.${ | ||
MISSING_REFLECT_CONF_MSG}`, | ||
); | ||
logError(`${decoratorName}: could not resolve detected property constructor at \ | ||
runtime. Potential solutions: | ||
- ${LAZY_TYPE_EXPLANATION} | ||
- ${MISSING_REFLECT_CONF_MSG}`); | ||
return; | ||
@@ -115,79 +120,97 @@ } | ||
injectMetadataInformation(prototype, propKey, { | ||
type: typeDescriptor, | ||
key: propKey.toString(), | ||
name: propKey.toString(), | ||
injectMetadataInformation(prototype, property, { | ||
type: () => typeDescriptor, | ||
key: propertyKeyOrOptions.toString(), | ||
name: propertyKeyOrOptions.toString(), | ||
}); | ||
} else { | ||
// jsonMember used as a decorator factory. | ||
return (target: Object, _propKey: string | symbol) => { | ||
const options: IJsonMemberOptions = optionsOrPrototype as IJsonMemberOptions ?? {}; | ||
let typeDescriptor: TypeDescriptor | undefined; | ||
const decoratorName = | ||
`@jsonMember on ${nameof(target.constructor)}.${String(_propKey)}`; | ||
return; | ||
} | ||
if (options.hasOwnProperty('constructor')) { | ||
if (!isValueDefined(options.constructor)) { | ||
logError( | ||
`${decoratorName}: cannot resolve specified property constructor at` | ||
+ ' runtime.', | ||
); | ||
return; | ||
} | ||
// jsonMember used as a decorator factory. | ||
return (target: Object, _propKey: string | symbol) => { | ||
const decoratorName = | ||
`@jsonMember on ${nameof(target.constructor)}.${String(_propKey)}`; | ||
const hasTypeThunk = typeof optionsOrPrototype === 'function'; | ||
const typeThunk = hasTypeThunk | ||
? ensureTypeThunk(optionsOrPrototype as any, decoratorName) | ||
: undefined; | ||
const options = (hasTypeThunk | ||
? propertyKeyOrOptions | ||
: optionsOrPrototype) as IJsonMemberOptions ?? {}; | ||
let typeDescriptor: TypeThunk | undefined; | ||
// Property constructor has been specified. Use ReflectDecorators (if available) to | ||
// check whether that constructor is correct. Warn if not. | ||
typeDescriptor = ensureTypeDescriptor(options.constructor); | ||
if (isReflectMetadataSupported && !isSubtypeOf( | ||
typeDescriptor.ctor, | ||
Reflect.getMetadata('design:type', target, _propKey), | ||
)) { | ||
logWarning( | ||
`${decoratorName}: detected property type does not match` | ||
+ ` 'constructor' option.`, | ||
); | ||
} | ||
} else if (isReflectMetadataSupported) { | ||
const reflectCtor = Reflect.getMetadata( | ||
'design:type', | ||
target, | ||
_propKey, | ||
) as Function | null | undefined; | ||
if (options.hasOwnProperty('constructor')) { | ||
if (hasTypeThunk) { | ||
throw new Error( | ||
'Cannot both define constructor option and type. Only one allowed.', | ||
); | ||
} | ||
if (reflectCtor == null) { | ||
logError( | ||
`${decoratorName}: cannot resolve detected property constructor at` | ||
+ ` runtime.`, | ||
); | ||
return; | ||
} | ||
typeDescriptor = ensureTypeDescriptor(reflectCtor); | ||
} else if (options.deserializer === undefined) { | ||
logError( | ||
`${decoratorName}: ReflectDecorators is required if no 'constructor' option` | ||
+ ` is specified.`, | ||
); | ||
if (!isValueDefined(options.constructor)) { | ||
logError(`${decoratorName}: cannot resolve specified property constructor at \ | ||
runtime. ${LAZY_TYPE_EXPLANATION}`); | ||
return; | ||
} | ||
if (typeDescriptor !== undefined | ||
&& isSpecialPropertyType(decoratorName, typeDescriptor)) { | ||
// Property constructor has been specified. Use ReflectDecorators (if available) to | ||
// check whether that constructor is correct. Warn if not. | ||
const newTypeDescriptor = ensureTypeDescriptor(options.constructor); | ||
typeDescriptor = () => newTypeDescriptor; | ||
if (isReflectMetadataSupported && !isSubtypeOf( | ||
newTypeDescriptor.ctor, | ||
Reflect.getMetadata('design:type', target, _propKey), | ||
)) { | ||
logWarning( | ||
`${decoratorName}: detected property type does not match` | ||
+ ` 'constructor' option.`, | ||
); | ||
} | ||
} else if (hasTypeThunk) { | ||
typeDescriptor = typeThunk; | ||
} else if (isReflectMetadataSupported) { | ||
const reflectCtor = Reflect.getMetadata( | ||
'design:type', | ||
target, | ||
_propKey, | ||
) as Function | null | undefined; | ||
if (reflectCtor == null) { | ||
logError(`${decoratorName}: cannot resolve detected property constructor at\ | ||
runtime. ${LAZY_TYPE_EXPLANATION}`); | ||
return; | ||
} | ||
injectMetadataInformation(target, _propKey, { | ||
type: typeDescriptor, | ||
emitDefaultValue: options.emitDefaultValue, | ||
isRequired: options.isRequired, | ||
options: extractOptionBase(options), | ||
key: _propKey.toString(), | ||
name: options.name ?? _propKey.toString(), | ||
deserializer: options.deserializer, | ||
serializer: options.serializer, | ||
}); | ||
}; | ||
} | ||
typeDescriptor = () => ensureTypeDescriptor(reflectCtor); | ||
} else if (options.deserializer === undefined) { | ||
logError(`${decoratorName}: Cannot determine type`); | ||
return; | ||
} | ||
const typeToTest = typeDescriptor?.(); | ||
if (typeToTest !== undefined && isSpecialPropertyType(decoratorName, typeToTest)) { | ||
return; | ||
} | ||
injectMetadataInformation(target, _propKey, { | ||
type: typeDescriptor === undefined | ||
? undefined | ||
: () => ensureTypeDescriptor(typeDescriptor!()), | ||
emitDefaultValue: options.emitDefaultValue, | ||
isRequired: options.isRequired, | ||
options: extractOptionBase(options), | ||
key: _propKey.toString(), | ||
name: options.name ?? _propKey.toString(), | ||
deserializer: options.deserializer, | ||
serializer: options.serializer, | ||
}); | ||
}; | ||
} | ||
function isSpecialPropertyType(decoratorName: string, typeDescriptor: TypeDescriptor) { | ||
if (!(typeDescriptor instanceof ArrayTypeDescriptor) && typeDescriptor.ctor === Array) { | ||
function isConstructorEqual(type: TypeDescriptor | Function, constructor: Constructor<any>) { | ||
return type instanceof TypeDescriptor ? type.ctor === constructor : type === constructor; | ||
} | ||
function isSpecialPropertyType(decoratorName: string, typeDescriptor: TypeDescriptor | Function) { | ||
if (!(typeDescriptor instanceof ArrayTypeDescriptor) | ||
&& isConstructorEqual(typeDescriptor, Array)) { | ||
logError(`${decoratorName}: property is an Array. Use the jsonArrayMember decorator to` | ||
@@ -198,3 +221,3 @@ + ` serialize this property.`); | ||
if (!(typeDescriptor instanceof SetTypeDescriptor) && typeDescriptor.ctor === Set) { | ||
if (!(typeDescriptor instanceof SetTypeDescriptor) && isConstructorEqual(typeDescriptor, Set)) { | ||
logError(`${decoratorName}: property is a Set. Use the jsonSetMember decorator to` | ||
@@ -205,3 +228,3 @@ + ` serialize this property.`); | ||
if (!(typeDescriptor instanceof MapTypeDescriptor) && typeDescriptor.ctor === Map) { | ||
if (!(typeDescriptor instanceof MapTypeDescriptor) && isConstructorEqual(typeDescriptor, Map)) { | ||
logError(`${decoratorName}: property is a Map. Use the jsonMapMember decorator to` | ||
@@ -208,0 +231,0 @@ + ` serialize this property.`); |
import {isReflectMetadataSupported, logError, MISSING_REFLECT_CONF_MSG, nameof} from './helpers'; | ||
import {injectMetadataInformation} from './metadata'; | ||
import {extractOptionBase, OptionsBase} from './options-base'; | ||
import {isTypelike, SetT} from './type-descriptor'; | ||
import {ensureTypeThunk, SetT} from './type-descriptor'; | ||
import {MaybeTypeThunk} from './types'; | ||
@@ -33,16 +34,12 @@ declare abstract class Reflect { | ||
* Use this decorator on properties of type Set<T>. | ||
* @param elementConstructor Constructor of set elements (e.g. 'Number' for Set<number> or 'Date' | ||
* @param maybeTypeThunk Constructor of set elements (e.g. 'Number' for Set<number> or 'Date' | ||
* for Set<Date>). | ||
* @param options Additional options. | ||
*/ | ||
export function jsonSetMember(elementConstructor: Function, options: IJsonSetMemberOptions = {}) { | ||
export function jsonSetMember(maybeTypeThunk: MaybeTypeThunk, options: IJsonSetMemberOptions = {}) { | ||
return (target: Object, propKey: string | symbol) => { | ||
// For error messages | ||
const decoratorName = `@jsonSetMember on ${nameof(target.constructor)}.${String(propKey)}`; | ||
const typeThunk = ensureTypeThunk(maybeTypeThunk, decoratorName); | ||
if (!isTypelike(elementConstructor)) { | ||
logError(`${decoratorName}: could not resolve constructor of set elements at runtime.`); | ||
return; | ||
} | ||
// If ReflectDecorators is available, use it to check whether 'jsonSetMember' has been used | ||
@@ -57,3 +54,3 @@ // on a set. Warn if not. | ||
injectMetadataInformation(target, propKey, { | ||
type: SetT(elementConstructor), | ||
type: () => SetT(typeThunk()), | ||
emitDefaultValue: options.emitDefaultValue, | ||
@@ -60,0 +57,0 @@ isRequired: options.isRequired, |
@@ -31,3 +31,3 @@ import {isDirectlySerializableNativeType, isTypeTypedArray, logError, nameof} from './helpers'; | ||
/** Type descriptor of the member. */ | ||
type?: TypeDescriptor | null; | ||
type?: (() => TypeDescriptor) | null; | ||
@@ -53,2 +53,5 @@ /** If set, indicates that the member must be present when deserializing. */ | ||
/** Known types to be evaluated when (de)serialization occurs */ | ||
knownTypesDeferred: Array<() => TypeDescriptor> = []; | ||
/** If present override the global function */ | ||
@@ -171,2 +174,9 @@ typeHintEmitter?: TypeHintEmitter | null; | ||
} | ||
processDeferredKnownTypes(): void { | ||
this.knownTypesDeferred.forEach(typeThunk => { | ||
typeThunk().getTypes().forEach(ctor => this.knownTypes.add(ctor)); | ||
}); | ||
this.knownTypesDeferred = []; | ||
} | ||
} | ||
@@ -214,9 +224,9 @@ | ||
// If deserializer is not present then type must be | ||
metadata.type!.getTypes().forEach(ctor => objectMetadata.knownTypes.add(ctor)); | ||
objectMetadata.knownTypesDeferred.push(metadata.type!); | ||
} | ||
// clear metadata of undefined properties to save memory | ||
(Object.keys(metadata) as [keyof JsonMemberMetadata]) | ||
(Object.keys(metadata) as Array<keyof JsonMemberMetadata>) | ||
.forEach((key) => (metadata[key] === undefined) && delete metadata[key]); | ||
objectMetadata.dataMembers.set(metadata.name, metadata); | ||
} |
@@ -411,2 +411,3 @@ import {defaultTypeResolver, Deserializer} from './deserializer'; | ||
if (rootMetadata !== undefined) { | ||
rootMetadata.processDeferredKnownTypes(); | ||
rootMetadata.knownTypes.forEach(knownTypeCtor => { | ||
@@ -413,0 +414,0 @@ knownTypes.set(this.nameResolver(knownTypeCtor), knownTypeCtor); |
@@ -1,2 +0,1 @@ | ||
import {DeserializerFn} from './deserializer'; | ||
import { | ||
@@ -12,2 +11,3 @@ identity, | ||
import { | ||
AnyT, | ||
ArrayTypeDescriptor, | ||
@@ -72,2 +72,3 @@ ConcreteTypeDescriptor, | ||
// primitives | ||
[AnyT.ctor, identity], | ||
[Date, identity], | ||
@@ -254,3 +255,3 @@ [Number, identity], | ||
sourceObject[objMemberMetadata.key], | ||
objMemberMetadata.type, | ||
objMemberMetadata.type(), | ||
`${nameof(sourceMeta.classType)}.${objMemberMetadata.key}`, | ||
@@ -257,0 +258,0 @@ objMemberOptions, |
@@ -0,1 +1,4 @@ | ||
import {LAZY_TYPE_EXPLANATION, logError} from './helpers'; | ||
import {MaybeTypeThunk, TypeThunk} from './types'; | ||
export abstract class TypeDescriptor { | ||
@@ -108,2 +111,4 @@ protected constructor(readonly ctor: Function) { | ||
export const AnyT = new ConcreteTypeDescriptor(() => undefined); | ||
// TODO support for dictionary types ie. maps that are plain objects | ||
@@ -131,1 +136,21 @@ // export class DictionaryTypeDescriptor extends GenericTypeDescriptor { | ||
} | ||
export function ensureTypeThunk( | ||
typeThunkOrSerializable: MaybeTypeThunk | null | undefined, | ||
decoratorName: string, | ||
): TypeThunk { | ||
if (typeThunkOrSerializable == null) { | ||
throw new Error(`No type given on ${decoratorName}. ${LAZY_TYPE_EXPLANATION}`); | ||
} | ||
if (typeThunkOrSerializable instanceof TypeDescriptor) { | ||
return () => typeThunkOrSerializable; | ||
} | ||
if (typeThunkOrSerializable.name !== '') { | ||
// Function is not anonymous and as such should not be a thunk | ||
return () => typeThunkOrSerializable; | ||
} | ||
return typeThunkOrSerializable as TypeThunk; | ||
} |
@@ -0,3 +1,8 @@ | ||
import {TypeDescriptor} from './type-descriptor'; | ||
export type IndexedObject = Object & {[key: string]: any}; | ||
export type TypeThunk = () => Serializable<any> | TypeDescriptor; | ||
export type MaybeTypeThunk = Serializable<any> | TypeDescriptor | TypeThunk; | ||
export interface AbstractType<T> extends Function { | ||
@@ -4,0 +9,0 @@ prototype: T; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
558572
7898
366
1