Skip to content

Instantly share code, notes, and snippets.

@gabriel-dehan
Last active February 27, 2023 14:34
Show Gist options
  • Save gabriel-dehan/20705d818171119e664105199f33c973 to your computer and use it in GitHub Desktop.
Save gabriel-dehan/20705d818171119e664105199f33c973 to your computer and use it in GitHub Desktop.
Gives an `EntityPartial<T>` type that can be used with a typeorm entity as a contructor input type
import { CreateDateColumn, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm';
import { EntityPartial } from 'tools/types/entity';
export class BaseEntity<T> {
constructor(input?: EntityPartial<T>) {
this.setAttributes(input);
}
@PrimaryGeneratedColumn()
readonly id!: number;
@CreateDateColumn({ name: 'created_at' })
createdAt!: Date;
@UpdateDateColumn({ name: 'updated_at' })
updatedAt!: Date;
setAttributes(input?: EntityPartial<T>) {
if (input) {
// Set all properties on `this` from input
Object.entries(input).forEach(([key, value]) => {
(this as any)[key] = value;
});
}
}
}
/*
* Source[1]: https://stackoverflow.com/questions/52443276/how-to-exclude-getter-only-properties-from-type-in-typescript/52473108#52473108
* Source[2]: https://stackoverflow.com/questions/64490108/partialsomeclass-but-without-methods
* Source[3]: https://github.com/ts-essentials/ts-essentials/blob/master/lib/types.ts
*/
type IfEquals<X, Y, A = X, B = never> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? A : B;
type PickKeysByValue<T, V> = { [K in keyof T]: T[K] extends V ? K : never }[keyof T];
export type OmitProperties<T, P> = Omit<T, PickKeysByValue<T, P>>;
type WritableKeys<T> = {
[P in keyof T]-?: IfEquals<{ [Q in P]: T[P] }, { -readonly [Q in P]: T[P] }, P>;
}[keyof T];
type WithoutMethods<T> = OmitProperties<T, Function>;
type WithoutReadonly<T> = Pick<T, WritableKeys<T>>;
// Entity attributes without any method (getters, setters, etc.)
export type EntityAttributes<T> = WithoutMethods<T>;
// Entity attributes without any method nor readonly properties
export type EntityWritableAttributes<T> = WithoutReadonly<WithoutMethods<T>>;
// Entity partial without any method
export type EntityPartialWithReadonly<T> = Partial<EntityAttributes<T>>;
// Entity partial without any method nor readonly properties
export type EntityPartial<T> = Partial<EntityWritableAttributes<T>>;
@gabriel-dehan
Copy link
Author

gabriel-dehan commented Feb 27, 2023

Usage :

@Entity()
export class User extends BaseEntity<User> {

  @Column({ type: 'text', unique: true })
  email!: string;

  public async someFunction() {}
}

Then

const user = new User({ 
  email: 1, // type error
  id: 1, // type error (because readonly)
 someFunction: () => {} // type error (because method)
});

// Or correctly:
const user = new User({ 
  email: 'bla' // good
});

await userRepo.save(user);

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