Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 24 additions & 20 deletions src/mongo_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ export type MatchKeysAndValues<TSchema> = Readonly<
PropertyType<TSchema, Property extends `${infer Key}.$${string}` ? Key : never>
>;
} & {
[Property in `${NestedPathsOfType<TSchema, Record<string, any>[]>}.$${
[Property in `${NestedPathsOfType<TSchema, Record<string | number, any>[]>}.$${
| `[${string}]`
| ''}.${string}`]?: any; // Could be further narrowed
}
Expand Down Expand Up @@ -478,14 +478,18 @@ export type PropertyType<Type, Property extends string> = string extends Propert
? unknown
: Property extends keyof Type
? Type[Property]
: Property extends `${number}`
: Property extends `${infer NumberProperty extends number}`
? Type extends ReadonlyArray<infer ArrayType>
? ArrayType
: NumberProperty extends keyof Type
? Type[NumberProperty]
: unknown
: Property extends `${infer Key}.${infer Rest}`
? Key extends `${number}`
? Key extends `${infer NumberKey extends number}`
? Type extends ReadonlyArray<infer ArrayType>
? PropertyType<ArrayType, Rest>
: NumberKey extends keyof Type
? PropertyType<Type[NumberKey], Rest>
: unknown
: Key extends keyof Type
? Type[Key] extends Map<string, infer MapType>
Expand Down Expand Up @@ -516,23 +520,23 @@ export type NestedPaths<Type> = Type extends
? [string]
: Type extends object
? {
[Key in Extract<keyof Type, string>]: Type[Key] extends Type // type of value extends the parent
? [Key]
: // for a recursive union type, the child will never extend the parent type.
// but the parent will still extend the child
Type extends Type[Key]
? [Key]
Comment on lines -521 to -524
Copy link
Author

@GrantGryczan GrantGryczan Oct 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This prevented template string keys from working because, for example, if Type is { a: Record<`example-${number}`, any> } and Key is 'a', then this condition succeeds, so it outputs ['a'] instead of ['a', `example-${number}`] like it's supposed to, despite there being no recursion there.

This fact is exclusive to records with number or template string keys because those allow for infinite possible key values, which apparently informs TypeScript that it should allow any keys on the object.

That's why I had to change some of the recursive typing support here. The new method I used is more robust anyway since it checks for truly identical expressions rather than merely parent extending child or child extending parent (neither of which necessarily imply recursion).

: Type[Key] extends ReadonlyArray<infer ArrayType> // handling recursive types with arrays
? Type extends ArrayType // is the type of the parent the same as the type of the array?
? [Key] // yes, it's a recursive array type
: // for unions, the child type extends the parent
ArrayType extends Type
? [Key] // we have a recursive array union
: // child is an array, but it's not a recursive array
[Key, ...NestedPaths<Type[Key]>]
: // child is not structured the same as the parent
[Key, ...NestedPaths<Type[Key]>] | [Key];
}[Extract<keyof Type, string>]
[Key in Extract<keyof Type, string | number>]: Type[Key] extends infer Value
? Value extends any // distribute union types
? // the type of the parent is the same as the type of the value (see https://github.com/Microsoft/TypeScript/issues/27024#issuecomment-421529650)
(<T>() => T extends Type ? 1 : 2) extends <T>() => T extends Value ? 1 : 2
? [Key] // the object value type is recursive
: Type[Key] extends ReadonlyArray<infer ArrayType> // handling recursive types with arrays
? Type extends ArrayType // is the type of the parent the same as the type of the array?
? [Key] // yes, it's a recursive array type
: // for unions, the child type extends the parent
ArrayType extends Type
? [Key] // we have a recursive array union
: // child is an array, but it's not a recursive array
[Key, ...NestedPaths<Type[Key]>]
: [Key] | [Key, ...NestedPaths<Value>] // not recursive
: never
: never;
}[Extract<keyof Type, string | number>]
: [];

/**
Expand Down