Skip to content

types: allow using ref with unknown generic types #1129

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed

Conversation

pikax
Copy link
Member

@pikax pikax commented May 6, 2020

Allow handling the case where the type of the generic is unknown:

function useState<State = string>(initial: State) {
  const state = ref(initial)
  const set = (v: State) => void (state.value = v) // before: error `Type 'State' is not assignable to type 'UnwrapRef<State>'`
  return {}
}

There's a few caveats:

  • When using Ref<TGeneric> and TGeneric doesn't extend, the ref.value will be any, this behaviour might be fine, but strict checking might cause some problems(tsd) example
  • When using Ref<TGeneric> and TGeneric extends something, the ref.value will be extends type, example

@ktsn
Copy link
Member

ktsn commented May 6, 2020

Hmm, I realized this is tougher than I thought. Making ref.value type to be the base type of generics wouldn't solve the issue completely as it mismatches if we want to assign it to another generics typed value.

function useState<State extends string>(initial: State) {
  const state = ref(initial) // state.value -> string
  const set = (v: State) => void (state.value = v) // pass
  const another: State = state.value // error
  return {}
}

Ideally state.value should also be State to allow both usage of input and output of the value. But I'm still not sure how we can infer the original generics type to ref value. 🤔

Rethinking about another option, We might want to just leave this type since assigning generics type into potentially unwrapped type is dangerous. If the users are very sure the generics type and unwrapped type are identical, they can just cast it.

function useState<State extends string>(initial: State) {
  const state = ref(initial) 
  const set = (v: State) => {
    state.value = v as UnwrapRef<State>
  }
  return {}
}

What do you think?

@pikax
Copy link
Member Author

pikax commented May 6, 2020

I would like to avoid exposing UnwrapRef<> to the user.

If you cast the value to State it works

function useState<State extends string>(initial: State) {
  const state = ref(initial) // state.value -> string
  const set = (v: State) => void (state.value = v) // pass
  const another = state.value as State // pass
  return {}
}

Ideally state.value should also be State to allow both usage of input and output of the value. But I'm still not sure how we can infer the original generics type to ref value. 🤔

For me ideally UnwrapRef<T> would be basically the same as T, but that is not possible. We might be able to do it, using different types for set and get (which is not supported currently on typescript, nor is planed AFAIK)

If the users are very sure the generics type and unwrapped type are identical

Thought about that and the cleanest way to do it would be other overload

export function ref<T, noRef extends  true>(value: T): Ref<T>

// 

function useState<State extends string>(initial: State) {
  const state = ref<State, true>(initial) // state.value -> State extends string
  const set = (v: State) => void (state.value = v) // pass
  const another: State = state.value // pass
  return {}
}

This way we can ignore the unwrap ref

What are your thoughts?

@ktsn
Copy link
Member

ktsn commented May 6, 2020

I think having noRef type parameter is as same as cast. I would say we don't have to introduce dedicated api for that. We can just do below if we don't want to encourage users to use UnwrapRef:

function useState<State extends string>(initial: State) {
  const state = ref(initial) as Ref<State> // state.value -> State extends string
  const set = (v: State) => void (state.value = v) // pass
  const another: State = state.value // pass
  return {}
}

@pikax
Copy link
Member Author

pikax commented May 6, 2020

We need to add that to the docs I can see some people having problems

@pikax pikax closed this May 6, 2020
@pikax pikax deleted the types/handling_unknown_generics_ref branch May 6, 2020 15:31
@pikax pikax restored the types/handling_unknown_generics_ref branch May 25, 2020 08:27
@pikax pikax deleted the types/handling_unknown_generics_ref branch May 25, 2020 08:27
@pikax pikax restored the types/handling_unknown_generics_ref branch May 25, 2020 08:27
@andreataglia
Copy link

so how did it end? I'm having this problem...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants