Skip to content

Commit e593f8f

Browse files
shaungradyShaun Grady
and
Shaun Grady
authored
feat: Allow schema metadata to be strongly typed (#2021)
* feat: Allow schema metadata to be strongly typed * fix: default `SchemaMetadata` typing, export types, update README ToC --------- Co-authored-by: Shaun Grady <[email protected]>
1 parent 6dfc75a commit e593f8f

File tree

3 files changed

+41
-9
lines changed

3 files changed

+41
-9
lines changed

README.md

+23-3
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ const parsedUser = await userSchema.validate(
9595
- [`Schema`](#schema)
9696
- [`Schema.clone(): Schema`](#schemaclone-schema)
9797
- [`Schema.label(label: string): Schema`](#schemalabellabel-string-schema)
98-
- [`Schema.meta(metadata: object): Schema`](#schemametametadata-object-schema)
98+
- [`Schema.meta(metadata: SchemaMetadata): Schema`](#schemametametadata-schemametadata-schema)
9999
- [`Schema.describe(options?: ResolveOptions): SchemaDescription`](#schemadescribeoptions-resolveoptions-schemadescription)
100100
- [`Schema.concat(schema: Schema): Schema`](#schemaconcatschema-schema-schema)
101101
- [`Schema.validate(value: any, options?: object): Promise<InferType<Schema>, ValidationError>`](#schemavalidatevalue-any-options-object-promiseinfertypeschema-validationerror)
@@ -631,10 +631,30 @@ Creates a deep copy of the schema. Clone is used internally to return a new sche
631631

632632
Overrides the key name which is used in error messages.
633633

634-
#### `Schema.meta(metadata: object): Schema`
634+
#### `Schema.meta(metadata: SchemaMetadata): Schema`
635635

636636
Adds to a metadata object, useful for storing data with a schema, that doesn't belong
637-
the cast object itself.
637+
to the cast object itself.
638+
639+
A custom `SchemaMetadata` interface can be defined through
640+
[merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#merging-interfaces)
641+
with the `CustomSchemaMetadata` interface. Start by creating a `yup.d.ts` file
642+
in your package and creating your desired `CustomSchemaMetadata` interface:
643+
644+
```ts
645+
// yup.d.ts
646+
import 'yup';
647+
648+
declare module 'yup' {
649+
// Define your desired `SchemaMetadata` interface by merging the
650+
// `CustomSchemaMetadata` interface.
651+
export interface CustomSchemaMetadata {
652+
placeholderText?: string
653+
tooltipText?: string
654+
//
655+
}
656+
}
657+
```
638658

639659
#### `Schema.describe(options?: ResolveOptions): SchemaDescription`
640660

src/index.ts

+4
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ import Schema, {
2727
SchemaLazyDescription,
2828
SchemaFieldDescription,
2929
SchemaDescription,
30+
SchemaMetadata,
31+
CustomSchemaMetadata,
3032
} from './schema';
3133
import type {
3234
InferType,
@@ -77,6 +79,8 @@ export type {
7779
SchemaLazyDescription,
7880
SchemaFieldDescription,
7981
SchemaDescription,
82+
SchemaMetadata,
83+
CustomSchemaMetadata,
8084
LocaleObject,
8185
ValidateOptions,
8286
DefaultThunk,

src/schema.ts

+14-6
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,17 @@ export type SchemaSpec<TDefault> = {
4444
strict?: boolean;
4545
recursive?: boolean;
4646
label?: string | undefined;
47-
meta?: any;
47+
meta?: SchemaMetadata;
4848
};
4949

50+
export interface CustomSchemaMetadata {}
51+
52+
// If `CustomSchemaMeta` isn't extended with any keys, we'll fall back to a
53+
// loose Record definition allowing free form usage.
54+
export type SchemaMetadata = keyof CustomSchemaMetadata extends never
55+
? Record<PropertyKey, any>
56+
: CustomSchemaMetadata;
57+
5058
export type SchemaOptions<TType, TDefault> = {
5159
type: string;
5260
spec?: Partial<SchemaSpec<TDefault>>;
@@ -111,7 +119,7 @@ export interface SchemaObjectDescription extends SchemaDescription {
111119
export interface SchemaLazyDescription {
112120
type: string;
113121
label?: string;
114-
meta: object | undefined;
122+
meta?: SchemaMetadata;
115123
}
116124

117125
export type SchemaFieldDescription =
@@ -124,7 +132,7 @@ export type SchemaFieldDescription =
124132
export interface SchemaDescription {
125133
type: string;
126134
label?: string;
127-
meta: object | undefined;
135+
meta?: SchemaMetadata;
128136
oneOf: unknown[];
129137
notOneOf: unknown[];
130138
default?: unknown;
@@ -234,9 +242,9 @@ export default abstract class Schema<
234242
return next;
235243
}
236244

237-
meta(): Record<string, unknown> | undefined;
238-
meta(obj: Record<string, unknown>): this;
239-
meta(...args: [Record<string, unknown>?]) {
245+
meta(): SchemaMetadata | undefined;
246+
meta(obj: SchemaMetadata): this;
247+
meta(...args: [SchemaMetadata?]) {
240248
if (args.length === 0) return this.spec.meta;
241249

242250
let next = this.clone();

0 commit comments

Comments
 (0)