Is it possible to create nested generics without explicit definition in typescript

Viewed 89

I am having trouble writing a type that applies one type to all the bottom level props of another type.

The purpose of this is to use create a type which can convert all bottom level props to Ref<T> for Vue 3.

// Start with some type T
type T = ...

// And a 'function' to apply, U
type U<T> = ...

// This needs to apply U to all bottom level properties of T
// ('bottom level' are all props who's type does not extend object)
type DeepApply<T,U> = ???

Example:

{
  a: string,
  b: number,
  c: {
    c1: number,
    c2: string
  }
}

Would become

{
  a: Ref<string>,
  b: Ref<number>,
  c: {
    c1: Ref<number>,
    c2: Ref<string>
  }
}

Here's my best shot but TS throws an error both when trying to give a generic its own generic arguments (U<T[P]>) and when trying to pass a type with generics as a generic without explicitly defining its generics (DeepApply<T, Ref>).

type DeepApply<T, U> = {
  [P in keyof T]: T[P] extends object ? DeepUnion<T[P], U> : U<T[P]>;
};

type MyRefType = DeepApply<MyType, Ref>
1 Answers

When a type is referenced, all it's generic parameters must be satisfied. Put another way, you can't pass a parameterized type without parameters to have it's parameters filled in later. There's just no syntax for that.

However, you can do it if you hardcode the Ref type. For example:

type DeepApplyRef<T> = {
  [P in keyof T]: T[P] extends object ? DeepApplyRef<T[P]> : Ref<T[P]>;
};

I don't think a solution exists that is quite as dynamic as you want here, if your recursive type knows the type that it's wrapping things up with, then this does work just fine.

Playground

Related