I have the following code:
type Id = string | number;
type ObjectId<T extends Id> = {
id: T;
};
type ValidObjectId = ObjectId<string> | ObjectId<number>
type ReverseId<T extends ValidObjectId> = T['id'] extends number ? string : number;
function encodeObjectId<T extends ObjectId<number>>(
obj: T
): ReverseId<T> {
const id = obj.id;
// Why does the following does not accept a string type?
// As it's the reverse of type number, because of the `ReverseId` type, it should be working.
return id.toString();
}
In it, I have the type ReverseId, which converts the type of the id property. For example, if the given object id property has a number type, it will return string; and if the given object's id property has a string type, the number type will be returned.
And that actually works:
type Id = string | number;
type ObjectId<T extends Id> = {
id: T;
};
type ValidObjectId = ObjectId<string> | ObjectId<number>
type ReverseId<T extends ValidObjectId> = T['id'] extends number ? string : number;
// The `t` constant has a `number` type, which is the "reverse" of the `string`
// type, used in the given `ObjectId`.
const t: ReverseId<ObjectId<string>> = 1;
The problem is: when the ObjectId comes from a generic, as we saw in the encodeObjectId function in the first example, the type does not work.
Is there a way to solve this problem using conditional types?
ReverseIdwhich happens to work but I wouldn't use it in any prod environment:type ReverseId<T extends ValidObjectId> = ({ [k: string]: number } & { [k: number]: string })[T["id"]];