Skip to content

Instantly share code, notes, and snippets.

@SMJSGaming
Last active November 4, 2021 19:29
Show Gist options
  • Save SMJSGaming/936b9edb25fc5a45b69692d4fb72d160 to your computer and use it in GitHub Desktop.
Save SMJSGaming/936b9edb25fc5a45b69692d4fb72d160 to your computer and use it in GitHub Desktop.
A class which throws all the questionable types from the Object class out of the window and instead uses more accurate types
export class BetterObject<T> {
public static assign<T, U>(target: T, ...sources: U[]): T & U {
// Tricking the compiler because merging 2 types by reference isn't truly possible
sources.forEach((source) => Object.entries(source).forEach(([key, value]) => target[key as keyof T] = value));
return target as T & U;
}
public static merge<T>(target: T, ...sources: T[]): T {
sources.forEach((source) =>
BetterObject.entries(source).forEach(([key, value]) =>
target[key] = value));
return target;
}
public static override<T>(target: T, ...sources: T[]): T {
const keysRemaining: (keyof T)[] = BetterObject.keys(target);
sources.forEach((source) => BetterObject.entries(source).forEach(([key, value]) => {
const keyIndex = keysRemaining.indexOf(key);
target[key] = value;
if (keyIndex != -1) {
keysRemaining.splice(keyIndex, 1);
}
}));
keysRemaining.forEach((key) => delete target[key]);
return target;
}
public static create<T>(object: T): BetterObject<T[keyof T]> {
return new BetterObject(BetterObject.assign({}, object));
}
public static keys<T>(object: T): (keyof T)[] {
return Object.keys(object) as (keyof T)[];
}
public static values<T>(object: T): T[keyof T][] {
return Object.values(object);
}
public static entries<T>(object: T): [keyof T, T[keyof T]][] {
return BetterObject.keys(object).map((key) => [key as keyof T, object[key as keyof T]]);
}
public static fromEntries<T>(entries: [keyof T, T[keyof T]][]): T {
return entries.reduce((target, [key, value]) => {
target[key] = value;
return target;
}, {} as T);
}
public static getProperty<T, U extends T[keyof T]>(object: T, key: keyof T): U {
return object[key] as U;
}
public static setProperty<T, U extends T[keyof T]>(object: T, key: keyof T, value: U): T {
object[key] = value;
return object;
}
public static is<T>(object1: T, object2: T): boolean {
const entries1 = BetterObject.entries(object1);
return entries1.length == BetterObject.entries(object2).length &&
entries1.map(([key, value]) => object2[key] === value).every(Boolean);
}
[key: string]: T;
constructor(object: BetterObject<T>) {
BetterObject.merge(this, object);
}
}
@SMJSGaming
Copy link
Author

Note

Everything uses generics, this might get confusing but keep in mind that this class was designed to automatically fill out the generics. You can of course overwrite them if you're using optional properties but I'd recommend letting TypeScript figure out the types for you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment