Reputation: 19202
I don't really know how to ask this question so I think the best way is with an example of what I'm trying to do. Let's say I have the following object:
const obj = {
one: 'some string',
two: new Set<string>(),
};
and now I want to write a function that takes in that object and converts the Set
s to Array
s of the same type.
Here's the untyped javascript implementation:
const obj = {
one: 'some string',
two: new Set().add('one').add('two'),
};
function convertToArrays(objWithSets) {
return Object.entries(objWithSets).reduce((objectWithArrays, [key, value]) => {
if (value instanceof Set) {
objectWithArrays[key] = Array.from(value);
} else {
objectWithArrays[key] = value;
}
return objectWithArrays;
}, {});
}
console.log('converted', convertToArrays(obj));
How can I properly type the above function? To clarify: it can take in any object (not just the one, two example) and if it sees a Set<T>
, it transforms that to an Array<T>
.
I know it requires capturing the type of the inner Set
(which is string
in this case) and conditionally mapping that type to Array<string>
.
Thanks!
Upvotes: 0
Views: 129
Reputation: 6456
You can use a mix of mapped types and conditional types to do this:
type InnerSetToArray<T> = {
[P in keyof T]:
T[P] extends Set<infer U>
? Array<U>
: T[P]
};
type InnerSet = { one: number, two: Set<string>, three: Set<Function>, four: Date };
// InnerArray === { one: number, two: string[], three: Function[], four: Date }
type InnerArray = InnerSetToArray<InnerSet>;
Edit: original, overly specific answer below:
Here we go, essentially as you describe:
function convertToArrays<T extends { one: string, two: Set<any> }>(objWithSets: T):
T extends { one: string, two: Set<infer U> }
? { one: string, two: Array<U> }
: never {
/* ... */
}
// typeof x === { one: string, two: number[] }
const x = convertToArrays({ one: "hello", two: new Set([1, 2, 3]) })
We allow it to initially be Set<any>
just to serve as a typeguard, and then infer the actual type with the conditional. The conditional narrows to never
on a match failure, which should be fine as the initial check theoretically guarantees the conditional will always evaluate to true.
Upvotes: 1