API reference › @evolu/common › Type › Object
const Object: createRootType(
"Object",
(
value: unknown,
options: ValidationOptions = firstValidationOptions,
): Result<Readonly<Record<string, unknown>>, PlainObjectError> => {
if (value === null || typeof value !== "object") {
return err({
type: "Object",
reason: { kind: "NotObject", value },
});
}
if (!isPlainObject(value)) {
return err({
type: "Object",
reason: { kind: "UnexpectedPrototype", value },
});
}
let errors: RuntimeObjectPropertyErrors | undefined;
for (const key of Reflect.ownKeys(value)) {
let propertyError:
ObjectPropertyAccessError | ObjectExcessPropertyError | undefined;
if (typeof key !== "string") {
propertyError = { type: "ObjectExcessProperty" };
} else {
const descriptor = globalThis.Object.getOwnPropertyDescriptor(
value,
key,
);
assert(
descriptor !== undefined,
"Object property descriptor is missing.",
);
if (!("value" in descriptor)) {
propertyError = {
type: "ObjectPropertyAccess",
reason: "Accessor",
};
} else if (!descriptor.enumerable) {
propertyError = {
type: "ObjectPropertyAccess",
reason: "NonEnumerable",
};
}
}
if (propertyError === undefined) continue;
errors ??= createMutableRecord<string, TypeError>();
errors[key] = propertyError;
if (options.errors === "first") break;
}
return errors === undefined
? ok(value)
: err({
type: "Object",
reason: { kind: "Properties", errors },
} as PlainObjectError);
},
(error: ObjectError) => {
if (error.reason.kind !== "Properties")
return formatPlainObjectRootError(error.reason);
const key = Reflect.ownKeys(error.reason.errors).at(0);
assertNonNullable(key);
const propertyError = error.reason.errors[key];
assertNonNullable(propertyError);
if (propertyError.type === "ObjectPropertyAccess") {
switch ((propertyError as ObjectPropertyAccessError).reason) {
case "Accessor":
return "An Object property must be a data property. Materialize accessor values into plain data before using this Type or use a different Type.";
case "NonEnumerable":
return "An Object property must be enumerable. Make it enumerable or use a different Type.";
}
}
if (propertyError.type === "ObjectMissingProperty")
return `The required property ${safelyStringifyUnknownValue(key)} is missing.`;
if (typeof key === "symbol")
return "An Object property key must be a string. Remove the symbol property or use a different Type.";
if (propertyError.type === "ObjectExcessProperty")
return `The property ${safelyStringifyUnknownValue(key)} is not allowed. Remove it or use a different Type.`;
return `The property ${safelyStringifyUnknownValue(key)} is invalid.`;
},
createObjectRuntimeTypeIssues(((error: ObjectError) => {
if (error.reason.kind !== "Properties")
return formatPlainObjectRootError(error.reason);
const key = Reflect.ownKeys(error.reason.errors).at(0);
assertNonNullable(key);
const propertyError = error.reason.errors[key];
assertNonNullable(propertyError);
if (propertyError.type === "ObjectPropertyAccess") {
switch ((propertyError as ObjectPropertyAccessError).reason) {
case "Accessor":
return "An Object property must be a data property. Materialize accessor values into plain data before using this Type or use a different Type.";
case "NonEnumerable":
return "An Object property must be enumerable. Make it enumerable or use a different Type.";
}
}
if (propertyError.type === "ObjectMissingProperty")
return `The required property ${safelyStringifyUnknownValue(key)} is missing.`;
if (typeof key === "symbol")
return "An Object property key must be a string. Remove the symbol property or use a different Type.";
if (propertyError.type === "ObjectExcessProperty")
return `The property ${safelyStringifyUnknownValue(key)} is not allowed. Remove it or use a different Type.`;
return `The property ${safelyStringifyUnknownValue(key)} is invalid.`;
}) as TypeErrorFormatter<TypeError>),
);
Defined in: packages/common/src/Type.ts:9430
A Type for readonly plain objects with unknown property values.
Object is the runtime counterpart of a Readonly<Record<string, unknown>>
data boundary. Its prototype rule uses the realm-neutral structural heuristic
described by isPlainObject. A matching custom root prototype can be
classified as plain; other custom prototypes and class instances are
rejected. Every own property must have a string key and be an enumerable data
property. Accessors, non-enumerable properties, and symbol properties are
rejected without reading their values.
Use object when property names are fixed, record when keys and values have their own Types, and instanceOf when an instance belongs to the domain.