microsoft / microsoft/TypeScript
Type Inference Fails with Generic Types
オープン
まだ誰も着手していません。
Needs More Info
- 主要言語
- Go
- スター
- 111k
- フォーク
- 14.3k
- 平均マージ
- 1日 19時間
- マージ済み PR(30日)
- 117
説明
🔎 Search Terms
error recursive generic types
🕗 Version & Regression Information
- This changed between versions v5.7.0-beta and v4.1.5
⏯ Playground Link
💻 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
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
調査の方向性
リンクされている TypeScript Playground から始め、BaseItem、Snapshot、Get、FlatQuery を含む再帰的なジェネリックの例を縮小し、ジェネリックではない代入と比較してください。ジェネリックな { collectionName: 'asdf' } の代入が、報告されている期待される動作を弱めることなく受け入れられれば完了です。
索引モデルが issue の本文から書いたものです。
評価
- 技術スタック
- typescript
- 領域
- compilers
- issue の種類
- バグ
- 難易度
- 5/5
- 見積もり時間
- 1週間以上
- 活発さ
- 停滞
- 明瞭さ
- 明確に書かれている
- 初心者へのやさしさ
- 30/100