Как избежать дублирование кода в конструкторах классов в typescript?
Один из многих классов:
export interface IMyClassConstructorParameters {
name: string;
id?: null | number;
deleted_at?: null | string;
created_at?: null | string;
updated_at?: null | string;
}
export default class MyClass {
public name: string;
private readonly _deleted_at: null | string;
private readonly _created_at: null | string;
private readonly _updated_at: null | string;
private readonly _id: null | number;
public get id(): null | number {
return this._id;
}
constructor(parameters: IMyClassConstructorParameters) {
this._id = parameters.id
? parameters.id
: null;
this._updated_at = parameters.updated_at
? parameters.updated_at
: null;
this._created_at = parameters.created_at
? parameters.created_at
: null;
this._deleted_at = parameters.deleted_at
? parameters.deleted_at
: null;
this.name = parameters.name;
}
}
Проблема: В конструкторе я определяю свойства _id, _updated_at, _created_at и _deleted_at. Такое определение я делаю в многих классах, т.е у меня появляется дублирование кода. Есть ли паттерны или общая практика решения подобных проблем?
Конечно, можно сделать класс Core и вынести туда часть методов и свойств. Но мне кажется это не лучшее решение, так если мне надо будет использовать разные методы или свойства в различных классах, то мне надо будет создавать классы типа CoreOnlyCreatedAt, CoreOnlyDeletedAt и так далее.
Ответы (1 шт):
не влезает в комменты.
пример ниже исключительно для примера, как вектор для размышлений.
что есть:
- Partial
- Required
- исключение методов из параметров
чего нет:
- не извлекаются методы для подмешивания
что ещё посмотреть:
- какой-нибудь lodash + @types/lodash
- typedi
const [_id, created_at, deleted_at, updated_at] = Array.from({ length: 10 }, () => null)
let t = 0
// -,-
console.clear()
console.log(Date.now())
class BaseA {
_id: null | number = null;
}
class BaseB {
deleted_at: null | string = null;
created_at: null | string = null;
updated_at: null | string = null;
}
class BaseC {
logMethod() {
logKeys(this)
}
}
run(() => { // Partial
interface MyClassParams extends BaseA, BaseB, BaseC { }
class MyClass extends Helper<MyClassParams, true>(BaseA, BaseB, BaseC) {
get id(): null | number {
return this._id;
}
}
const q$1 = new MyClass({ _id, created_at, deleted_at, updated_at })
const q$2 = new MyClass({ _id, deleted_at, updated_at })
})
run(() => { // Required
interface MyClassParams extends BaseA, BaseB, BaseC { }
class MyClass extends Helper<MyClassParams>(BaseA, BaseB, BaseC) {
get id(): null | number {
return this._id;
}
}
const q$1 = new MyClass({ _id, created_at, deleted_at, updated_at })
const q$2 = new MyClass({ _id, deleted_at, updated_at })
})
// --------------
type Ctor = { new(...args: any[]): any }
// #region `OmitType` https://stackoverflow.com/a/55479659/4794368
// 1 Transform the type to flag all the undesired keys as 'never'
type FlagExcludedType<Base, Type> = { [Key in keyof Base]: Base[Key] extends Type ? never : Key };
// 2 Get the keys that are not flagged as 'never'
type AllowedNames<Base, Type> = FlagExcludedType<Base, Type>[keyof Base];
// 3 Use this with a simple Pick to get the right interface, excluding the undesired type
type OmitType<Base, Type> = Pick<Base, AllowedNames<Base, Type>>;
// 4 Exclude the Function type to only get properties
type ConstructorType<T> = OmitType<T, Function>;
// #endregion
function Helper<T, P extends boolean = false>(...superCtorList: Ctor[]):
new (params: P extends true ? OmitType<Partial<T>, Function> : OmitType<T, Function>) => T {
//@ts-ignore
return class {
constructor(params: any) {
// TODO: добавить извлечение методов
const superList = Array.from(superCtorList, Ctor => new Ctor())
// const _params = Object.assign({}, ...superList, params)
Object.assign(this, ...superList)
Object.keys(this).forEach(key => {
if (key in params)
//@ts-ignore
this[key] = params[key]
})
}
}
}
function logKeys(q: {}) {
console.log(`keys: ${Object.keys(q)}`)
}
function run(f: Function): void {
console.log(``)
console.log(`${++t} -- >`)
try { f() } catch (e) { }
console.log(`${t} -- <`)
}
// --------------
// test: {
// abstract class Base<T = any> {
// constructor(params: Partial<T>) {
// Object.keys(this).forEach(key => {
// if (key in params)
// //@ts-ignore
// this[key] = params[key]
// })
// }
// }
// class BaseA extends Base<BaseA> {
// _id: null | number = null;
// }
// class BaseB extends Base<BaseB>{
// deleted_at: null | string = null;
// created_at: null | string = null;
// updated_at: null | string = null;
// }
// }