Package version
1.1.1
Runtime
Bun
Reproduction
SelectProxy<T> in src/types/query.types.ts uses -? to strip the optional modifier from every key:
export type SelectProxy<T> = {
[K in keyof T]-?: NonNullable<T[K]> extends Array<infer U>
? SelectProxy<NonNullable<U>> & { $all: SelectProxyLeaf }
: NonNullable<T[K]> extends object
? SelectProxy<NonNullable<T[K]>> & { $all: SelectProxyLeaf }
: NonNullable<T[K]>;
} & { $all: SelectProxyLeaf };
This lets you write game.cover.image_id inside .select() without a compile error, even though cover?: Cover is optional on the real Game model. This is fine for building the query. The problem is that the callback's return type is computed as ReturnType<TSelector> (InferShape<TProxy, TSelector>), and since the proxy has already erased optionality, the inferred output shape never reintroduces it. So .execute() returns objects typed as if every selected relation is guaranteed present, when IGDB can and does omit fields like cover at runtime.
Reproduction:
const games = await client.games
.query()
.select((game) => ({
id: game.id,
name: game.name,
cover: { imageId: game.cover.image_id },
}))
.execute();
games[0].cover.imageId; // typed as string, no null check required
Hovering the inferred type of games shows:
const games: {
id: number;
name: string;
cover: {
imageId: string;
};
}[]
cover should not be seen as guaranteed here.
Expected behavior
Expected:
cover (and any other optional relation) in the result type should be matching what IGDB can actually return.
Actual behavior
Actual:
cover is typed as { imageId: string }, always present, letting you access .imageId without a guard and hit a runtime crash on games with no cover.
Validation
No response
Checklist
Package version
1.1.1
Runtime
Bun
Reproduction
SelectProxy<T>insrc/types/query.types.tsuses-?to strip the optional modifier from every key:This lets you write
game.cover.image_idinside.select()without a compile error, even thoughcover?: Coveris optional on the realGamemodel. This is fine for building the query. The problem is that the callback's return type is computed asReturnType<TSelector>(InferShape<TProxy, TSelector>), and since the proxy has already erased optionality, the inferred output shape never reintroduces it. So.execute()returns objects typed as if every selected relation is guaranteed present, when IGDB can and does omit fields like cover at runtime.Reproduction:
Hovering the inferred type of
gamesshows:covershould not be seen as guaranteed here.Expected behavior
Expected:
cover(and any other optional relation) in the result type should be matching what IGDB can actually return.Actual behavior
Actual:
coveris typed as { imageId: string }, always present, letting you access.imageIdwithout a guard and hit a runtime crash on games with no cover.Validation
No response
Checklist