diff --git a/packages/types/README.md b/packages/types/README.md index c7c89129..f3850d5c 100644 --- a/packages/types/README.md +++ b/packages/types/README.md @@ -78,7 +78,7 @@ export default config; type Values = InferSchemaValues; // Import Product type into your application! -export type Product = Extract; +export type Product = Values["product"]; /** * Product === { * _createdAt: string; @@ -135,7 +135,7 @@ export default config; type Values = InferSchemaValues; -export type Foo = Extract; +export type Foo = Values["foo"]; /** * Foo === { * _createdAt: string; @@ -203,7 +203,7 @@ export default config; type Values = InferSchemaValues; -export type Foo = Extract; +export type Foo = Values["foo"]; /** * Foo === { * _createdAt: string; @@ -227,3 +227,69 @@ Typescript was an after-the-fact concern with sanity, since the rise of typescri The long term goal is to deprecate the monorepo altogether. Building this seperately was to move quickly and these features should be in sanity directly (and is likely one of their internal goals). The idea is to introduce these changes iteratively into sanity itself while removing them from this library, until it's reduced to simply passing through the `define*` methods directly, and will then be deprecated. This shouldn't deter you from using it! Under the hood, it's passing all the inputs to sanity's native `define*` methods, so you shouldn't have any runtime differences. With all the typings being attempting to make their way into sanity, you should keep all the benefits of just importing the `define*` methods and noticing no differences. + +## Migrations + +### Migrating from 2.x to 3.x + +#### InferSchemaValues + +`InferSchemaValues` used to return a union of all types but now returns an object keyed off by type. This is because using `Extract` to retrieve specific type was difficult. Object types would have a `_type` for easy extraction, but all the other types were less reliable (i.e. arrays and primitives). + +```diff +export default config; + +type Values = InferSchemaValues; + +- export type Product = Extract ++ export type Product = Values["product"]; +``` + +#### InferValue + +Types used to be inferred using `InferValue` for easy exporting. Now, `InferSchemaValues` needs to be used, and individual types keyed off of it. The reason for this is that only the config has context about aliased types, so `InferValue` was always going to be missing those values. + +```diff +const product = defineType({ + name: "product", + type: "document", + title: "Product", + fields: [ + // ... + ], +}); + +- export type Product = InferValue; + +const config = defineConfig({ + // ... + schema: { + types: [ + product, + // ... + ], + }, +}); + +export default config; + +type Values = InferSchemaValues; + ++ export type Product = Values["product"]; +``` + +You can still use `_InferValue` but this is discouraged, because it will be missing the context from the config: + +```diff +const product = defineType({ + name: "product", + type: "document", + title: "Product", + fields: [ + // ... + ], +}); + +- export type Product = InferValue; ++ export type Product = _InferValue; +``` diff --git a/packages/types/src/index.test.ts b/packages/types/src/index.test.ts index 9e51a8a8..69d20c86 100644 --- a/packages/types/src/index.test.ts +++ b/packages/types/src/index.test.ts @@ -2659,20 +2659,22 @@ describe("defineConfig", () => { }, }); - expectType>().toStrictEqual< - | { - _createdAt: string; - _id: string; - _rev: string; - _type: "foo"; - _updatedAt: string; - bar?: boolean; - } - | { - _type: "baz"; - qux?: boolean; - } - >(); + type Values = InferSchemaValues; + + expectType().toStrictEqual<{ + baz: { + _type: "baz"; + qux?: boolean; + }; + foo: { + _createdAt: string; + _id: string; + _rev: string; + _type: "foo"; + _updatedAt: string; + bar?: boolean; + }; + }>(); }); it("infers aliased type value", () => { @@ -2705,24 +2707,65 @@ describe("defineConfig", () => { }, }); - expectType>().toStrictEqual< - | { - _createdAt: string; - _id: string; - _rev: string; - _type: "foo"; - _updatedAt: string; - bar?: { - _type: "bar"; - } & { - baz?: boolean; - }; - } - | { + type Values = InferSchemaValues; + + expectType().toStrictEqual<{ + bar: { + _type: "bar"; + baz?: boolean; + }; + foo: { + _createdAt: string; + _id: string; + _rev: string; + _type: "foo"; + _updatedAt: string; + bar?: { _type: "bar"; + } & { baz?: boolean; - } - >(); + }; + }; + }>(); + }); + + it("infers non-object aliased type value", () => { + const config = defineConfig({ + dataset: "dataset", + projectId: "projectId", + schema: { + types: [ + defineType({ + name: "foo", + type: "document", + fields: [ + defineField({ + name: "bar", + type: "bar", + }), + ], + }), + defineType({ + name: "bar", + type: "string", + }), + ], + }, + }); + + type Values = InferSchemaValues; + + expectType().toStrictEqual<{ + bar: string; + foo: { + _createdAt: string; + _id: string; + _rev: string; + _type: "foo"; + _updatedAt: string; + bar?: string; + }; + }>(); }); it('adds "_type" to inferred named alias values in arrays', () => { @@ -2761,25 +2804,27 @@ describe("defineConfig", () => { }, }); - expectType>().toStrictEqual< - | { - _createdAt: string; - _id: string; - _rev: string; - _type: "foo"; - _updatedAt: string; - array?: ({ - _key: string; - _type: "aliasedMemberName"; - } & { - baz?: boolean; - })[]; - } - | { - _type: "bar"; + type Values = InferSchemaValues; + + expectType().toStrictEqual<{ + bar: { + _type: "bar"; + baz?: boolean; + }; + foo: { + _createdAt: string; + _id: string; + _rev: string; + _type: "foo"; + _updatedAt: string; + array?: ({ + _key: string; + _type: "aliasedMemberName"; + } & { baz?: boolean; - } - >(); + })[]; + }; + }>(); }); it("infers cyclical type value", () => { @@ -2803,9 +2848,11 @@ describe("defineConfig", () => { }, }); + type Values = InferSchemaValues; + // It really is cyclical! - expectType["foo"]["foo"]>().toStrictEqual< - InferSchemaValues["foo"] + expectType().toStrictEqual< + Values["foo"]["foo"] >(); }); @@ -2852,19 +2899,12 @@ describe("defineConfig", () => { }, }); + type Values = InferSchemaValues; + // It really is cyclical! expectType< - Extract< - InferSchemaValues, - // Gets us the Foo object - { _type: "foo" } - >["bar"]["baz"]["foo"]["bar"]["baz"]["foo"] - >().toStrictEqual< - Extract< - InferSchemaValues, - { _type: "foo" } - >["bar"]["baz"]["foo"] - >(); + Values["foo"]["bar"]["baz"]["foo"]["bar"]["baz"]["foo"] + >().toStrictEqual(); }); it("infers plugin type value", () => { @@ -2906,16 +2946,20 @@ describe("defineConfig", () => { ], }); - expectType>().toStrictEqual<{ - _createdAt: string; - _id: string; - _rev: string; - _type: "foo"; - _updatedAt: string; - pluginValue?: { - _type: "pluginValue"; - } & { - baz?: boolean; + type Values = InferSchemaValues; + + expectType().toStrictEqual<{ + foo: { + _createdAt: string; + _id: string; + _rev: string; + _type: "foo"; + _updatedAt: string; + pluginValue?: { + _type: "pluginValue"; + } & { + baz?: boolean; + }; }; }>(); }); @@ -2959,13 +3003,17 @@ describe("defineConfig", () => { ], }); - expectType>().toStrictEqual<{ - _createdAt: string; - _id: string; - _rev: string; - _type: "foo"; - _updatedAt: string; - pluginValue?: unknown; + type Values = InferSchemaValues; + + expectType().toStrictEqual<{ + foo: { + _createdAt: string; + _id: string; + _rev: string; + _type: "foo"; + _updatedAt: string; + pluginValue?: unknown; + }; }>(); }); }); @@ -3046,20 +3094,22 @@ describe("definePlugin", () => { }, })(); - expectType>().toStrictEqual< - | { - _createdAt: string; - _id: string; - _rev: string; - _type: "foo"; - _updatedAt: string; - bar?: boolean; - } - | { - _type: "baz"; - qux?: boolean; - } - >(); + type Values = InferSchemaValues; + + expectType().toStrictEqual<{ + baz: { + _type: "baz"; + qux?: boolean; + }; + foo: { + _createdAt: string; + _id: string; + _rev: string; + _type: "foo"; + _updatedAt: string; + bar?: boolean; + }; + }>(); }); it("infers aliased type value", () => { @@ -3091,24 +3141,64 @@ describe("definePlugin", () => { }, })(); - expectType>().toStrictEqual< - | { - _createdAt: string; - _id: string; - _rev: string; - _type: "foo"; - _updatedAt: string; - bar?: { - _type: "bar"; - } & { - baz?: boolean; - }; - } - | { + type Values = InferSchemaValues; + + expectType().toStrictEqual<{ + bar: { + _type: "bar"; + baz?: boolean; + }; + foo: { + _createdAt: string; + _id: string; + _rev: string; + _type: "foo"; + _updatedAt: string; + bar?: { _type: "bar"; + } & { baz?: boolean; - } - >(); + }; + }; + }>(); + }); + + it("infers non-object aliased type value", () => { + const plugin = definePlugin({ + name: "plugin", + schema: { + types: [ + defineType({ + name: "foo", + type: "document", + fields: [ + defineField({ + name: "bar", + type: "bar", + }), + ], + }), + defineType({ + name: "bar", + type: "string", + }), + ], + }, + })(); + + type Values = InferSchemaValues; + + expectType().toStrictEqual<{ + bar: string; + foo: { + _createdAt: string; + _id: string; + _rev: string; + _type: "foo"; + _updatedAt: string; + bar?: string; + }; + }>(); }); it('adds "_type" to inferred named alias values in arrays', () => { @@ -3146,25 +3236,27 @@ describe("definePlugin", () => { }, })(); - expectType>().toStrictEqual< - | { - _createdAt: string; - _id: string; - _rev: string; - _type: "foo"; - _updatedAt: string; - array?: ({ - _key: string; - _type: "aliasedMemberName"; - } & { - baz?: boolean; - })[]; - } - | { - _type: "bar"; + type Values = InferSchemaValues; + + expectType().toStrictEqual<{ + bar: { + _type: "bar"; + baz?: boolean; + }; + foo: { + _createdAt: string; + _id: string; + _rev: string; + _type: "foo"; + _updatedAt: string; + array?: ({ + _key: string; + _type: "aliasedMemberName"; + } & { baz?: boolean; - } - >(); + })[]; + }; + }>(); }); it("infers cyclical type value", () => { @@ -3187,9 +3279,11 @@ describe("definePlugin", () => { }, })(); + type Values = InferSchemaValues; + // It really is cyclical! - expectType["foo"]["foo"]>().toStrictEqual< - InferSchemaValues["foo"] + expectType().toStrictEqual< + Values["foo"]["foo"] >(); }); @@ -3235,19 +3329,12 @@ describe("definePlugin", () => { }, })(); + type Values = InferSchemaValues; + // It really is cyclical! expectType< - Extract< - InferSchemaValues, - // Gets us the Foo object - { _type: "foo" } - >["bar"]["baz"]["foo"]["bar"]["baz"]["foo"] - >().toStrictEqual< - Extract< - InferSchemaValues, - { _type: "foo" } - >["bar"]["baz"]["foo"] - >(); + Values["foo"]["bar"]["baz"]["foo"]["bar"]["baz"]["foo"] + >().toStrictEqual(); }); it("infers plugin type value", () => { @@ -3288,16 +3375,20 @@ describe("definePlugin", () => { ], })(); - expectType>().toStrictEqual<{ - _createdAt: string; - _id: string; - _rev: string; - _type: "foo"; - _updatedAt: string; - pluginValue?: { - _type: "pluginValue"; - } & { - baz?: boolean; + type Values = InferSchemaValues; + + expectType().toStrictEqual<{ + foo: { + _createdAt: string; + _id: string; + _rev: string; + _type: "foo"; + _updatedAt: string; + pluginValue?: { + _type: "pluginValue"; + } & { + baz?: boolean; + }; }; }>(); }); @@ -3340,13 +3431,17 @@ describe("definePlugin", () => { ], })(); - expectType>().toStrictEqual<{ - _createdAt: string; - _id: string; - _rev: string; - _type: "foo"; - _updatedAt: string; - pluginValue?: unknown; + type Values = InferSchemaValues; + + expectType().toStrictEqual<{ + foo: { + _createdAt: string; + _id: string; + _rev: string; + _type: "foo"; + _updatedAt: string; + pluginValue?: unknown; + }; }>(); }); }); diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 27501543..dfc95d41 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -102,15 +102,13 @@ type DefinitionBase< validation?: ValidationBuilder; }; -export type _InferValue> = - Def extends DefinitionBase ? Value : never; - /** - * @deprecated Use {@link InferSchemaValues} instead. Otherwise, you won't get any aliased types (e.g. named object types, plugin types). + * Infers the Value of a Definition, without aliased types. + * + * @private */ -export type InferValue = Def extends DefinitionBase - ? _InferValue - : never; +export type _InferValue> = + Def extends DefinitionBase ? Value : never; type RewriteValue> = Merge< { @@ -644,21 +642,27 @@ export const defineConfig = < type ExpandAliasValues< Value, - TAliasedDefinition extends Type<"object", any, any, any, any, any> + TAliasedDefinition extends Type > = Value extends AliasValue ? Extract< TAliasedDefinition, - Type<"object", TType, any, any, any, any> + Type > extends never ? unknown : ExpandAliasValues< _InferValue< - Extract> + Extract> >, TAliasedDefinition - > & { - _type: TType; - } + > & + (Extract< + TAliasedDefinition, + Type<"object", TType, any, any, any, any> + > extends never + ? unknown + : { + _type: TType; + }) : Value extends (infer Item)[] ? (Item extends ObjectArrayMemberValue ? Item extends { [key: string]: any } @@ -679,25 +683,29 @@ export type InferSchemaValues< > = TConfig extends MaybeArray< ConfigBase > - ? ExpandAliasValues< - TTypeDefinition extends Type< - "object", + ? { + [TName in TTypeDefinition extends Type< + any, infer TName extends string, any, any, any, any > - ? _InferValue & { _type: TName } - : _InferValue, - Extract< + ? TName + : never]: ExpandAliasValues< + TTypeDefinition extends Type<"object", TName, any, any, any, any> + ? _InferValue & { _type: TName } + : TTypeDefinition extends Type + ? _InferValue + : never, + // TPluginTypeDefinition | TTypeDefinition | (Type extends TPluginTypeDefinition ? never : TPluginTypeDefinition) | (Type extends TTypeDefinition ? never - : TTypeDefinition), - Type<"object", any, any, any, any, any> - > - > + : TTypeDefinition) + >; + } : never;