Last active
November 4, 2021 19:29
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.