microsoft / microsoft/TypeScript

Type Inference Fails with Generic Types

Abierto
#60,447 5 comentarios 1 reacción 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

Needs More Info
Lenguaje dominante
Go
Estrellas
111k
Forks
14.3k
Merge medio
1 d 19 h
PR fusionados (30 d)
117

Descripción

🔎 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
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:

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:

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

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Línea de trabajo

Comienza con el TypeScript Playground enlazado y reduce el ejemplo genérico recursivo que involucra BaseItem, Snapshot, Get y FlatQuery, comparándolo con la asignación no genérica. Se considera terminado cuando se acepta la asignación genérica { collectionName: 'asdf' } sin debilitar el comportamiento esperado indicado.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
typescript
Área
compilers
Tipo de issue
Error
Dificultad
5/5
Tiempo estimado
Más de una semana
Estado de actividad
Estancado
Claridad
Bien especificado
Aptitud para principiantes
30/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.