If our dependencies look like this:
declare class Repository<T> {
find<T>(options: FindOptions): Promise<T>;
}
interface FindOptions {
limit: number;
offset?: number;
}we can apply defaults in a number of ways:
class Foo {
constructor(private readonly repo: Repository<any>) {}
find(options: Partial<FindOptions>) {
return this.repo.find({
...options,
limit: options.limit || 10,
});
}
}class Foo {
constructor(private readonly repo: Repository<any>) {}
find({ limit = 10, ...rest }: Partial<FindOptions>) {
return this.repo.find({
limit,
...rest,
});
}
}declare const defaults: FindOptions;
class Foo {
constructor(private readonly repo: Repository<any>) {}
find(options: Partial<FindOptions>) {
return this.repo.find(
update(defaults, options)
);
}
}Where:
const update = <T extends object>(source: T, patch: Partial<T>): T =>
Object.assign({}, source, patch);type Defaults = Pick<FindOptions, 'limit'>;
declare const defaults: Defaults;
class Foo {
constructor(private readonly repo: Repository<any>) {}
find(options: Omit<FindOptions, keyof Defaults>) {
return this.repo.find(
merge(defaults, options),
);
}
}Where:
const merge = <T extends object, U extends object>(source: T, patch: U): Overwrite<T, U> =>
Object.assign({}, source, patch);
type Overwrite<T, U> = Omit<T, Extract<keyof T, keyof U>> & U;
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;