Skip to content

Assigning to a unwrapped type doesn't work on Generics but works with Interface #33942

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
pikax opened this issue Oct 11, 2019 · 2 comments
Closed
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@pikax
Copy link

pikax commented Oct 11, 2019

TypeScript Version: 3.7.0-dev.20191009

Search Terms:

Code

// Types
export interface Ref<T = any> {
  value: UnwrapRef<T>
}
export function ref<T>(raw: T): Ref<T>{
    return null as any;
}

type BailTypes =
  | Function
  | Map<any, any>
  | Set<any>
  | WeakMap<any, any>
  | WeakSet<any>
  
export type UnwrapRef<T> = {
  ref: T extends Ref<infer V> ? UnwrapRef<V> : T
  array: T extends Array<infer V> ? Array<UnwrapRef<V>> : T
  object: { [K in keyof T]: UnwrapRef<T[K]> }
  stop: T
}[T extends Ref
  ? 'ref'
  : T extends Array<any>
    ? 'array'
    : T extends BailTypes
      ? 'stop' // bail out on types that shouldn't be unwrapped
      : T extends object ? 'object' : 'stop']

// /types

type Supa = {
    xx: number,
    x1: number
}
function setup(d: Supa, fn: () => Supa) {
  const r = ref(d);
  // assigning the value
  r.value = fn(); // works as expected
}

function setup2<T>(d: T, fn: () => T) {
  const r = ref(d)
  r.value = fn(); // ERROR
}

with simpler recursion

export interface Ref<T = any> {
  value: UnwrapRef<T>
}
export type UnwrapRef<T> = T extends Ref<infer V>
  ? UnwrapRefProp<V>
  : UnwrapRefProp<T>
export type UnwrapRefProp<T> = { [K in keyof T]: UnwrapRefPropValue<T[K]> }

type BailTypes =
  | Function
  | Map<any, any>
  | Set<any>
  | WeakMap<any, any>
  | WeakSet<any>

// NOTE not fully tested
export type UnwrapRefPropValue<T> = T extends BailTypes
  ? T
  : T extends Array<infer V>
    ? UnwrapRef<V>[]
    : T extends Ref<infer VR> ? UnwrapRef<VR> : UnwrapRef<T> // we might need to return T at some point

export function ref<T>(raw: T): Ref<T>{
    return null as any;
}


type Supa = {
    xx: number,
    x1: number
}
function setup(d: Supa, fn: () => Supa) {
    const r = ref(d);
    r.value = fn(); // works as expected
}

function setup2<T>(d: T, fn: () => T) {
    const r = ref(d)
    r.value = fn(); // error `Type 'T' is not assignable to type 'UnwrapRef<T>'`
}

Expected behavior:
Both should be assignable to value, the generic should be able to infer the type.

Actual behavior:
When using inside of generics it doesn't work, on the first example show an huge error log, the second one is error "Type 'T' is not assignable to type 'UnwrapRef<T>'"

Playground Link:
example1
example2

The first example the code comes from vue-next

@RyanCavanaugh
Copy link
Member

I'm reading the definition of UnwrapRef<T> and can't convince myself that an arbitrary T should be assignable to it. Things that are true at zero-order might not be true at higher-order and TypeScript doesn't have the necessary higher-order logic to prove that all Ts going in to UnwrapRef eventually produce something assignable from another T.

@kaigregliu
Copy link

kaigregliu commented Sep 17, 2020

You may wanna try

function setup2<T>(d: T, fn: () => UnwrapRef<T>) { // ++
  const r = ref(d)
  r.value = fn(); 

and inside your fn function

function fn(){
  ...
  // return data              // --
  return reactive(data) // ++
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed
Projects
None yet
Development

No branches or pull requests

3 participants