diff --git a/.prettierrc.toml b/.prettierrc.toml index 3b96282..a0d4875 100644 --- a/.prettierrc.toml +++ b/.prettierrc.toml @@ -1,2 +1,2 @@ tabWidth = 4 -printWidth = 220 \ No newline at end of file +printWidth = 220 diff --git a/src/IsNullable.ts b/src/IsNullable.ts index 0a1d649..3a0a0e1 100644 --- a/src/IsNullable.ts +++ b/src/IsNullable.ts @@ -1 +1 @@ -export type IsNullable = null extends T ? true : never; \ No newline at end of file +export type IsNullable = null extends T ? true : never; diff --git a/src/NonForeignKeyObjects.ts b/src/NonForeignKeyObjects.ts index 373160e..23f9be4 100644 --- a/src/NonForeignKeyObjects.ts +++ b/src/NonForeignKeyObjects.ts @@ -1 +1 @@ -export type NonForeignKeyObjects = any[] | Date; \ No newline at end of file +export type NonForeignKeyObjects = any[] | Date; diff --git a/src/NonNullableRecursive.ts b/src/NonNullableRecursive.ts index 65df48a..2a8be53 100644 --- a/src/NonNullableRecursive.ts +++ b/src/NonNullableRecursive.ts @@ -2,4 +2,4 @@ import { NonForeignKeyObjects } from './NonForeignKeyObjects'; // Removes all optional, undefined and null from type export type NonNullableRecursive = { [P in keyof T]-?: T[P] extends object ? T[P] extends NonForeignKeyObjects ? Required> : NonNullableRecursive : Required>; -}; \ No newline at end of file +}; diff --git a/src/TransformPropertiesToFunction.ts b/src/TransformPropertiesToFunction.ts index ce0bce5..7556e85 100644 --- a/src/TransformPropertiesToFunction.ts +++ b/src/TransformPropertiesToFunction.ts @@ -8,6 +8,7 @@ type GetNullIfNullable = T extends { nullable: never } ? never : null; type AddToArray = ((a: A, ...t: T) => void) extends ((...u: infer U) => void) ? U : never; + // Take { a : string, b : { c : string }} and return { a : ()=> {a: string}, b : { c : ()=> { b: { c: string } }}} // PropertyPath contains the path to one leaf. {a : { b: string }} will have a PropertyPath of [{name:'b'}, {name:'a'}] // Special cases: diff --git a/src/typedKnex.ts b/src/typedKnex.ts index 18ac232..96c933a 100644 --- a/src/typedKnex.ts +++ b/src/typedKnex.ts @@ -1,5 +1,4 @@ // tslint:disable:use-named-parameter -import { unflatten } from 'flat'; import * as Knex from 'knex'; import { getColumnInformation, @@ -10,7 +9,7 @@ import { import { NonForeignKeyObjects } from './NonForeignKeyObjects'; import { NonNullableRecursive } from './NonNullableRecursive'; import { TransformPropertiesToFunction } from './TransformPropertiesToFunction'; -import { FlattenOption, setToNull } from './unflatten'; +import { FlattenOption, setToNull, unflatten } from './unflatten'; export class TypedKnex { @@ -72,7 +71,7 @@ export interface ITypedQueryBuilder { orderBy: IOrderBy; innerJoinColumn: IKeyFunctionAsParametersReturnQueryBuider; - leftOuterJoinColumn: IKeyFunctionAsParametersReturnQueryBuider; + leftOuterJoinColumn: IOuterJoin; whereColumn: IWhereCompareTwoColumns; @@ -81,7 +80,7 @@ export interface ITypedQueryBuilder { orWhereNull: IColumnParameterNoRowTransformation; orWhereNotNull: IColumnParameterNoRowTransformation; - leftOuterJoinTableOnFunction: IJoinTableMultipleOnClauses< + leftOuterJoinTableOnFunction: IOuterJoinTableMultipleOnClauses< Model, SelectableModel, Row extends Model ? {} : Row @@ -317,6 +316,40 @@ interface IJoinTableMultipleOnClauses { >; } +interface IOuterJoinTableMultipleOnClauses { + < + NewPropertyType, + NewPropertyKey extends keyof any + >( + newPropertyKey: NewPropertyKey, + newPropertyClass: new () => NewPropertyType, + on: ( + join: IJoinOnClause2< + AddPropertyWithType, + NewPropertyType + > + ) => void + ): ITypedQueryBuilder< + Model, + AddPropertyWithType, + Row + >; +} + + + +interface IOuterJoin { + ( + selectColumnFunction: ( + c: TransformPropertiesToFunction + ) => void + ): ITypedQueryBuilder; + + +} + + + interface ISelectRaw { < TReturn extends Boolean | String | Number, @@ -627,12 +660,6 @@ interface IKeyFunctionAsParametersReturnQueryBuider ) => void ): ITypedQueryBuilder; - ( - selectColumnFunction: ( - c: TransformPropertiesToFunction> - ) => void, - setToNullIfNullFunction: (r: Row) => void - ): ITypedQueryBuilder; } interface IWhere { diff --git a/test/compilation/compilationTests.ts b/test/compilation/compilationTests.ts index 3714662..b3f478b 100644 --- a/test/compilation/compilationTests.ts +++ b/test/compilation/compilationTests.ts @@ -524,7 +524,7 @@ describe('compile time typed-knex', function() { if (item !== undefined) { console.log(item.user2.numericValue); - console.log(item.otherUser.name); + console.log(item.otherUser?.name); } })(); @@ -537,6 +537,43 @@ describe('compile time typed-knex', function() { done(); }); + + it('should fail leftOuterJoinTableOnFunction result if it is used as not null', done => { + file = project.createSourceFile( + 'test/test4.ts', + ` + import * as knex from 'knex'; + import { TypedKnex } from '../src/typedKnex'; + import { User, UserSetting } from './testEntities'; + + + (async () => { + + const typedKnex = new TypedKnex(knex({ client: 'postgresql' })); + + const item = await typedKnex + .query(UserSetting) + .leftOuterJoinTableOnFunction('otherUser', User, join => { + join.onColumns(i => i.user2Id, '=', j => j.id); + }) + .select(i => [i.otherUser.name, i.user2.numericValue]) + .getFirst(); + + if (item !== undefined) { + console.log(item.user2.numericValue); + console.log(item.otherUser.name); + } + + })(); + ` + ); + + assert.equal(project.getPreEmitDiagnostics().length, 1); + + file.delete(); + done(); + }); + it('should not return type from leftOuterJoinTableOnFunction with not selected from joined table', done => { file = project.createSourceFile( 'test/test4.ts', diff --git a/test/testEntities.ts b/test/testEntities.ts index 5852053..aca1545 100644 --- a/test/testEntities.ts +++ b/test/testEntities.ts @@ -68,4 +68,6 @@ export class UserSetting { public value!: string; @Column() public initialValue!: string; + @Column({ name: 'user3Id' }) + public user3?: User; } diff --git a/test/unit/typedQueryBuilderTests.ts b/test/unit/typedQueryBuilderTests.ts index 92aadcb..04cdf56 100644 --- a/test/unit/typedQueryBuilderTests.ts +++ b/test/unit/typedQueryBuilderTests.ts @@ -1340,24 +1340,70 @@ describe('TypedKnexQueryBuilder', () => { }); // it('should stay commented out', async done => { + // const typedKnex = new TypedKnex(knex({ client: 'postgresql' })); + // const query = typedKnex + // .query(UserSetting) + // .innerJoinTableOnFunction('otherUser', User, join => { + // join.onColumns(i => i.user2Id, '=', j => j.id); + // join.onNull(i => i.name); + // }) + // .select(i => i.otherUser.birthDate); - // // const item = await typedKnex - // // .query(UserSetting) - // // .insertItem({ id: '1', key: }); + // const a = await query.getFirst(); + // console.log('a: ', a.otherUser.birthDate); + // console.log('a: ', a.otherUser?.birthDate); + + // done(); + // }); + + // it('should stay commented out', async done => { + + // const typedKnex = new TypedKnex(knex({ client: 'postgresql' })); + // const query = typedKnex + // .query(UserSetting) + // .leftOuterJoinTableOnFunction('otherUser', User, join => { + // join.onColumns(i => i.user2Id, '=', j => j.id); + // join.onNull(i => i.name); + // }) + // .select(i => i.otherUser.birthDate); + + // const a = await query.getFirst(); + // console.log('a: ', a.otherUser.birthDate); + // console.log('a: ', a.otherUser?.birthDate); + + // done(); + // }) - // const item = await typedKnex - // .query(User) - // .select(i => i.category.name) - // .getFirst(); - // console.log('item: ', item.category.name); + // it('should stay commented out', async done => { + + // const typedKnex = new TypedKnex(knex({ client: 'postgresql' })); + // const query = typedKnex + // .query(UserSetting) + // .innerJoinColumn(i => i.user3) + // .select(i => i.user3.birthDate); + + // const a = await query.getFirst(); + // console.log('a: ', a.user3.birthDate); + // console.log('a: ', a.user3?.birthDate); + + // done(); + // }); - // // if (item !== undefined) { - // // console.log(item.user2.numericValue); - // // console.log(item.otherUser.name); - // // } + // it('should stay commented out', async done => { + + // const typedKnex = new TypedKnex(knex({ client: 'postgresql' })); + // const query = typedKnex + // .query(UserSetting) + // .leftOuterJoinColumn(i => i.user3) + // .select(i => i.user3.birthDate); + + // const a = await query.getFirst(); + // console.log('a: ', a.user3.birthDate); + // console.log('a: ', a.user3?.birthDate); // done(); // }); + });