Reputation: 455
I want to declare a mapping of one object to another.
The first object can have any string keys and values of a generic type.
I want to map this object with the same keys, and the types of the values can be anything (but nice would be if I could extract them from the generic). Specifically these are properties of a class and the first is passed in the constructor.
class C {
ob1: {
[key: string]: Wrapper<any>
};
ob2; // should have the same keys as ob1
constructor(o?: { [key: string]: Wrapper<any> }) {
this.ob1 = o;
// map ob2 from o
}
}
Upvotes: 0
Views: 160
Reputation: 327624
I think the way to do this is to make C
itself a generic class with a type parameter T
corresponding to either the type of ob1
or ob2
, and then use mapped types to express "same keys but different/related values". For example:
type MappedWrapper<T> = { [K in keyof T]: Wrapper<T[K]> }
class C<T> {
ob1: MappedWrapper<T>
ob2: T;
constructor(o: MappedWrapper<T>) { // made param required here
this.ob1 = o;
this.ob2 = mapUnwrap(o); // some function that unwraps properties
}
}
Here we say that T
is the type of ob2
, while ob1
has the type MappedWrapper<T>
, a mapped type where each property of ob1
is mapped to a Wrapper
-version.
Depending on the implementation and declaration of Wrapper
and related types, such as:
type Wrapper<T> = { x: T };
function wrap<T>(x: T): Wrapper<T> {
return { x };
}
function unwrap<T>(x: Wrapper<T>): T {
return x.x;
}
function mapUnwrap<T>(x: MappedWrapper<T>): T {
return Object.fromEntries(Object.entries(x).map(([k, v]) => [k, unwrap(v)])) as any as T;
}
you can verify that this works as expected:
const c = new C({ a: wrap(123), b: wrap("hello"), c: wrap(true) });
/* const c: C<{ a: number; b: string; c: boolean;}> */
c.ob1.a.x.toFixed(); // no error
c.ob2.b.toUpperCase(); // no error
Upvotes: 1