diff --git a/.changeset/thin-chairs-compare.md b/.changeset/thin-chairs-compare.md new file mode 100644 index 0000000000..6d76a5ab99 --- /dev/null +++ b/.changeset/thin-chairs-compare.md @@ -0,0 +1,7 @@ +--- +"@latticexyz/cli": patch +"@latticexyz/common": patch +"@latticexyz/store": major +--- + +Changed the `userTypes` property to accept `{ filePath: string, internalType: SchemaAbiType }` to enable strong type inference from the config. diff --git a/packages/cli/scripts/generate-test-tables.ts b/packages/cli/scripts/generate-test-tables.ts index 1d14474bd2..450dda72fc 100644 --- a/packages/cli/scripts/generate-test-tables.ts +++ b/packages/cli/scripts/generate-test-tables.ts @@ -83,11 +83,11 @@ try { }, userTypes: { - TestTypeAddress: "./contracts/src/types.sol", - TestTypeInt64: "./contracts/src/types.sol", - TestTypeBool: "./contracts/src/types.sol", - TestTypeUint128: "./contracts/src/types.sol", - ResourceId: "@latticexyz/store/src/ResourceId.sol", + TestTypeAddress: { filePath: "./contracts/src/types.sol", internalType: "address" }, + TestTypeInt64: { filePath: "./contracts/src/types.sol", internalType: "int64" }, + TestTypeBool: { filePath: "./contracts/src/types.sol", internalType: "bool" }, + TestTypeUint128: { filePath: "./contracts/src/types.sol", internalType: "uint128" }, + ResourceId: { filePath: "@latticexyz/store/src/ResourceId.sol", internalType: "bytes32" }, }, }); } catch (error: unknown) { diff --git a/packages/common/src/codegen/utils/loadUserTypesFile.ts b/packages/common/src/codegen/utils/loadUserTypesFile.ts index 8573e8c165..699575da6d 100644 --- a/packages/common/src/codegen/utils/loadUserTypesFile.ts +++ b/packages/common/src/codegen/utils/loadUserTypesFile.ts @@ -1,14 +1,20 @@ import { readFileSync } from "fs"; import path from "path"; import { SolidityUserDefinedType, extractUserTypes } from "./extractUserTypes"; +import { MUDError } from "../../errors"; + +export type UserType = { + filePath: string; + internalType: string; +}; export function loadAndExtractUserTypes( - userTypes: Record, + userTypes: Record, outputBaseDirectory: string, remappings: [string, string][] ): Record { const userTypesPerFile: Record = {}; - for (const [userTypeName, unresolvedFilePath] of Object.entries(userTypes)) { + for (const [userTypeName, { filePath: unresolvedFilePath }] of Object.entries(userTypes)) { if (!(unresolvedFilePath in userTypesPerFile)) { userTypesPerFile[unresolvedFilePath] = []; } @@ -17,7 +23,18 @@ export function loadAndExtractUserTypes( let extractedUserTypes: Record = {}; for (const [unresolvedFilePath, userTypeNames] of Object.entries(userTypesPerFile)) { const { filePath, data } = loadUserTypesFile(outputBaseDirectory, unresolvedFilePath, remappings); - extractedUserTypes = Object.assign(userTypes, extractUserTypes(data, userTypeNames, filePath)); + const userTypesInFile = extractUserTypes(data, userTypeNames, filePath); + + // Verify the actual user type matches the internalType specified in the config + for (const [userTypeName, userType] of Object.entries(userTypesInFile)) { + if (userType.internalTypeId !== userTypes[userTypeName].internalType) { + throw new MUDError( + `User type "${userTypeName}" has internal type "${userType.internalTypeId}" but config specifies "${userTypes[userTypeName].internalType}"` + ); + } + } + + extractedUserTypes = Object.assign(extractedUserTypes, userTypesInFile); } return extractedUserTypes; } diff --git a/packages/store/ts/config/storeConfig.ts b/packages/store/ts/config/storeConfig.ts index b7df302df9..cd8959311f 100644 --- a/packages/store/ts/config/storeConfig.ts +++ b/packages/store/ts/config/storeConfig.ts @@ -22,6 +22,7 @@ import { zName, } from "@latticexyz/config"; import { DEFAULTS, PATH_DEFAULTS, TABLE_DEFAULTS } from "./defaults"; +import { UserType } from "@latticexyz/common/codegen"; const zTableName = zObjectName; const zKeyName = zValueName; @@ -241,7 +242,7 @@ export const zEnumsConfig = z.object({ * ************************************************************************/ -export type UserTypesConfig = never extends UserTypeNames +export type UserTypesConfig = never extends UserTypeNames ? { /** * User types mapped to file paths from which to import them. @@ -250,7 +251,7 @@ export type UserTypesConfig = never extend * * (user types are inferred to be absent) */ - userTypes?: Record; + userTypes?: Record; } : StringForUnion extends UserTypeNames ? { @@ -261,7 +262,7 @@ export type UserTypesConfig = never extend * * (user types aren't inferred - use `mudConfig` or `storeConfig` helper, and `as const` for variables) */ - userTypes?: Record; + userTypes?: Record; } : { /** @@ -271,15 +272,20 @@ export type UserTypesConfig = never extend * * User types defined here can be used as types in table schemas/keys */ - userTypes: Record; + userTypes: Record; }; export type FullUserTypesConfig = { userTypes: Record; }; +const zUserTypeConfig = z.object({ + filePath: z.string(), + internalType: z.string(), +}); + export const zUserTypesConfig = z.object({ - userTypes: z.record(zUserTypeName, z.string()).default(DEFAULTS.userTypes), + userTypes: z.record(zUserTypeName, zUserTypeConfig).default(DEFAULTS.userTypes), }); /************************************************************************