Skip to content

Commit

Permalink
refactor(store,world): refactor types, remove redundant casts (#2555)
Browse files Browse the repository at this point in the history
  • Loading branch information
ssalbdivad authored Apr 15, 2024
1 parent a825199 commit 9720b56
Show file tree
Hide file tree
Showing 32 changed files with 158 additions and 143 deletions.
6 changes: 6 additions & 0 deletions .changeset/late-bats-smash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@latticexyz/store": patch
"@latticexyz/world": patch
---

Internal type improvements.
2 changes: 1 addition & 1 deletion packages/store/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"test:ci": "pnpm run test"
},
"dependencies": {
"@arktype/util": "0.0.27",
"@arktype/util": "0.0.29",
"@latticexyz/common": "workspace:*",
"@latticexyz/config": "workspace:*",
"@latticexyz/protocol-parser": "workspace:*",
Expand Down
10 changes: 8 additions & 2 deletions packages/store/ts/config/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,22 @@ export const PATH_DEFAULTS = {
codegenIndexFilename: "index.sol",
} as const;

export type PATH_DEFAULTS = typeof PATH_DEFAULTS;

export const DEFAULTS = {
namespace: "",
enums: {} as Record<string, never>,
userTypes: {} as Record<string, never>,
enums: {},
userTypes: {},
} as const;

export type DEFAULTS = typeof DEFAULTS;

export const TABLE_DEFAULTS = {
directory: "tables",
keySchema: { key: "bytes32" },
tableIdArgument: false,
storeArgument: false,
offchainOnly: false,
} as const;

export type TABLE_DEFAULTS = typeof TABLE_DEFAULTS;
4 changes: 2 additions & 2 deletions packages/store/ts/config/v2/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { CODEGEN_DEFAULTS } from "./defaults";
import { isObject, mergeIfUndefined } from "./generics";

export type resolveCodegen<codegen> = codegen extends {}
? mergeIfUndefined<codegen, typeof CODEGEN_DEFAULTS>
: typeof CODEGEN_DEFAULTS;
? mergeIfUndefined<codegen, CODEGEN_DEFAULTS>
: CODEGEN_DEFAULTS;

export function resolveCodegen<codegen>(codegen: codegen): resolveCodegen<codegen> {
return (
Expand Down
2 changes: 1 addition & 1 deletion packages/store/ts/config/v2/compat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,5 +71,5 @@ export function storeToV1<store>(store: conform<store, Store>): storeToV1<store>
codegenIndexFilename: store.codegen.indexFilename,
tables: resolvedTables,
v2: store,
} as unknown as storeToV1<store>;
} as never;
}
10 changes: 10 additions & 0 deletions packages/store/ts/config/v2/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,31 @@ export const CODEGEN_DEFAULTS = {
indexFilename: "index.sol",
} as const;

export type CODEGEN_DEFAULTS = typeof CODEGEN_DEFAULTS;

export const TABLE_CODEGEN_DEFAULTS = {
outputDirectory: "tables",
tableIdArgument: false,
storeArgument: false,
} as const;

export type TABLE_CODEGEN_DEFAULTS = typeof TABLE_CODEGEN_DEFAULTS;

export const TABLE_DEPLOY_DEFAULTS = {
disabled: false,
} as const;

export type TABLE_DEPLOY_DEFAULTS = typeof TABLE_DEPLOY_DEFAULTS;

export const TABLE_DEFAULTS = {
namespace: "",
type: "table",
} as const;

export type TABLE_DEFAULTS = typeof TABLE_DEFAULTS;

export const CONFIG_DEFAULTS = {
namespace: "",
} as const;

export type CONFIG_DEFAULTS = typeof CONFIG_DEFAULTS;
4 changes: 2 additions & 2 deletions packages/store/ts/config/v2/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ export function scopeWithEnums<enums, scope extends AbiTypeScope = AbiTypeScope>
): scopeWithEnums<enums, scope> {
if (isEnums(enums)) {
const enumScope = Object.fromEntries(Object.keys(enums).map((key) => [key, "uint8" as const]));
return extendScope(scope, enumScope) as scopeWithEnums<enums, scope>;
return extendScope(scope, enumScope) as never;
}
return scope as scopeWithEnums<enums, scope>;
return scope as never;
}

export type resolveEnums<enums> = { readonly [key in keyof enums]: Readonly<enums[key]> };
20 changes: 15 additions & 5 deletions packages/store/ts/config/v2/generics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,20 @@ import { merge } from "@arktype/util";
export type get<input, key> = key extends keyof input ? input[key] : undefined;

export function get<input, key extends PropertyKey>(input: input, key: key): get<input, key> {
return (typeof input === "object" && input != null && hasOwnKey(input, key) ? input[key] : undefined) as get<
input,
key
>;
return (typeof input === "object" && input != null && hasOwnKey(input, key) ? input[key] : undefined) as never;
}

export type getPath<input, path extends readonly PropertyKey[]> = path extends readonly [
infer head,
...infer tail extends PropertyKey[],
]
? head extends keyof input
? getPath<input[head], tail>
: undefined
: input;

export function getPath<input, path extends readonly PropertyKey[]>(input: input, path: path): getPath<input, path> {
return path.length ? (getPath(get(input, path[0]), path.slice(1)) as never) : (input as never);
}

export function hasOwnKey<obj, const key extends PropertyKey>(
Expand Down Expand Up @@ -39,5 +49,5 @@ export function mergeIfUndefined<base extends object, merged extends object>(
const allKeys = [...new Set([...Object.keys(base), ...Object.keys(merged)])];
return Object.fromEntries(
allKeys.map((key) => [key, base[key as keyof base] ?? merged[key as keyof merged]]),
) as mergeIfUndefined<base, merged>;
) as never;
}
3 changes: 2 additions & 1 deletion packages/store/ts/config/v2/input.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Hex } from "viem";
import { Codegen, Enums, TableCodegen, TableDeploy, UserTypes } from "./output";
import { Scope } from "./scope";
import { evaluate } from "@arktype/util";

export type SchemaInput = {
readonly [key: string]: string;
Expand Down Expand Up @@ -41,4 +42,4 @@ export type TablesWithShorthandsInput = {
readonly [key: string]: TableInput | TableShorthandInput;
};

export type StoreWithShorthandsInput = Omit<StoreInput, "tables"> & { tables: TablesWithShorthandsInput };
export type StoreWithShorthandsInput = evaluate<Omit<StoreInput, "tables"> & { tables: TablesWithShorthandsInput }>;
11 changes: 7 additions & 4 deletions packages/store/ts/config/v2/output.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { evaluate } from "@arktype/util";
import { AbiType, Schema, Table as BaseTable } from "@latticexyz/config";

export type { AbiType, Schema };
Expand All @@ -21,10 +22,12 @@ export type TableDeploy = {
readonly disabled: boolean;
};

export type Table = BaseTable & {
readonly codegen: TableCodegen;
readonly deploy: TableDeploy;
};
export type Table = evaluate<
BaseTable & {
readonly codegen: TableCodegen;
readonly deploy: TableDeploy;
}
>;

export type Codegen = {
readonly storeImportPath: string;
Expand Down
12 changes: 5 additions & 7 deletions packages/store/ts/config/v2/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export type validateSchema<schema, scope extends Scope = AbiTypeScope> = schema

export function validateSchema<scope extends Scope = AbiTypeScope>(
schema: unknown,
scope: scope = AbiTypeScope as unknown as scope,
scope: scope = AbiTypeScope as never,
): asserts schema is SchemaInput {
if (!isObject(schema)) {
throw new Error(`Expected schema, received ${JSON.stringify(schema)}`);
Expand Down Expand Up @@ -46,26 +46,24 @@ export function resolveSchema<schema extends SchemaInput, scope extends Scope =
Object.entries(schema).map(([key, internalType]) => [
key,
{
type: isFixedArrayAbiType(internalType)
? fixedArrayToArray(internalType)
: scope.types[internalType as keyof typeof scope.types],
type: isFixedArrayAbiType(internalType) ? fixedArrayToArray(internalType) : scope.types[internalType as never],
internalType,
},
]),
) as unknown as resolveSchema<schema, scope>;
) as never;
}

export function defineSchema<schema, scope extends AbiTypeScope = AbiTypeScope>(
schema: validateSchema<schema, scope>,
scope: scope = AbiTypeScope as scope,
): resolveSchema<schema, scope> {
validateSchema(schema, scope);
return resolveSchema(schema, scope) as resolveSchema<schema, scope>;
return resolveSchema(schema, scope) as never;
}

export function isSchemaInput<scope extends Scope = AbiTypeScope>(
input: unknown,
scope: scope = AbiTypeScope as unknown as scope,
scope: scope = AbiTypeScope as never,
): input is SchemaInput {
return (
typeof input === "object" &&
Expand Down
2 changes: 1 addition & 1 deletion packages/store/ts/config/v2/scope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export type getStaticAbiTypeKeys<
> = SchemaInput extends schema
? string
: {
[key in keyof schema]: scope["types"][schema[key] & keyof scope["types"]] extends StaticAbiType ? key : never;
[key in keyof schema]: scope["types"] extends { [_ in schema[key]]: StaticAbiType } ? key : never;
}[keyof schema];

export type extendScope<scope extends ScopeOptions, additionalTypes extends Dict<string, AbiType>> = evaluate<
Expand Down
28 changes: 13 additions & 15 deletions packages/store/ts/config/v2/store.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { evaluate, narrow } from "@arktype/util";
import { flatMorph, narrow } from "@arktype/util";
import { get, hasOwnKey, mergeIfUndefined } from "./generics";
import { UserTypes } from "./output";
import { CONFIG_DEFAULTS } from "./defaults";
Expand Down Expand Up @@ -37,13 +37,13 @@ export function validateStore(store: unknown): asserts store is StoreInput {
}
}

type keyPrefix<store> = "namespace" extends keyof store
? store["namespace"] extends ""
type keyPrefix<store> = store extends { namespace: infer namespace extends string }
? namespace extends ""
? ""
: `${store["namespace"] & string}__`
: `${namespace}__`
: "";

export type resolveStore<store> = evaluate<{
export type resolveStore<store> = {
readonly tables: "tables" extends keyof store
? resolveTables<
{
Expand All @@ -57,29 +57,27 @@ export type resolveStore<store> = evaluate<{
: {};
readonly userTypes: "userTypes" extends keyof store ? store["userTypes"] : {};
readonly enums: "enums" extends keyof store ? resolveEnums<store["enums"]> : {};
readonly namespace: "namespace" extends keyof store ? store["namespace"] : (typeof CONFIG_DEFAULTS)["namespace"];
readonly namespace: "namespace" extends keyof store ? store["namespace"] : CONFIG_DEFAULTS["namespace"];
readonly codegen: "codegen" extends keyof store ? resolveCodegen<store["codegen"]> : resolveCodegen<{}>;
}>;
};

export function resolveStore<const store extends StoreInput>(store: store): resolveStore<store> {
return {
tables: resolveTables(
Object.fromEntries(
Object.entries(store.tables ?? {}).map(([tableKey, table]) => {
const key = store.namespace ? `${store.namespace}__${tableKey}` : tableKey;
return [key, mergeIfUndefined(table, { namespace: store.namespace, name: tableKey })];
}),
),
flatMorph(store.tables ?? {}, (tableKey, table) => {
const key = store.namespace ? `${store.namespace}__${tableKey}` : tableKey;
return [key, mergeIfUndefined(table, { namespace: store.namespace, name: tableKey })];
}),
extendedScope(store),
),
userTypes: store.userTypes ?? {},
enums: store.enums ?? {},
namespace: store.namespace ?? CONFIG_DEFAULTS["namespace"],
codegen: resolveCodegen(store.codegen),
} as unknown as resolveStore<store>;
} as never;
}

export function defineStore<const store>(store: validateStore<store>): resolveStore<store> {
validateStore(store);
return resolveStore(store) as unknown as resolveStore<store>;
return resolveStore(store) as never;
}
4 changes: 2 additions & 2 deletions packages/store/ts/config/v2/storeWithShorthands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ export function resolveStoreWithShorthands<const store extends StoreWithShorthan
};

validateStore(fullConfig);
return resolveStore(fullConfig) as unknown as resolveStoreWithShorthands<store>;
return resolveStore(fullConfig) as never;
}

export function defineStoreWithShorthands<const store>(
store: validateStoreWithShorthands<store>,
): resolveStoreWithShorthands<store> {
validateStoreWithShorthands(store);
return resolveStoreWithShorthands(store) as unknown as resolveStoreWithShorthands<store>;
return resolveStoreWithShorthands(store) as never;
}
34 changes: 17 additions & 17 deletions packages/store/ts/config/v2/table.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ErrorMessage, conform, narrow, requiredKeyOf } from "@arktype/util";
import { ErrorMessage, conform, evaluate, narrow, requiredKeyOf } from "@arktype/util";
import { isStaticAbiType } from "@latticexyz/schema-type/internal";
import { Hex } from "viem";
import { get, hasOwnKey, mergeIfUndefined } from "./generics";
Expand All @@ -20,7 +20,7 @@ function getValidKeys<schema extends SchemaInput, scope extends Scope = AbiTypeS
): ValidKeys<schema, scope> {
return Object.entries(schema)
.filter(([, internalType]) => hasOwnKey(scope.types, internalType) && isStaticAbiType(scope.types[internalType]))
.map(([key]) => key) as unknown as ValidKeys<schema, scope>;
.map(([key]) => key) as never;
}

export function isValidPrimaryKey<schema extends SchemaInput, scope extends Scope>(
Expand All @@ -45,17 +45,17 @@ export type validateKeys<validKeys extends PropertyKey, keys> = keys extends rea

export type ValidateTableOptions = { inStoreContext: boolean };

export type requiredTableKey<inStoreContext extends boolean> = Exclude<
requiredKeyOf<TableInput>,
inStoreContext extends true ? "name" | "namespace" : ""
>;

export type validateTable<
input,
scope extends Scope = AbiTypeScope,
options extends ValidateTableOptions = { inStoreContext: false },
> = {
[key in
| keyof input
| Exclude<
requiredKeyOf<TableInput>,
options["inStoreContext"] extends true ? "name" | "namespace" : ""
>]: key extends "key"
[key in keyof input | requiredTableKey<options["inStoreContext"]>]: key extends "key"
? validateKeys<getStaticAbiTypeKeys<conform<get<input, "schema">, SchemaInput>, scope>, get<input, key>>
: key extends "schema"
? validateSchema<get<input, key>, scope>
Expand Down Expand Up @@ -101,22 +101,22 @@ export function validateTable<input, scope extends Scope = AbiTypeScope>(
}
}

export type resolveTableCodegen<input extends TableInput> = {
export type resolveTableCodegen<input extends TableInput> = evaluate<{
[key in keyof TableCodegen]-?: key extends keyof input["codegen"]
? undefined extends input["codegen"][key]
? key extends "dataStruct"
? boolean
: key extends keyof typeof TABLE_CODEGEN_DEFAULTS
? (typeof TABLE_CODEGEN_DEFAULTS)[key]
: key extends keyof TABLE_CODEGEN_DEFAULTS
? TABLE_CODEGEN_DEFAULTS[key]
: never
: input["codegen"][key]
: // dataStruct isn't narrowed, because its value is conditional on the number of value schema fields
key extends "dataStruct"
? boolean
: key extends keyof typeof TABLE_CODEGEN_DEFAULTS
? (typeof TABLE_CODEGEN_DEFAULTS)[key]
: key extends keyof TABLE_CODEGEN_DEFAULTS
? TABLE_CODEGEN_DEFAULTS[key]
: never;
};
}>;

export function resolveTableCodegen<input extends TableInput>(input: input): resolveTableCodegen<input> {
const options = input.codegen;
Expand All @@ -140,7 +140,7 @@ export type resolveTable<input, scope extends Scope = Scope> = input extends Tab
readonly codegen: resolveTableCodegen<input>;
readonly deploy: mergeIfUndefined<
undefined extends input["deploy"] ? {} : input["deploy"],
typeof TABLE_DEPLOY_DEFAULTS
TABLE_DEPLOY_DEFAULTS
>;
}
: never;
Expand All @@ -163,13 +163,13 @@ export function resolveTable<input extends TableInput, scope extends Scope = Abi
schema: resolveSchema(input.schema, scope),
codegen: resolveTableCodegen(input),
deploy: mergeIfUndefined(input.deploy ?? {}, TABLE_DEPLOY_DEFAULTS),
} as unknown as resolveTable<input, scope>;
} as never;
}

export function defineTable<input, scope extends Scope = AbiTypeScope>(
input: validateTable<input, scope>,
scope: scope = AbiTypeScope as unknown as scope,
): resolveTable<input, scope> {
validateTable(input, scope);
return resolveTable(input, scope) as resolveTable<input, scope>;
return resolveTable(input, scope) as never;
}
Loading

0 comments on commit 9720b56

Please sign in to comment.