Skip to content

Instantly share code, notes, and snippets.

@BirgitPohl
Created October 7, 2020 12:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save BirgitPohl/ea584183edc083d1efef8ce90f0485e2 to your computer and use it in GitHub Desktop.
Save BirgitPohl/ea584183edc083d1efef8ce90f0485e2 to your computer and use it in GitHub Desktop.
A collection of generic types for objects
import { testAgainstSchemaDeeply } from "./utils";
interface IMyObject {
name: string
{[key: string]: any}
}
class ObjectSchema {
name: any
}
/**
* Parses object taking the name property and using it as the key
* @param myObject: IMyObject
* @fires getDeeplyTypedObject
* @returns ObjectSchema
*/
const parseObject = (myObject: IMyObject): ObjectSchema => {
const prettyObject: any = {};
const {name, ...properties} = myObject;
prettyObject[name] = properties;
let prettyObjectTyped;
try {
prettyFontsTyped = getDeeplyTypedObject(prettyObject, new ObjectSchema());
} catch {
throw new Error(`Parsing Object didn't work out.`);
}
return prettyObjectTyped;
};
/**
* Checks if property exists in the target object.
* Use for each property
* @param object object to check against
* @param property property to be checked
* @returns boolean, true if it exists
*/
export const hasOwnProperty = <T extends {}, P extends PropertyKey>(object: T, property: P):
object is NonNullable<T> => {
return object.hasOwnProperty(property);
};
export const isInstanceOf = <S, T extends FunctionConstructor>(source: S, target: T): T => {
if (source instanceof target) {
return source as unknown as T
} else {
throw new Error(`Object doesn't match the schema.`);
}
};
type ObjectDescriptor<D> = {
data?: D;
};
export function makeTypedObject<D>(desc: ObjectDescriptor<D>): D {
let data: object = desc.data || {};
return { ...data } as D;
}
/**
* Checks if property exists in the target object.
* @Description Since we check type on Javascript level, we can safely cast type as T
* @param source any object
* @param schema any class
* @returns boolean | Error
*/
export const testAgainstSchema = <S extends {[key: string]: string},T extends {[key: string]: string}>
(source: S, schema: T): boolean | Error => {
const keysOfSource = Object.keys(source);
const keyOfSchema = Object.keys(schema);
for (const key of keysOfSource) {
if (!schema.hasOwnProperty(key)) {
throw new Error(`Property ${key} doesn't exist.`);
}
}
for (const key of keyOfSchema) {
if (!source.hasOwnProperty(key)) {
throw new Error(`Property ${key} is missing.`);
}
}
return true;
};
/**
* Checks deeply a source object against a schema.
* @Description We type-check in Javascript-level
* @param source any object
* @param schema any class
* @returns boolean | Error
*/
export const testAgainstSchemaDeeply = <X extends {[key: string]: any}, Y extends {[key: string]: any}>
(source: X, schema: Y): boolean | Error => {
const keysOfSource: string[] = Object.keys(source);
const keysOfSchema: string[] = Object.keys(schema);
// test if a key is to much
for (const key of keysOfSource) {
if (!hasOwnProperty(schema, key)) {
throw new Error(`Property ${key} in object to be tested doesn't exist in the schema.`);
}
// 👀 It's a tree!
if (typeof source[key] === "object" && typeof schema[key] == "object") {
getDeeplyTypedObject(source[key], schema[key]);
}
}
for (const key of keysOfSchema) {
if (!hasOwnProperty(source, key)) {
throw new Error(`Property ${key} is missing in object to be tested.`);
}
// 👀 It's a tree!
if (typeof schema[key] === "object" && typeof source[key] == "object") {
getDeeplyTypedObject(schema[key], source[key]);
}
}
return true;
}
/**
* Checks if property exists in the target object.
* @Description Since we check type on Javascript level, we can safely cast type as T
* @param sourceObject any object
* @param TargetType any class
* @returns sourceObject as T
*/
export const getTypedObject = <S extends {},T extends {}>(sourceObject: S, targetType: T): T => {
const keysOfSource = Object.keys(sourceObject);
const keyOfTarget = Object.keys(targetType);
for (const key of keysOfSource) {
if (!targetType.hasOwnProperty(key)) {
throw new Error(`Property ${key} doesn't exist.`);
}
}
for (const key of keyOfTarget) {
if (!sourceObject.hasOwnProperty(key)) {
throw new Error(`Property ${key} is missing.`);
}
}
return sourceObject as unknown as T;
};
/**
* Checks deeply if property exists in the target object.
* @Description Since we check type on Javascript level, we can safely cast type as T
* @param sourceObject any object
* @param TargetType any class
* @returns sourceObject as T | Error
*/
export const getDeeplyTypedObject = <S extends {[key: string]: any}, T extends {[key: string]: any;}>
(sourceObject: S, targetType: T): T => {
const keysOfSource: string[] = Object.keys(sourceObject);
const keyOfTarget: string[] = Object.keys(targetType);
for (const key of keysOfSource) {
if (!hasOwnProperty(targetType, key)) {
throw new Error(`Property ${key} doesn't exist`);
}
// 👀 It's a tree!
if (typeof sourceObject[key] === "object" && typeof targetType[key] == "object") {
getDeeplyTypedObject(sourceObject[key], targetType[key]);
}
}
for (const key of keyOfTarget) {
if (!hasOwnProperty(sourceObject, key)) {
throw new Error(`Property ${key} doesn't exist`);
}
// 👀 It's a tree!
if (typeof targetType[key] === "object" && typeof sourceObject[key] == "object") {
getDeeplyTypedObject(targetType[key], sourceObject[key]);
}
}
return sourceObject as unknown as T;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment