diff --git a/bun.lockb b/bun.lockb index b9e96e6..a50d6e2 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 7396a16..8e4489a 100644 --- a/package.json +++ b/package.json @@ -42,8 +42,9 @@ "license": "Apache-2.0", "devDependencies": { "@biomejs/biome": "1.9.4", - "@ronin/compiler": "0.17.2", + "@ronin/compiler": "0.17.5", "@types/bun": "1.2.1", + "expect-type": "1.1.0", "tsup": "8.3.6", "typescript": "5.7.3" } diff --git a/src/helpers/expressions.ts b/src/helpers/expressions.ts index 3e2a542..4d91a72 100644 --- a/src/helpers/expressions.ts +++ b/src/helpers/expressions.ts @@ -7,11 +7,9 @@ import { QUERY_SYMBOLS } from '@ronin/compiler'; * * @returns An object containing the expression wrapped in a query symbol. */ -export const expression = ( +export const expression = >( expression: string, -): Record => { - return { [QUERY_SYMBOLS.EXPRESSION]: expression }; -}; +): T => ({ [QUERY_SYMBOLS.EXPRESSION]: expression }) as T; /** Valid operators for string concatenation */ type StringOperator = '||'; @@ -38,16 +36,19 @@ export const op = < operator: NumberOperator | ComparisonOperator | StringOperator, right: T, ): T => { + // Unwrap the left and right operands if they are expression objects let leftValue = left; if (typeof left === 'object' && QUERY_SYMBOLS.EXPRESSION in left) { leftValue = left[QUERY_SYMBOLS.EXPRESSION] as T; } + // Unwrap the right operand if it is an expression object let rightValue = right; if (typeof right === 'object' && QUERY_SYMBOLS.EXPRESSION in right) { rightValue = right[QUERY_SYMBOLS.EXPRESSION] as T; } + // Wrap the left and right operands in single quotes if they are strings let wrappedLeft = leftValue; if ( typeof leftValue === 'string' && @@ -59,6 +60,7 @@ export const op = < wrappedLeft = `'${leftValue}'` as T; } + // Wrap the right operand in single quotes if it is a string let wrappedRight = rightValue; if ( typeof rightValue === 'string' && @@ -70,5 +72,5 @@ export const op = < wrappedRight = `'${rightValue}'` as T; } - return expression(`(${wrappedLeft} ${operator} ${wrappedRight})`) as unknown as T; + return expression(`(${wrappedLeft} ${operator} ${wrappedRight})`); }; diff --git a/src/helpers/functions.ts b/src/helpers/functions.ts index f6ad8bc..b25ab52 100644 --- a/src/helpers/functions.ts +++ b/src/helpers/functions.ts @@ -9,18 +9,14 @@ import { QUERY_SYMBOLS, getQuerySymbol } from '@ronin/compiler'; * * @returns The wrapped SQL expression */ -export const sql = (expressions: string): any => { - return expression(expressions); -}; +export const sql = (expressions: string): any => expression(expressions); /** * Generates a pseudo-random integer between -9223372036854775808 and +9223372036854775807. * * @returns SQL expression that evaluates to a random number. */ -export const random = (): number => { - return expression('random()') as unknown as number; -}; +export const random = (): number => expression('random()'); /** * Calculates the absolute value of a number. @@ -34,7 +30,8 @@ export const abs = (value: number | Record): number => typeof value === 'object' && QUERY_SYMBOLS.EXPRESSION in value ? value[QUERY_SYMBOLS.EXPRESSION] : value; - return expression(`abs(${valueExpression})`) as unknown as number; + + return expression(`abs(${valueExpression})`); }; /** @@ -45,9 +42,8 @@ export const abs = (value: number | Record): number => * * @returns SQL expression that evaluates to the formatted timestamp. */ -export const strftime = (format: string, timestamp: string | 'now'): Date => { - return expression(`strftime('${format}', '${timestamp}')`) as unknown as Date; -}; +export const strftime = (format: string, timestamp: string | 'now'): Date => + expression(`strftime('${format}', '${timestamp}')`); /** * Applies a JSON patch operation to a JSON document. @@ -57,9 +53,8 @@ export const strftime = (format: string, timestamp: string | 'now'): Date => { * * @returns SQL expression that evaluates to the patched JSON document. */ -export const json_patch = (patch: string, input: string): string => { - return expression(`json_patch('${patch}', '${input}')`) as unknown as string; -}; +export const json_patch = (patch: string, input: string): string => + expression(`json_patch('${patch}', '${input}')`); /** * Sets a value in a JSON document at the specified path. @@ -71,9 +66,8 @@ export const json_patch = (patch: string, input: string): string => { * * @returns SQL expression that evaluates to the modified JSON document. */ -export const json_set = (json: string, path: string, value: string): string => { - return expression(`json_set('${json}', '${path}', '${value}')`) as unknown as string; -}; +export const json_set = (json: string, path: string, value: string): string => + expression(`json_set('${json}', '${path}', '${value}')`); /** * Replaces a value in a JSON document at the specified path. @@ -85,11 +79,8 @@ export const json_set = (json: string, path: string, value: string): string => { * * @returns SQL expression that evaluates to the modified JSON document. */ -export const json_replace = (json: string, path: string, value: string): string => { - return expression( - `json_replace('${json}', '${path}', '${value}')`, - ) as unknown as string; -}; +export const json_replace = (json: string, path: string, value: string): string => + expression(`json_replace('${json}', '${path}', '${value}')`); /** * Inserts a value into a JSON document at the specified path. @@ -101,9 +92,8 @@ export const json_replace = (json: string, path: string, value: string): string * * @returns SQL expression that evaluates to the modified JSON document. */ -export const json_insert = (json: string, path: string, value: string): string => { - return expression(`json_insert('${json}', '${path}', '${value}')`) as unknown as string; -}; +export const json_insert = (json: string, path: string, value: string): string => + expression(`json_insert('${json}', '${path}', '${value}')`); /** * Concatenates a list of strings together. @@ -120,7 +110,7 @@ export const concat = ( return symbol?.type === 'expression' ? symbol.value : `'${value}'`; }); - return expression(`concat(${formattedValues.join(', ')})`) as unknown as string; + return expression(`concat(${formattedValues.join(', ')})`); }; /** @@ -132,8 +122,5 @@ export const concat = ( * * @returns SQL expression that evaluates to the modified string. */ -export const replace = (input: string, search: string, replacement: string): string => { - return expression( - `replace('${input}', '${search}', '${replacement}')`, - ) as unknown as string; -}; +export const replace = (input: string, search: string, replacement: string): string => + expression(`replace('${input}', '${search}', '${replacement}')`); diff --git a/src/schema/model.ts b/src/schema/model.ts index 492f041..80fcc90 100644 --- a/src/schema/model.ts +++ b/src/schema/model.ts @@ -156,7 +156,10 @@ type Expand = T extends infer O ? { [K in keyof O]: O[K] } : never; * * @returns The generated model definition. */ -export const model = >( +export const model = < + // biome-ignore lint/complexity/noBannedTypes: `Fields` requires an empty object as a fallback. + Fields extends RecordWithoutForbiddenKeys = {}, +>( model: Model | (() => Model), ): Expand> => { return getSyntaxProxy({ modelType: true, chaining: false })(model) as unknown as Expand< diff --git a/tests/helpers/expressions.test.ts b/tests/helpers/expressions.test.ts index 6ee35ff..437c532 100644 --- a/tests/helpers/expressions.test.ts +++ b/tests/helpers/expressions.test.ts @@ -12,6 +12,7 @@ import { strftime, } from '@/src/helpers/functions'; import { date, model, number, string } from '@/src/schema'; +import { expectTypeOf } from 'expect-type'; describe('expressions', () => { test('sql expression', () => { @@ -26,6 +27,11 @@ describe('expressions', () => { expect(Test.fields.test.defaultValue).toEqual({ __RONIN_EXPRESSION: "UPPER('test')", }); + expectTypeOf(Test).toEqualTypeOf<{ + id: string; + ronin: string; + test: string; + }>(); }); describe('operator expressions', () => { @@ -40,6 +46,11 @@ describe('expressions', () => { expect(Test.fields.stringConcat.defaultValue).toEqual({ __RONIN_EXPRESSION: "('Hello' || 'World')", }); + expectTypeOf(Test).toEqualTypeOf<{ + id: string; + ronin: string; + stringConcat: string; + }>(); }); describe('arithmetic operators', () => { @@ -54,6 +65,11 @@ describe('expressions', () => { expect(Test.fields.add.defaultValue).toEqual({ __RONIN_EXPRESSION: '(1 + 2)', }); + expectTypeOf(Test).toEqualTypeOf<{ + id: string; + ronin: string; + add: number; + }>(); }); test('subtraction', () => { @@ -67,6 +83,11 @@ describe('expressions', () => { expect(Test.fields.subtract.defaultValue).toEqual({ __RONIN_EXPRESSION: '(5 - 3)', }); + expectTypeOf(Test).toEqualTypeOf<{ + id: string; + ronin: string; + subtract: number; + }>(); }); test('multiplication', () => { @@ -80,6 +101,11 @@ describe('expressions', () => { expect(Test.fields.multiply.defaultValue).toEqual({ __RONIN_EXPRESSION: '(4 * 2)', }); + expectTypeOf(Test).toEqualTypeOf<{ + id: string; + ronin: string; + multiply: number; + }>(); }); test('division', () => { @@ -93,6 +119,11 @@ describe('expressions', () => { expect(Test.fields.divide.defaultValue).toEqual({ __RONIN_EXPRESSION: '(10 / 2)', }); + expectTypeOf(Test).toEqualTypeOf<{ + id: string; + ronin: string; + divide: number; + }>(); }); test('modulo', () => { @@ -106,6 +137,11 @@ describe('expressions', () => { expect(Test.fields.modulo.defaultValue).toEqual({ __RONIN_EXPRESSION: '(7 % 3)', }); + expectTypeOf(Test).toEqualTypeOf<{ + id: string; + ronin: string; + modulo: number; + }>(); }); }); @@ -126,6 +162,12 @@ describe('expressions', () => { expect(Test.fields.equalsRight.check).toEqual({ __RONIN_EXPRESSION: "('test' = __RONIN_FIELD_equalsRight)", }); + expectTypeOf(Test).toEqualTypeOf<{ + id: string; + ronin: string; + equals: string; + equalsRight: string; + }>(); }); test('not equals', () => { @@ -146,6 +188,12 @@ describe('expressions', () => { expect(Test.fields.notEqualsRight.check).toEqual({ __RONIN_EXPRESSION: "('test' != (__RONIN_FIELD_notEqualsRight != 'test'))", }); + expectTypeOf(Test).toEqualTypeOf<{ + id: string; + ronin: string; + notEquals: string; + notEqualsRight: string; + }>(); }); test('greater than', () => { @@ -166,6 +214,12 @@ describe('expressions', () => { expect(Test.fields.greaterThanRight.check).toEqual({ __RONIN_EXPRESSION: '(5 > __RONIN_FIELD_greaterThanRight)', }); + expectTypeOf(Test).toEqualTypeOf<{ + id: string; + ronin: string; + greaterThan: number; + greaterThanRight: number; + }>(); }); test('less than', () => { @@ -190,6 +244,12 @@ describe('expressions', () => { __RONIN_EXPRESSION: '((5 < __RONIN_FIELD_lessThanRight) < (5 < __RONIN_FIELD_lessThanRight))', }); + expectTypeOf(Test).toEqualTypeOf<{ + id: string; + ronin: string; + lessThan: number; + lessThanRight: number; + }>(); }); test('greater than or equal', () => { @@ -212,6 +272,12 @@ describe('expressions', () => { expect(Test.fields.greaterThanEqualRight.check).toEqual({ __RONIN_EXPRESSION: '(5 >= __RONIN_FIELD_greaterThanEqualRight)', }); + expectTypeOf(Test).toEqualTypeOf<{ + id: string; + ronin: string; + greaterThanEqual: number; + greaterThanEqualRight: number; + }>(); }); test('less than or equal', () => { @@ -232,6 +298,12 @@ describe('expressions', () => { expect(Test.fields.lessThanEqualRight.check).toEqual({ __RONIN_EXPRESSION: '(5 <= __RONIN_FIELD_lessThanEqualRight)', }); + expectTypeOf(Test).toEqualTypeOf<{ + id: string; + ronin: string; + lessThanEqual: number; + lessThanEqualRight: number; + }>(); }); }); }); @@ -302,6 +374,20 @@ describe('expressions', () => { __RONIN_EXPRESSION: '(__RONIN_FIELD_numberAdd || __RONIN_FIELD_numberSubtract)', }, }); + expectTypeOf(Test).toEqualTypeOf<{ + id: string; + ronin: string; + stringConcat: string; + numberAdd: number; + numberSubtract: number; + numberMultiply: number; + numberDivide: number; + numberModulo: number; + stringCompare: string; + numberCompare: number; + rightSideField: string; + computedAs: string; + }>(); }); test('default expression', () => { @@ -331,6 +417,14 @@ describe('expressions', () => { expect(Test.fields.test4.defaultValue).toEqual({ __RONIN_EXPRESSION: "(('Hello' || 'World') || ('Hello' || 'World'))", }); + expectTypeOf(Test).toEqualTypeOf<{ + id: string; + ronin: string; + test1: string; + test2: string; + test3: string; + test4: string; + }>(); }); test('random expression', () => { @@ -345,6 +439,11 @@ describe('expressions', () => { expect(Test.fields.test.defaultValue).toEqual({ __RONIN_EXPRESSION: 'random()', }); + expectTypeOf(Test).toEqualTypeOf<{ + id: string; + ronin: string; + test: number; + }>(); }); test('abs expression', () => { @@ -366,6 +465,12 @@ describe('expressions', () => { expect(Test.fields.expressionAbs.defaultValue).toEqual({ __RONIN_EXPRESSION: 'abs((1 - 5))', }); + expectTypeOf(Test).toEqualTypeOf<{ + id: string; + ronin: string; + numberAbs: number; + expressionAbs: number; + }>(); }); test('strftime expression', () => { @@ -385,6 +490,12 @@ describe('expressions', () => { expect(Test.fields.customFormat.defaultValue).toEqual({ __RONIN_EXPRESSION: "strftime('%H:%M:%S', 'now')", }); + expectTypeOf(Test).toEqualTypeOf<{ + id: string; + ronin: string; + currentDate: Date; + customFormat: Date; + }>(); }); test('json expressions', () => { @@ -425,6 +536,14 @@ describe('expressions', () => { expect(Test.fields.jsonInsert.defaultValue).toEqual({ __RONIN_EXPRESSION: 'json_insert(\'{"foo": "bar"}\', \'$.newKey\', \'"inserted"\')', }); + expectTypeOf(Test).toEqualTypeOf<{ + id: string; + ronin: string; + jsonPatch: string; + jsonSet: string; + jsonReplace: string; + jsonInsert: string; + }>(); }); test('nested expressions', () => { @@ -451,6 +570,13 @@ describe('expressions', () => { expect(Test.fields.complexNesting.defaultValue).toEqual({ __RONIN_EXPRESSION: 'abs((random() * (2 + 3)))', }); + expectTypeOf(Test).toEqualTypeOf<{ + id: string; + ronin: string; + randomPlusTwo: number; + absOfRandom: number; + complexNesting: number; + }>(); }); test('nested expressions', () => { @@ -467,6 +593,11 @@ describe('expressions', () => { expect(Test.fields.test.defaultValue).toEqual({ __RONIN_EXPRESSION: "(('Hello' || 'World') || ('Hello' || 'World'))", }); + expectTypeOf(Test).toEqualTypeOf<{ + id: string; + ronin: string; + test: string; + }>(); }); test('replace expression', () => { @@ -490,5 +621,11 @@ describe('expressions', () => { expect(Test.fields.multiReplace.defaultValue).toEqual({ __RONIN_EXPRESSION: "replace('hello hello hello', 'hello', 'hi')", }); + expectTypeOf(Test).toEqualTypeOf<{ + id: string; + ronin: string; + simpleReplace: string; + multiReplace: string; + }>(); }); }); diff --git a/tests/queries/index.test.ts b/tests/queries/index.test.ts index 33a4acc..c7ea90a 100644 --- a/tests/queries/index.test.ts +++ b/tests/queries/index.test.ts @@ -18,6 +18,7 @@ import { type Query, type SetQuery, } from '@ronin/compiler'; +import { expectTypeOf } from 'expect-type'; describe('syntax proxy', () => { test('using sub query', () => { @@ -432,6 +433,8 @@ describe('syntax proxy', () => { expect(queries.length === 1 ? { result: true } : null).toMatchObject({ result: true, }); + + expectTypeOf(queries).toEqualTypeOf>>(); }); test('using queries with placeholder in batch', () => { @@ -462,6 +465,8 @@ describe('syntax proxy', () => { }, }, ]); + + expectTypeOf(queries).toEqualTypeOf>>(); }); test('using options for query in batch', () => { @@ -492,6 +497,8 @@ describe('syntax proxy', () => { }, }, ]); + + expectTypeOf(queryList).toEqualTypeOf>>(); }); test('using function chaining in batch', () => { @@ -541,6 +548,8 @@ describe('syntax proxy', () => { }, }, ]); + + expectTypeOf(queryList).toEqualTypeOf>>(); }); test('using nested function as argument in batch', () => { @@ -596,6 +605,8 @@ describe('syntax proxy', () => { }, }, ]); + + expectTypeOf(queryList).toEqualTypeOf>>(); }); test('using schema query types', () => { @@ -707,6 +718,8 @@ describe('syntax proxy', () => { }, }, ]); + + expectTypeOf(queryList).toEqualTypeOf>>(); }); test('using a function call at the root', () => { diff --git a/tests/queries/statements.test.ts b/tests/queries/statements.test.ts index 7c040a8..4789960 100644 --- a/tests/queries/statements.test.ts +++ b/tests/queries/statements.test.ts @@ -1,6 +1,7 @@ import { expect, spyOn, test } from 'bun:test'; import { getBatchProxySQL, getSyntaxProxySQL } from '@/src/queries'; import type { Statement } from '@ronin/compiler'; +import { expectTypeOf } from 'expect-type'; test('using raw SQL', async () => { let statement: Statement | undefined; @@ -41,6 +42,8 @@ test('using raw SQL in batch', async () => { }, ]); + expectTypeOf(batchProxy).toEqualTypeOf>(); + expect(statementHandlerSpy).not.toHaveBeenCalled(); }); diff --git a/tests/schema/model.test.ts b/tests/schema/model.test.ts index 934bf8d..b659cb3 100644 --- a/tests/schema/model.test.ts +++ b/tests/schema/model.test.ts @@ -1,7 +1,8 @@ import { describe, expect, test } from 'bun:test'; import { getSyntaxProxy } from '@/src/queries'; import { blob, boolean, date, json, link, model, number, string } from '@/src/schema'; -import { type GetQuery, QUERY_SYMBOLS } from '@ronin/compiler'; +import { type GetQuery, QUERY_SYMBOLS, type StoredObject } from '@ronin/compiler'; +import { expectTypeOf } from 'expect-type'; describe('models', () => { test('create empty model', () => { @@ -15,9 +16,13 @@ describe('models', () => { expect(Account).toEqual({ // @ts-expect-error: The Account object has 'slug'. slug: 'account', - // @ts-expect-error: The Account object has 'pluralSlug'. pluralSlug: 'accounts', }); + + expectTypeOf(Account).toEqualTypeOf<{ + id: string; + ronin: string; + }>(); }); test('create empty model with name', () => { @@ -31,11 +36,14 @@ describe('models', () => { expect(Account).toEqual({ // @ts-expect-error: The Account object has 'slug'. slug: 'account', - // @ts-expect-error: The Account object has 'pluralSlug'. pluralSlug: 'accounts', - // @ts-expect-error: The Account object has 'name'. name: 'Account', }); + + expectTypeOf(Account).toEqualTypeOf<{ + id: string; + ronin: string; + }>(); }); test('create empty model with plural name', () => { @@ -50,13 +58,15 @@ describe('models', () => { expect(Account).toEqual({ // @ts-expect-error: The Account object has 'slug'. slug: 'account', - // @ts-expect-error: The Account object has 'pluralSlug'. pluralSlug: 'accounts', - // @ts-expect-error: The Account object has 'name'. name: 'Account', - // @ts-expect-error: The Account object has 'pluralName'. pluralName: 'Accounts', }); + + expectTypeOf(Account).toEqualTypeOf<{ + id: string; + ronin: string; + }>(); }); test('create empty model without plural slug', () => { @@ -73,6 +83,11 @@ describe('models', () => { name: 'Account', fields: {}, }); + + expectTypeOf(Account).toEqualTypeOf<{ + id: string; + ronin: string; + }>(); }); test('create empty model with name and plural name', () => { @@ -93,6 +108,11 @@ describe('models', () => { pluralName: 'Accounts', fields: {}, }); + + expectTypeOf(Account).toEqualTypeOf<{ + id: string; + ronin: string; + }>(); }); test('create empty model with identifier', () => { @@ -119,6 +139,11 @@ describe('models', () => { }, fields: {}, }); + + expectTypeOf(Account).toEqualTypeOf<{ + id: string; + ronin: string; + }>(); }); test('create empty model with id prefix', () => { @@ -137,6 +162,11 @@ describe('models', () => { idPrefix: 'acc_', fields: {}, }); + + expectTypeOf(Account).toEqualTypeOf<{ + id: string; + ronin: string; + }>(); }); test('create simple model', () => { @@ -162,6 +192,12 @@ describe('models', () => { }, }, }); + + expectTypeOf(Account).toEqualTypeOf<{ + id: string; + ronin: string; + name: string; + }>(); }); test('create simple model with blob field', () => { @@ -191,6 +227,13 @@ describe('models', () => { }, }, }); + + expectTypeOf(Account).toEqualTypeOf<{ + id: string; + ronin: string; + name: string; + avatar: StoredObject; + }>(); }); test('create simple model with json field', () => { @@ -220,6 +263,13 @@ describe('models', () => { }, }, }); + + expectTypeOf(Account).toEqualTypeOf<{ + id: string; + ronin: string; + name: string; + bio: object; + }>(); }); test('create simple model with date field', () => { @@ -249,6 +299,13 @@ describe('models', () => { }, }, }); + + expectTypeOf(Account).toEqualTypeOf<{ + id: string; + ronin: string; + name: string; + birthday: Date; + }>(); }); test('create simple model with custom field name', () => { @@ -275,6 +332,12 @@ describe('models', () => { }, }, }); + + expectTypeOf(Account).toEqualTypeOf<{ + id: string; + ronin: string; + name: string; + }>(); }); test('create simple model with field name inferred from slug', () => { @@ -300,6 +363,12 @@ describe('models', () => { }, }, }); + + expectTypeOf(Account).toEqualTypeOf<{ + id: string; + ronin: string; + createdAt: string; + }>(); }); test('create model with multiple fields', () => { @@ -342,6 +411,16 @@ describe('models', () => { }, }, }); + + expectTypeOf(Account).toEqualTypeOf<{ + id: string; + ronin: string; + name: string; + email: string; + emailVerified: boolean; + password: string; + follower: number; + }>(); }); test('create model with unique field', () => { @@ -368,6 +447,12 @@ describe('models', () => { }, }, }); + + expectTypeOf(Account).toEqualTypeOf<{ + id: string; + ronin: string; + name: string; + }>(); }); test('create model with link field', () => { @@ -409,6 +494,12 @@ describe('models', () => { }, }); + expectTypeOf(Account).toEqualTypeOf<{ + id: string; + ronin: string; + name: string; + }>(); + expect(Post).toBeTypeOf('object'); expect(Post).toEqual({ @@ -427,6 +518,12 @@ describe('models', () => { }, }, }); + + expectTypeOf(Post).toEqualTypeOf<{ + id: string; + ronin: string; + author: string; + }>(); }); test('create model with index', () => { @@ -464,6 +561,12 @@ describe('models', () => { }, }, }); + + expectTypeOf(Account).toEqualTypeOf<{ + id: string; + ronin: string; + name: string; + }>(); }); test('create model with invalid index field', () => { @@ -504,6 +607,7 @@ describe('models', () => { }, indexes: { name: { + // @ts-expect-error Types should block users from doing this fields: [], }, }, @@ -572,11 +676,18 @@ describe('models', () => { }, }, }); + + expectTypeOf(Account).toEqualTypeOf<{ + id: string; + ronin: string; + name: string; + }>(); }); test('create model with presets', () => { const Account = model({ slug: 'account', + // fields: {}, presets: { onlyName: { instructions: { @@ -607,6 +718,11 @@ describe('models', () => { }, }, }); + + expectTypeOf(Account).toEqualTypeOf<{ + id: string; + ronin: string; + }>(); }); test('create model with presets including sub queries', () => { @@ -659,6 +775,12 @@ describe('models', () => { }, }, }); + + expectTypeOf(Member).toEqualTypeOf<{ + id: string; + ronin: string; + account: string; + }>(); }); test('create model with nested fields', () => { @@ -691,5 +813,14 @@ describe('models', () => { }, }, }); + + expectTypeOf(Account).toEqualTypeOf<{ + id: string; + ronin: string; + address: { + country: string; + city: string; + }; + }>(); }); });