say i have an interface
interface Data { one: number; another: string; }
i have a serialization method
serialize(data: Data): string {
return 'one: ' + data.one + ', another: ' + data.another;
}
now i am doing refactoring and want to add a new property to the Data interface, so that
interface Data { one: number, another: string, yetAnother: boolean }
now by requirement the new property yetAnother must be serialized too, yet the compiler won't point out to that because i didn't ask for it, so there is a chance of a bug to slip in
now my question: is there a way to ask the TypeScript compiler if i addressed all properties of an interface?
crossposted to StackOverflow: http://stackoverflow.com/questions/43140675/is-there-a-way-to-guarantee-that-all-properties-of-an-interface-are-addressed
UPDATE
thank to @RyanCavanaugh 's idea, here is what i ended up with
below is a full working example
try it live at playground:
type Around<T, R> = { [P in keyof T]: (result: R, value: T[P], name: P) => R; }
function around<T, R>(data: T, result: R, around: Around<T, R>): R {
for (const name in data) {
result = around[name](result, data[name], name);
}
return result;
}
function identity<T>(value: T): T { return value; }
function addOver<T>(toString: (value: T) => string) {
return function add(values: string[], value: T, name: string): string[] {
values.push(name + ': ' + toString(value));
return values;
};
}
interface Data { one: number, another: string, yetAnother: boolean }
function serialize(data: Data): string {
return around<Data, string[]>(data, [], {
one: addOver(String),
another: addOver(identity),
yetAnother: addOver(String)
}).join(', ');
}
const serialized = serialize({ one: 1, another: 'hey', yetAnother: true });
alert(serialized) // one: 1, another: hey, yetAnother: true
Would be nice see that also with switch enums:
const enum Foo {
A, B, C
}
switch (x) {
case A: return 1;
case B: return 1;
// C missed
}
@aleksey-bykov here's a fairly odd solution you may or may not like
interface Data { one: number; another: string; theNewOne: Date }
function assertNever(x: never) {
throw new Error(`Oops! Forgot about ${x}`);
}
function serialize(data: Data) {
var keys = Object.keys(data) as Array<keyof Data>;
const parts: string[] = [];
for (const k of keys) {
switch (k) {
case 'one':
parts.push('uno: ', data.one.toString());
break;
case 'another':
parts.push('another: ', data.another);
break;
case 'mispeld': // Error
break;
default:
assertNever(k); // Error, can't convert theNewOne to never
}
}
return parts.join('');
}
i like it, thanks
inspired by @RyanCavanaugh, i ended up with some extremely elegant little function:
export type Around<T extends object, R> = { [P in keyof T]: (result: R, value: T[P], name: P) => R; }
export function around<T extends object, R>(data: T, result: R, around: Around<T, R>): R {
for (const name in data) {
result = around[name](result, data[name], name);
}
return result;
}
problem solved
@aleksey-bykov How to use this function?
check my latest update to the initial post in this topic, there is an example there