-
Notifications
You must be signed in to change notification settings - Fork 39
Expand file tree
/
Copy pathgeneric.ts
More file actions
54 lines (45 loc) · 1.33 KB
/
generic.ts
File metadata and controls
54 lines (45 loc) · 1.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
type Coords = Record<string, number>;
type Coords2d = { x: number; y: number };
const validatePoint = <T extends Coords>(coords: T): Error[] => {
const errors: Error[] = [];
if (typeof coords === 'object' && coords !== null) {
for (const [key, value] of Object.entries(coords)) {
if (typeof value === 'number' && !Number.isFinite(value)) {
errors.push(new TypeError(`Invalid ${key}: ${value}`));
}
}
}
return errors;
};
function addVectors<T extends Coords>(a: T, b: T): T {
const result = {} as T;
for (const key of Object.keys(a) as Array<keyof T>) {
result[key] = (a[key] + b[key]) as T[typeof key];
}
return result;
}
class Point<T extends Coords> {
#coords: T;
constructor(coords: T) {
const errors = validatePoint(coords);
if (errors.length > 0) {
const cause = new AggregateError(errors, 'Validation');
throw new RangeError('Bad coordinates', { cause });
}
this.#coords = coords;
}
move(delta: T): void {
this.#coords = addVectors(this.#coords, delta);
}
clone(): Point<T> {
return new Point({ ...this.#coords });
}
toString(): string {
return `(${Object.values(this.#coords).join(', ')})`;
}
}
// Usage
const p1 = new Point<Coords2d>({ x: 10, y: 20 });
console.log(p1.toString());
p1.move({ x: -5, y: 10 });
console.log(p1.toString());