I have implemented 2 versions of the Vuejs function "unref".
This has one parameter which can be of type Ref<T>:{ value: T }
or anything else.
If the parameter gets a Ref unref or unref2 returns the Ref.value otherwise it simply returns the parameter.
The only difference between the functions is how I use the Typescript syntax.
Why does Typescript has an issue with the unref2 function?
type Ref<Value> = { value: Value };
// Returns the type of Ref<TypeOfRef> if its a Ref else return T
type GetTypeOfMaybeRef<T> = T extends Ref<infer V> ? V : T;
// works
function unref<T>(maybeRef: Ref<GetTypeOfMaybeRef<T>>|GetTypeOfMaybeRef<T>): GetTypeOfMaybeRef<T> {
if (maybeRef != null && typeof maybeRef === 'object' && "value" in maybeRef) {
return maybeRef.value;
} else {
return maybeRef;
}
}
// does not work, why?
// should be like unref.
// I know, T is just T.
// I did it to give ts a hint that T can be a Ref.
function unref2<T>(maybeRef: T extends Ref<infer Value> ? Ref<Value> : T): GetTypeOfMaybeRef<T> {
if (maybeRef != null && typeof maybeRef === 'object' && "value" in maybeRef) {
return maybeRef.value;
} else {
// why ts error?
// should it not be the same as unref?
return maybeRef;
}
}
const ref: Ref<number> = { value: 3 };
const numRef: Ref<number> = { value: 3 };
const num = unref(numRef)
const strRef: Ref<string> = { value: "blalba" };
const text = unref(strRef)
Deriving a generic type from an argument that has a conditional type is going to hit and miss. Sometimes Typescript can figure out simple cases, but when it gets complicated things will start to fail.
It's best to avoid that situation altogether.
That said, You are trying very hard here, and this can be much simpler:
type Ref<Value> = { value: Value };
function unref<T>(maybeRef: Ref<T> | T): T {
if (maybeRef != null && typeof maybeRef === 'object' && "value" in maybeRef) {
return maybeRef.value;
} else {
return maybeRef;
}
}
There is no infer
or even any conditional types required. unref
takes either a Ref<T> | T
and T
is returned. Easy peasy.
And if you need GetTypeOfMaybeRef<T>
still then you can do that without infer
:
type GetTypeOfMaybeRef<T> = T extends Ref<unknown> ? T['value'] : T;
type A = GetTypeOfMaybeRef<{ value: number }> // number
type B = GetTypeOfMaybeRef<string> // string