Repository: TypeScript
Issue: Type Inference Fails with Generic Types
Labels: Needs More Info

### 🔎 Search Terms

error recursive generic types

### 🕗 Version & Regression Information

- This changed between versions v5.7.0-beta and v4.1.5


### ⏯ Playground Link

https://www.typescriptlang.org/play/?ts=5.6.3#code/C4TwDgpgBAQghgZwgSWBAtgHgCpQLxRwB2IAfPlAN5QCWAJgFxS4C+AUGzUWgE4BmcAMbQAykThgEACwD2wHFAgAPNEToJYiFGizJyBeElQYANFGQViZKmyi1GUBMB5cA5idtRgNdBCZEAV3QAIwgeDztBGQAbaIhBbxkiADk4XyYnFyJ3TxodBCZsAG0AXQ92NiiiJ0cIOISZHiYAMWi4YABFALCQTDEJaTlMQ20MUn0qKCjY+MSUtL8oAHJEOj4lqHYAei2oAD09th2oZJlFHh5GqEBQcg4+AKIEmiSvCCdMY3RscGhlVXVNEYdB86N9IKQzMhQT9LCRSAAKACUNjsoEg5h0AGkICANAQACJyU7AdrPIjY3F9cSSWTyT5giCQ6Hg0ieY4HNm7ABE9C5UAAPlAud5fHzBVzpvU5qlRQKhXkMAgxVAAAYK9AIAB0ABJKJk3CwVZ5PGjoABhGJSskyiAMiitdqqTAAcQg8n6NKG9J+TIZEOWktm1oWS3GnP2hzsxy5+uyXI4kSSNVcECIYRoghEdVmjRabU63R4vQ9gzpOgZvp+4wo1EDTySNqYKwQaw27Cjuw5HagAFELldADLkhRhS1rlqDDYWGWcbgA3JsNjQNEQ5IQEAgaK5xME4l4zqblg6Cz0qQNaR9yz7zMyIOMlpq2BVjgBVbzRPIgLww4BnQRSeIANa0HwhBfuiS6EEQhAXHAn5XDIwQAFazA+B7IAgADySGzDgEy4H8qYAghyEJFAAD8UDwvhKiERoACCMG9FY5AUQI0RIFATDON0yJMGxSAcMcABK8QBDwG4AG7QAeP5QCmaY8O00B0HIAC0K4knMUAATiCBsAehLAMSpJJBSCC4TWnhFJitBQTpIAyCBuAAGSUbGrhyoEIRhIiJQMJ4xSYiUig0Wo9GMZgXB8GEUDPqy3bICBeRLBoxDQYpIBmFwgjRAEdDQMEchSF4-62flSiQXQJXQNqUAAO40NEdCCHAPB0J4dgUSquqYoacrdZQvU6iq-U9SwOqUIZxlzGZmBxYa4aJbQwApZBUDEbMZg8KJ4nQFwsl5B1nHmJh2EJDg1klOQBFhV4PDdEdnWqmNI2CgNQ26lNcgmeSukXUFpALXYwPHe9fXHCMUxaEwABSAQ1NtwBiVBwClfZj5FPZjnMFArnwu5nlBKEPC+RwB4iGA77uiF-waO5Zj4nUPgKjwNO0Y4M7ZPongiGzt0DVFMUABIQHAdAsJ9TPoCzEuUILrPYHAjVA8DFFFCLYtmJq2sU1TOBK9EDNSyzpAlEdTBFCIZv6TCrrAAyMAgAACq1wDmdgZguzwbt8wC21i0k0Sfu5pTc3YXs+zdAJFPLUAa3QWva7HiuNWbIMUfHvsaFjTmPeRUB2w7zuu+76ui3QZS9ioinnSnhsc1krih-FIPHZnUcaEs2pLHnT3UbTUAMRlkVENFrNxb3qsF26RcR+Zz5mD21dCPIddmCHV0t63wP+BAUk8JPu-7+bzBk7bbo4J77TFR3DduGH0-2z8jtz5fUC63kmAu6jZj3qGrI23REeJ02A8JZ0giAUo+digAAZgqFDPkA-MXQTygMsnYIoTtbJQC+ppMks1QElDInmR0qYXQXw9lAJ2YZ2BAA

### 💻 Code

```ts
type BaseItem<T = any> = { id: T }

interface Snapshot<T extends BaseItem<I> = BaseItem, I = any> {
  id: string,
  time: number,
  collectionName: string,
  items: T[],
}

const selector: FlatQuery<Snapshot<BaseItem>> = { collectionName: 'asdf' }
// ^^
// No error ✅

function test<ItemType extends BaseItem<IdType>, IdType = any>() {
  type ItemKeys = DotNotationKeys<Snapshot<ItemType, IdType>>
  // ^^
  // "id" | "time" | "collectionName" | "items" | `items.${string}`
  
  type CollectionNameType = Flatten<Get<Snapshot<ItemType, IdType>, 'collectionName'>>
  // ^^
  // "string"

  const genericSelector: FlatQuery<Snapshot<ItemType, IdType>> = { collectionName: 'asdf' }
  // ^^
  // Error ❌: Type '{ collectionName: string; }' is not assignable to type 'FlatQuery<Snapshot<ItemType, IdType>>'.
}

// Utility type to check if a type is an array or object.
type IsObject<T> = T extends object ? (T extends Array<any> ? false : true) : false

// Recursive type to generate dot-notation keys
type DotNotationKeys<T> = {
  [K in keyof T & (string | number)]:
  T[K] extends Array<infer U>
  // If it's an array, include both the index and the $ wildcard
    ? `${K}` | `${K}.$` | `${K}.${DotNotationKeys<U>}`
  // If it's an object, recurse into it
    : IsObject<T[K]> extends true
      ? `${K}` | `${K}.${DotNotationKeys<T[K]>}`
      : `${K}` // Base case: Just return the key
}[keyof T & (string | number)]

type Split<S extends string, Delimiter extends string> =
  S extends `${infer Head}${Delimiter}${infer Tail}`
    ? [Head, ...Split<Tail, Delimiter>]
    : [S]

type GetTypeByParts<T, Parts extends readonly string[]> =
  Parts extends [infer Head, ...infer Tail]
    ? Head extends keyof T
      ? GetTypeByParts<T[Head], Extract<Tail, string[]>>
      : Head extends '$'
        ? T extends Array<infer U>
          ? GetTypeByParts<U, Extract<Tail, string[]>>
          : never
        : never
    : T

type Get<T, Path extends string> =
  GetTypeByParts<T, Split<Path, '.'>>

type Flatten<T> = T extends any[] ? T[0] : T

type FlatQuery<T> = {
  [P in DotNotationKeys<T>]?: Flatten<Get<T, P>>
}
```

### 🙁 Actual behavior

Typescript isn't able to assign `{ collectionName: 'asdf' }` when using generic types:
```ts
function test<ItemType extends BaseItem<IdType>, IdType = any>() {
  const selector: FlatQuery<Snapshot<ItemType, IdType>> = { collectionName: 'asdf' }
  // ^^
  // Type '{ collectionName: string; }' is not assignable to type 'FlatQuery<Snapshot<ItemType, IdType>>'.
}
```

Without generic types it work seamlessly: 
```ts
const selector: FlatQuery<Snapshot<BaseItem>> = { collectionName: 'asdf' }
```

### 🙂 Expected behavior

`{ collectionName: 'asdf' }` should be successfully assignable to `FlatQuery<Snapshot<ItemType, IdType>>` using generic types.

### Additional information about the issue

Related issue: https://github.com/maxnowack/signaldb/pull/1030