diff --git a/Makefile b/Makefile index c46332a3..08a4abec 100644 --- a/Makefile +++ b/Makefile @@ -29,12 +29,12 @@ test-system-browser-watch: build: @rm -rf lib - @${BIN}/tsc + @${BIN}/tsc --project tsconfig.lib.json @${BIN}/prettier "lib/**/*.[jt]s" --write --loglevel silent @cp package.json lib @cp *.md lib @rsync --archive --prune-empty-dirs --exclude '*.ts' --relative src/./ lib - @${BIN}/tsc --outDir lib/esm --module es2020 --target es2019 + @${BIN}/tsc --project tsconfig.lib.json --outDir lib/esm --module es2020 --target es2019 @cp src/adaptor/package.esm.json lib/esm/adaptor/package.json publish: build diff --git a/package.json b/package.json index 5f7738b6..09c678b7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "typesaurus", - "version": "7.2.0", + "version": "8.0.0-alpha.1", "description": "Type-safe ODM for Firestore", "keywords": [ "Firebase", diff --git a/src/field/index.ts b/src/field/index.ts index 9e250e5b..b988da05 100644 --- a/src/field/index.ts +++ b/src/field/index.ts @@ -1,5 +1,21 @@ import { UpdateValue } from '../value' +type Whole = Type[Key] extends + | infer Value + | undefined + ? Value + : never + +type PartialValue< + Model, + Key1 extends keyof Model, + Key2 extends keyof Whole +> = Partial> extends Pick + ? Partial extends Model[Key1] + ? Whole[Key2] + : never + : Whole[Key2] + /** * The field type. It contains path to the property and property value. */ @@ -13,9 +29,13 @@ function field( value: Model[Key] | UpdateValue ): Field -function field( +function field< + Model, + Key1 extends keyof Model, + Key2 extends keyof Whole +>( key: [Key1, Key2], - value: Model[Key1][Key2] | UpdateValue + value: PartialValue | UpdateValue, Key2> ): Field function field< diff --git a/src/field/test.ts b/src/field/test.ts index dfb75ab0..027d80f2 100644 --- a/src/field/test.ts +++ b/src/field/test.ts @@ -17,14 +17,44 @@ describe('field', () => { }) it('disallows deleting required fields', () => { - type TestModel = { a: { b: { c: number; d?: string } }; e?: string } + type TestModel = { + a: { b: { c: number; d?: string } } + e?: string + f?: { [key: string]: number | undefined } + } // @ts-expect-error assertField(field(['a', 'b', 'c'], value('remove'))) assertField(field(['a', 'b', 'c'], value('increment', 1))) assertField(field(['a', 'b', 'd'], value('remove'))) assertField(field('e', 'ok')) assertField(field('e', value('remove'))) + assertField(field(['f', 'ok'], value('remove'))) + }) + + it('allows updating fields in optional partial objects', () => { + type TestModel = { + a?: { b?: number; c?: string } + d?: { e: number; f: string } + g?: { [key: string]: number | undefined } + h?: { [key: string]: number } + i: { j: number; k: string } + l?: { [key: string]: string | undefined } + } + assertField(field(['a', 'b'], 123)) + assertField(field(['a', 'b'], value('remove'))) + // @ts-expect-error + assertField(field(['d', 'e'], 123)) + // @ts-expect-error + assertField(field(['d', 'e'], value('remove'))) + assertField(field(['g', 'qwe'], 123)) + assertField(field(['i', 'j'], 123)) + assertField(field(['g', 'ok'], value('increment', 1))) + assertField(field(['h', 'ok'], value('increment', 1))) + // @ts-expect-error + assertField(field(['h', 'nope'], 123)) + // @ts-expect-error + assertField(field(['k', 'nope'], value('increment', 1))) }) }) -function assertField(fields: Field) {} +function assertField(_field: Field) {} diff --git a/src/value/index.ts b/src/value/index.ts index 2b7b1e5d..b71d6066 100644 --- a/src/value/index.ts +++ b/src/value/index.ts @@ -60,22 +60,27 @@ type MaybeValueRemoveOr = Partial< type MaybeValueRemove = Partial< Pick > extends Pick + ? ValueRemove + : Undefined extends Model[Key] ? ValueRemove : never +type Undefined = T extends undefined ? T : never + /** * The value types to use for update operation. */ -export type UpdateValue< - Model, - Key extends keyof Model -> = Model[Key] extends number - ? MaybeValueRemoveOr - : Model[Key] extends Array - ? MaybeValueRemoveOr - : Model[Key] extends Date - ? MaybeValueRemoveOr - : MaybeValueRemove +export type UpdateValue = Key extends keyof Model + ? Model[Key] extends infer Type + ? Type extends number + ? MaybeValueRemoveOr + : Type extends Array + ? MaybeValueRemoveOr + : Type extends Date + ? MaybeValueRemoveOr + : MaybeValueRemove + : never + : never /** * The value types to use for add operation. diff --git a/tsconfig.json b/tsconfig.json index e67aed61..d15268bd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,9 +8,8 @@ "sourceMap": true, "outDir": "lib", "strict": true, - "noImplicitAny": true, "esModuleInterop": true, "resolveJsonModule": true }, - "exclude": ["**/test.ts", "lib", "node_modules", "test"] + "exclude": ["lib", "node_modules"] } diff --git a/tsconfig.lib.json b/tsconfig.lib.json new file mode 100644 index 00000000..99882b4f --- /dev/null +++ b/tsconfig.lib.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig", + "exclude": ["lib", "node_modules", "**/test.ts", "test"] +}