diff --git a/packages/realm/package.json b/packages/realm/package.json index 249a952591..d6868605e8 100644 --- a/packages/realm/package.json +++ b/packages/realm/package.json @@ -73,7 +73,7 @@ ], "scripts": { "test": "wireit", - "test:types": "tsc --project type-tests/tsconfig.json", + "test:types": "wireit", "lint": "eslint --ext .js,.mjs,.ts .", "prebuild": "tsx ./src/scripts/build/cli.ts", "prebuild-apple": "wireit", @@ -102,6 +102,12 @@ "TSX_TSCONFIG_PATH": "tsconfig.tests.json" } }, + "test:types": { + "command": "tsc --project type-tests/tsconfig.json", + "dependencies": [ + "build:ts" + ] + }, "prebuild-apple": { "command": "tsx ./src/scripts/build/cli.ts build-apple", "files": [ diff --git a/packages/realm/type-tests/nested-unmanaged-tests.ts b/packages/realm/type-tests/nested-unmanaged-tests.ts new file mode 100644 index 0000000000..7c0ef23b6e --- /dev/null +++ b/packages/realm/type-tests/nested-unmanaged-tests.ts @@ -0,0 +1,253 @@ +//////////////////////////////////////////////////////////////////////////// +// +// Copyright 2024 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////// + +/* eslint-disable @typescript-eslint/no-unused-vars -- We're just testing types */ + +// This file checks that the Unmanaged type re-mappings work correctly for Realm types, especially when there are nested classes + +import * as BSON from "bson"; +import Realm from "realm"; + +// Simple Unmanaged remapping tests +type MyObj = { + stringProp1: Realm.Types.String; + stringProp2: Realm.Types.String; + nullableStringProp1: Realm.Types.String | null; + nullableStringProp2: Realm.Types.String | null; + stringOrUndefined1?: Realm.Types.String; + stringOrUndefined2?: Realm.Types.String; + + listStringProp1: Realm.Types.List; + listStringProp2: Realm.Types.List; + listStringProp3: Realm.Types.List; + nullableListStringProp1: Realm.Types.List | null; + nullableListStringProp2: Realm.Types.List | null; + nullableListStringProp3: Realm.Types.List | null; + listStringOrUndefinedProp1?: Realm.Types.List; + listStringOrUndefinedProp2?: Realm.Types.List; + listStringOrUndefinedProp3?: Realm.Types.List; +}; +const test: Realm.Unmanaged = { + stringProp1: "test", + // @ts-expect-error - Expected: invalid + stringProp2: null, + nullableStringProp1: null, + nullableStringProp2: "blah", + stringOrUndefined1: undefined, + stringOrUndefined2: "sad", + + someExtraPropertyNotSpecifiedOnMyObj: "string", + + listStringProp1: ["test"], + // @ts-expect-error - Expected: invalid + listStringProp2: [1], + // @ts-expect-error - Expected: invalid + listStringProp3: null, + nullableListStringProp1: null, + nullableListStringProp2: ["test"], + // @ts-expect-error - Expected: invalid + nullableListStringProp3: [1], + listStringOrUndefinedProp1: undefined, + listStringOrUndefinedProp2: ["test"], + // @ts-expect-error - Expected: invalid + listStringOrUndefinedProp3: [1], +}; +test; + +// Extended functionality tests (ensure RequiredProperties work) +class RealmClassWithRequiredParams extends Realm.Object< + RealmClassWithRequiredParams, + | "aMandatoryString" + | "anOptionalString" + | "aMandatoryBool" + | "aMandatoryInt" + | "aMandatoryFloat" + | "aMandatoryDouble" + | "aMandatoryDecimal128" + | "aMandatoryObjectId" + | "aMandatoryData" + | "aMandatoryDate" + | "aMandatoryMixed" + | "aMandatoryUuid" +> { + public aMandatoryString!: Realm.Types.String; + public anOptionalString?: Realm.Types.String; + public aMandatoryBool!: Realm.Types.Bool; + public aMandatoryInt!: Realm.Types.Int; + public aMandatoryFloat!: Realm.Types.Float; + public aMandatoryDouble!: Realm.Types.Double; + public aMandatoryDecimal128!: Realm.Types.Decimal128; + public aMandatoryObjectId!: Realm.Types.ObjectId; + public aMandatoryData!: Realm.Types.Data; + public aMandatoryDate!: Realm.Types.Date; + public aMandatoryMixed!: Realm.Types.Mixed; + public aMandatoryUuid!: Realm.Types.UUID; + + static schema: Realm.ObjectSchema = { + name: "RealmClassWithRequiredParams", + properties: { + aMandatoryString: "string", + anOptionalString: "string?", + aMandatoryBool: "bool", + aMandatoryInt: "int", + aMandatoryFloat: "float", + aMandatoryDouble: "double", + aMandatoryDecimal128: "decimal128", + aMandatoryObjectId: "objectId", + aMandatoryData: "data", + aMandatoryDate: "date", + // TODO list + // TODO linkingObjects? + // TODO dictionary + // TODO set + aMandatoryMixed: "mixed", + aMandatoryUuid: "uuid", + }, + }; + + public someFunction() { + return 1; + } +} + +class RealmClassWithoutRequiredParams extends Realm.Object { + public aMandatoryString!: Realm.Types.String; + public anOptionalString?: Realm.Types.String; + public aMandatoryBool!: Realm.Types.Bool; + public aMandatoryInt!: Realm.Types.Int; + public aMandatoryFloat!: Realm.Types.Float; + public aMandatoryDouble!: Realm.Types.Double; + public aMandatoryDecimal128!: Realm.Types.Decimal128; + public aMandatoryObjectId!: Realm.Types.ObjectId; + public aMandatoryData!: Realm.Types.Data; + public aMandatoryDate!: Realm.Types.Date; + public aMandatoryMixed!: Realm.Types.Mixed; + public aMandatoryUuid!: Realm.Types.UUID; + + static schema: Realm.ObjectSchema = { + name: "RealmClassWithoutRequiredParams", + properties: { + aMandatoryString: "string", + anOptionalString: "string?", + aMandatoryBool: "bool", + aMandatoryInt: "int", + aMandatoryFloat: "float", + aMandatoryDouble: "double", + aMandatoryDecimal128: "decimal128", + aMandatoryObjectId: "objectId", + aMandatoryData: "data", + aMandatoryDate: "date", + aMandatoryMixed: "mixed", + aMandatoryUuid: "uuid", + }, + }; + + public someFunction() { + return 1; + } +} + +const realm = new Realm({ schema: [RealmClassWithRequiredParams, RealmClassWithoutRequiredParams] }); + +// @ts-expect-error - a String shouldn't be accepted as 'values' +new RealmClassWithRequiredParams(realm, "this shouldn't be accepted"); + +// TODO - this doesn't error and it should +// should-be-@ts-expect-error - a String shouldn't be accepted as 'values' +new RealmClassWithoutRequiredParams(realm, "this shouldn't be accepted"); + +// Realm.create() tests + +realm.write(() => { + // === Realm.Object classes === + // @ts-expect-error - Empty object not allowed due to being required params + realm.create(RealmClassWithRequiredParams, new RealmClassWithRequiredParams(realm, {})); + + realm.create( + RealmClassWithRequiredParams, + new RealmClassWithRequiredParams(realm, { + aMandatoryString: "string", + // anOptionalString is a required param, but it's of type "string?" so doesn't need to be specified + aMandatoryBool: true, + aMandatoryInt: 1, + aMandatoryFloat: 1.2, + aMandatoryDouble: 1.3, + aMandatoryDecimal128: new BSON.Decimal128("123"), + aMandatoryObjectId: new BSON.ObjectId(123), + aMandatoryData: new ArrayBuffer(123), + aMandatoryDate: new Date(), + aMandatoryMixed: "a", + aMandatoryUuid: new BSON.UUID(), + }), + ); + + realm.create(RealmClassWithoutRequiredParams, new RealmClassWithoutRequiredParams(realm, {})); + + // === Unmanaged objects === + // TODO - Should this be erroring? The class has required types so shouldn't they be automatically required in the Unmanaged version too? Is this possible + // should-be-@ts-expect-error - Empty object not allowed due to being required params + realm.create(RealmClassWithRequiredParams, {}); + + realm.create(RealmClassWithRequiredParams, { + aMandatoryString: "string", + // anOptionalString is a required param, but it's of type "string?" so doesn't need to be specified + aMandatoryBool: true, + aMandatoryInt: 1, + aMandatoryFloat: 1.2, + aMandatoryDouble: 1.3, + aMandatoryDecimal128: new BSON.Decimal128("123"), + aMandatoryObjectId: new BSON.ObjectId(123), + aMandatoryData: new ArrayBuffer(123), + aMandatoryDate: new Date(), + aMandatoryMixed: 1, + aMandatoryUuid: new BSON.UUID(), + }); + + realm.create(RealmClassWithoutRequiredParams, {}); +}); + +// Shouldn't be expecting someFunction(), keys(), entries(), etc +const realmObjectPropertiesOmitted1: Realm.Unmanaged = {}; + +const realmObjectPropertiesOmitted2: Realm.Unmanaged< + RealmClassWithRequiredParams, + | "aMandatoryString" + | "anOptionalString" + | "aMandatoryBool" + | "aMandatoryInt" + | "aMandatoryFloat" + | "aMandatoryDouble" + | "aMandatoryDecimal128" + | "aMandatoryObjectId" + | "aMandatoryData" + | "aMandatoryDate" + | "aMandatoryMixed" + | "aMandatoryUuid" +> = { + aMandatoryString: "string", + aMandatoryBool: true, + aMandatoryInt: 1, + aMandatoryFloat: 1.2, + aMandatoryDouble: 1.3, + aMandatoryDecimal128: new BSON.Decimal128("123"), + aMandatoryObjectId: new BSON.ObjectId(123), + aMandatoryData: new ArrayBuffer(123), + aMandatoryDate: new Date(), + aMandatoryMixed: true, + aMandatoryUuid: new BSON.UUID(), +}; diff --git a/packages/realm/type-tests/tsconfig.json b/packages/realm/type-tests/tsconfig.json index f1a8980fc4..670aacdc8d 100644 --- a/packages/realm/type-tests/tsconfig.json +++ b/packages/realm/type-tests/tsconfig.json @@ -1,21 +1,14 @@ { - "extends": "../tsconfig.json", + "extends": "../tsconfig.public-types-check.json", "compilerOptions": { - "noEmit": true, - "noResolve": true, "esModuleInterop": true, - "lib": [ - "ES2022", - ], - "baseUrl": ".", "paths": { - "realm": ["../../.."], - "next-realm": [".."], + "realm": [ + "../dist/public-types" + ] } }, - "exclude": [], "include": [ - "../src/**/*.ts", - "type-tests.ts" + "*.ts" ] -} \ No newline at end of file +} diff --git a/packages/realm/type-tests/type-tests.ts b/packages/realm/type-tests/type-tests.ts index c60439e27c..5bb1f81dc3 100644 --- a/packages/realm/type-tests/type-tests.ts +++ b/packages/realm/type-tests/type-tests.ts @@ -18,7 +18,7 @@ /* eslint-disable @typescript-eslint/no-unused-vars -- We're just testing types */ -import { Realm as Realm2 } from "../src/index"; +import { Realm as Realm2 } from "realm"; const realm = new Realm(); const realm2: Realm = new Realm();