fix: FindOptionsSelect to use correct type when property is an object (#11355)

* fix: FindOptionsSelectProperty or boolean when Property is object

* fix: add test cases for find options select

* fix: use simple-json for tests
This commit is contained in:
Muhammad Ghayas Baig 2025-03-25 15:40:42 +05:00 committed by GitHub
parent 00d5639efb
commit 834e85692f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 280 additions and 1 deletions

View File

@ -24,7 +24,7 @@ export type FindOptionsSelectProperty<Property> = Property extends Promise<
: Property extends ObjectId
? boolean
: Property extends object
? FindOptionsSelect<Property>
? FindOptionsSelect<Property> | boolean
: boolean
/**

View File

@ -0,0 +1,30 @@
import { Entity } from "../../../../../src/decorator/entity/Entity"
import { PrimaryGeneratedColumn } from "../../../../../src/decorator/columns/PrimaryGeneratedColumn"
import { Column } from "../../../../../src/decorator/columns/Column"
import { VersionColumn } from "../../../../../src/decorator/columns/VersionColumn"
import { Post } from "./Post"
import { OneToMany } from "../../../../../src"
import { CategoryMeta } from "../types"
@Entity()
export class Category {
@PrimaryGeneratedColumn()
id: number
@Column()
name: string
@Column()
description: string
@VersionColumn()
version: string
@Column("simple-json", { nullable: true })
meta?: CategoryMeta
@OneToMany(() => Post, (post) => post.category, {
cascade: ["insert", "update", "soft-remove", "recover"],
})
posts: Post[]
}

View File

@ -0,0 +1,35 @@
import {
Column,
Entity,
JoinTable,
ManyToMany,
ManyToOne,
PrimaryColumn,
} from "../../../../../src"
import { Tag } from "./Tag"
import { Category } from "./Category"
import { PostMeta } from "../types"
@Entity()
export class Post {
@PrimaryColumn()
id: number
@Column()
title: string
@Column()
description: string
@ManyToOne((type) => Category)
category: Category
@Column("simple-json", { nullable: true })
meta?: PostMeta
@ManyToMany(() => Tag, (tag) => tag.posts, {
cascade: ["insert", "update", "soft-remove", "recover"],
})
@JoinTable()
tags: Tag[]
}

View File

@ -0,0 +1,19 @@
import { Post } from "./Post"
import {
Column,
Entity,
ManyToMany,
PrimaryGeneratedColumn,
} from "../../../../../src"
@Entity()
export class Tag {
@PrimaryGeneratedColumn()
id: number
@Column()
name: string
@ManyToMany(() => Post, (post) => post.tags)
posts: Post[]
}

View File

@ -0,0 +1,187 @@
import "reflect-metadata"
import "../../../utils/test-setup"
import { DataSource } from "../../../../src"
import {
closeTestingConnections,
createTestingConnections,
reloadTestingDatabases,
} from "../../../utils/test-utils"
import { Post } from "./entity/Post"
import { Category } from "./entity/Category"
import { Tag } from "./entity/Tag"
describe("find options > select", () => {
let dataSources: DataSource[]
before(
async () =>
(dataSources = await createTestingConnections({
__dirname,
})),
)
beforeEach(() => reloadTestingDatabases(dataSources))
after(() => closeTestingConnections(dataSources))
async function prepareData(dataSource: DataSource) {
const tag = new Tag()
tag.id = 1
tag.name = "SuperTag"
const post = new Post()
post.id = 1
post.title = "Hello"
post.description = "This is a post!"
post.meta = {
likes: 10,
dislikes: 1,
}
post.tags = [tag]
const category = new Category()
category.id = 1
category.name = "Action"
category.description = "Action movies"
category.posts = [post]
await dataSource.manager.save(category)
}
it("should select all properties of relation post", () =>
Promise.all(
dataSources.map(async (dataSource) => {
await prepareData(dataSource)
const categories = await dataSource.manager.find(Category, {
select: {
id: true,
posts: true,
},
relations: ["posts"],
})
categories.should.be.eql([
{
id: 1,
posts: [
{
id: 1,
title: "Hello",
description: "This is a post!",
meta: {
likes: 10,
dislikes: 1,
},
},
],
},
])
}),
))
it("should select all properties of relation post and its relation tag", () =>
Promise.all(
dataSources.map(async (dataSource) => {
await prepareData(dataSource)
const categories = await dataSource.manager.find(Category, {
select: {
id: true,
posts: {
id: true,
title: true,
description: true,
meta: true,
tags: true,
},
},
relations: ["posts", "posts.tags"],
})
categories.should.be.eql([
{
id: 1,
posts: [
{
id: 1,
title: "Hello",
description: "This is a post!",
meta: {
likes: 10,
dislikes: 1,
},
tags: [
{
id: 1,
name: "SuperTag",
},
],
},
],
},
])
}),
))
it("should select all from category and meta only from post", () =>
Promise.all(
dataSources.map(async (dataSource) => {
await prepareData(dataSource)
const categories = await dataSource.manager.find(Category, {
select: {
id: true,
posts: {
meta: true,
},
},
relations: ["posts"],
})
categories.should.be.eql([
{
id: 1,
posts: [
{
meta: {
likes: 10,
dislikes: 1,
},
},
],
},
])
}),
))
it("should select all from category and meta only from post with all meta properties", () =>
Promise.all(
dataSources.map(async (dataSource) => {
await prepareData(dataSource)
const categories = await dataSource.manager.find(Category, {
select: {
id: true,
posts: {
meta: {
likes: true,
},
},
},
relations: ["posts"],
})
categories.should.be.eql([
{
id: 1,
posts: [
{
meta: {
likes: 10,
dislikes: 1,
},
},
],
},
])
}),
))
})

View File

@ -0,0 +1,8 @@
export interface PostMeta {
likes: number
dislikes: number
}
export interface CategoryMeta {
views: number
}