diff --git a/client/packages/admin/src/index.ts b/client/packages/admin/src/index.ts index 29ec457ec..dccc33a72 100644 --- a/client/packages/admin/src/index.ts +++ b/client/packages/admin/src/index.ts @@ -45,6 +45,8 @@ import { type InstaQLEntity, type InstaQLResult, type InstantRules, + type UpdateParams, + type LinkParams, } from "@instantdb/core"; import version from "./version"; @@ -951,4 +953,6 @@ export { type InstaQLEntity, type InstaQLResult, type InstantRules, + type UpdateParams, + type LinkParams, }; diff --git a/client/packages/core/src/index.ts b/client/packages/core/src/index.ts index a47dacced..571e43ded 100644 --- a/client/packages/core/src/index.ts +++ b/client/packages/core/src/index.ts @@ -62,6 +62,8 @@ import type { ValueTypes, InstantUnknownSchema, BackwardsCompatibleSchema, + UpdateParams, + LinkParams, } from "./schemaTypes"; const defaultOpenDevtool = true; @@ -730,4 +732,6 @@ export { type IInstantDatabase, type BackwardsCompatibleSchema, type InstantRules, + type UpdateParams, + type LinkParams, }; diff --git a/client/packages/core/src/instatx.ts b/client/packages/core/src/instatx.ts index f26a7adea..bad7a353a 100644 --- a/client/packages/core/src/instatx.ts +++ b/client/packages/core/src/instatx.ts @@ -1,4 +1,8 @@ -import type { DataAttrDef, IContainEntitiesAndLinks, InstantGraph, LinkAttrDef } from "./schemaTypes"; +import type { + IContainEntitiesAndLinks, + LinkParams, + UpdateParams, +} from "./schemaTypes"; type Action = "update" | "link" | "unlink" | "delete" | "merge"; type EType = string; @@ -8,38 +12,6 @@ type LookupRef = [string, any]; type Lookup = string; export type Op = [Action, EType, Id | LookupRef, Args]; -type UpdateParams< - Schema extends IContainEntitiesAndLinks, - EntityName extends keyof Schema["entities"], -> = { - [AttrName in keyof Schema["entities"][EntityName]["attrs"]]?: Schema["entities"][EntityName]["attrs"][AttrName] extends DataAttrDef< - infer ValueType, - infer IsRequired - > - ? IsRequired extends true - ? ValueType - : ValueType | null - : never; -} & (Schema extends IContainEntitiesAndLinks - ? {} - : { - [attribute: string]: any; - }); - -type LinkParams< - Schema extends IContainEntitiesAndLinks, - EntityName extends keyof Schema["entities"], -> = { - [LinkName in keyof Schema["entities"][EntityName]["links"]]?: Schema["entities"][EntityName]["links"][LinkName] extends LinkAttrDef< - infer Cardinality, - any - > - ? Cardinality extends "one" - ? string - : string | string[] - : never; -} & (Schema extends InstantGraph ? {} : { [attribute: string]: any }); - export interface TransactionChunk< Schema extends IContainEntitiesAndLinks, EntityName extends keyof Schema["entities"], diff --git a/client/packages/core/src/schemaTypes.ts b/client/packages/core/src/schemaTypes.ts index d36408ac7..b0351584b 100644 --- a/client/packages/core/src/schemaTypes.ts +++ b/client/packages/core/src/schemaTypes.ts @@ -305,21 +305,21 @@ export class InstantSchemaDef< /** * @deprecated - * `withRoomSchema` is deprecated. Define your schema in `rooms` directly: - * - * @example - * // Before: - * const schema = i.schema({ + * `withRoomSchema` is deprecated. Define your schema in `rooms` directly: + * + * @example + * // Before: + * const schema = i.schema({ * // ... * }).withRoomSchema() - * + * * // After * const schema = i.schema({ * rooms: { * // ... * } * }) - * + * * @see https://instantdb.com/docs/presence-and-topics#typesafety */ withRoomSchema<_RoomSchema extends RoomSchemaShape>() { @@ -335,7 +335,7 @@ export class InstantSchemaDef< /** * @deprecated * `i.graph` is deprecated. Use `i.schema` instead. - * + * * @see https://instantdb.com/docs/modeling-data */ export class InstantGraph< @@ -457,3 +457,31 @@ export type InstantUnknownSchema = InstantSchemaDef< UnknownLinks, UnknownRooms >; + +export type UpdateParams< + Schema extends IContainEntitiesAndLinks, + EntityName extends keyof Schema["entities"], +> = { + [AttrName in keyof Schema["entities"][EntityName]["attrs"]]?: Schema["entities"][EntityName]["attrs"][AttrName] extends DataAttrDef< + infer ValueType, + infer IsRequired + > + ? IsRequired extends true + ? ValueType + : ValueType | null + : never; +}; + +export type LinkParams< + Schema extends IContainEntitiesAndLinks, + EntityName extends keyof Schema["entities"], +> = { + [LinkName in keyof Schema["entities"][EntityName]["links"]]?: Schema["entities"][EntityName]["links"][LinkName] extends LinkAttrDef< + infer Cardinality, + any + > + ? Cardinality extends "one" + ? string + : string | string[] + : never; +}; diff --git a/client/packages/react-native/src/index.ts b/client/packages/react-native/src/index.ts index 2cb57788a..28bad783c 100644 --- a/client/packages/react-native/src/index.ts +++ b/client/packages/react-native/src/index.ts @@ -51,6 +51,8 @@ import { type InstantSchemaDef, type InstantUnknownSchema, type InstantRules, + type UpdateParams, + type LinkParams, } from "@instantdb/core"; /** @@ -147,4 +149,6 @@ export { type InstantUnknownSchema, type BackwardsCompatibleSchema, type InstantRules, + type UpdateParams, + type LinkParams, }; diff --git a/client/packages/react/src/index.ts b/client/packages/react/src/index.ts index 85776f4aa..f8fa17e71 100644 --- a/client/packages/react/src/index.ts +++ b/client/packages/react/src/index.ts @@ -40,6 +40,8 @@ import { type InstantSchemaDef, type BackwardsCompatibleSchema, type InstantRules, + type UpdateParams, + type LinkParams, } from "@instantdb/core"; import InstantReactAbstractDatabase from "./InstantReactAbstractDatabase"; @@ -95,4 +97,6 @@ export { type InstantSchemaDef, type BackwardsCompatibleSchema, type InstantRules, + type UpdateParams, + type LinkParams, }; diff --git a/client/sandbox/strong-init-vite/src/typescript_test_abstract.tsx b/client/sandbox/strong-init-vite/src/typescript_test_abstract.tsx new file mode 100644 index 000000000..81d569f96 --- /dev/null +++ b/client/sandbox/strong-init-vite/src/typescript_test_abstract.tsx @@ -0,0 +1,39 @@ +import { i, id, init, UpdateParams } from "@instantdb/admin"; + +const _schema = i.schema({ entities: { users: i.entity({ email: i.string() }) } }); +type _AppSchema = typeof _schema; +interface AppSchema extends _AppSchema {} +const schema: AppSchema = _schema; + +const db = init({ + adminToken: "...", + appId: "...", + schema, +}); + +type Collection = keyof typeof schema.entities; + +type EntityUpdate = UpdateParams; + +export const newEntity = async ( + type: T, + props: EntityUpdate, +) => { + const theId = id(); + await db.transact(db.tx[type][theId].update(props)); + return theId; +}; + +// seems good +const existing_attr_works = await newEntity("users", { email: "alice@gmail.com" }); + +// @ts-expect-error +const non_existing_attr_errors = await newEntity("users", { blabla: "bob@gmail.com" }); + +// @ts-expect-error +const wrong_type_errors = await newEntity("users", { email: 123 }); + +// to silence ts warnings +existing_attr_works; +non_existing_attr_errors; +wrong_type_errors;