mirror of
https://github.com/sindresorhus/type-fest.git
synced 2025-12-08 19:25:05 +00:00
Schema: Add recurseIntoArrays option (#960)
Co-authored-by: Grigoris Christainas <grigorisxristainas@gmail.com>
This commit is contained in:
parent
d7b692bb04
commit
fbbb8ba53b
2
index.d.ts
vendored
2
index.d.ts
vendored
@ -66,7 +66,7 @@ export type {SimplifyDeep} from './source/simplify-deep';
|
||||
export type {Jsonify} from './source/jsonify';
|
||||
export type {Jsonifiable} from './source/jsonifiable';
|
||||
export type {StructuredCloneable} from './source/structured-cloneable';
|
||||
export type {Schema} from './source/schema';
|
||||
export type {Schema, SchemaOptions} from './source/schema';
|
||||
export type {LiteralToPrimitive} from './source/literal-to-primitive';
|
||||
export type {LiteralToPrimitiveDeep} from './source/literal-to-primitive-deep';
|
||||
export type {
|
||||
|
||||
57
source/schema.d.ts
vendored
57
source/schema.d.ts
vendored
@ -19,6 +19,7 @@ interface User {
|
||||
created: Date;
|
||||
active: boolean;
|
||||
passwordHash: string;
|
||||
attributes: ['Foo', 'Bar']
|
||||
}
|
||||
|
||||
type UserMask = Schema<User, 'mask' | 'hide' | 'show'>;
|
||||
@ -32,12 +33,13 @@ const userMaskSettings: UserMask = {
|
||||
created: 'show',
|
||||
active: 'show',
|
||||
passwordHash: 'hide',
|
||||
attributes: ['mask', 'show']
|
||||
}
|
||||
```
|
||||
|
||||
@category Object
|
||||
*/
|
||||
export type Schema<ObjectType, ValueType> = ObjectType extends string
|
||||
export type Schema<ObjectType, ValueType, Options extends SchemaOptions = {}> = ObjectType extends string
|
||||
? ValueType
|
||||
: ObjectType extends Map<unknown, unknown>
|
||||
? ValueType
|
||||
@ -48,7 +50,9 @@ export type Schema<ObjectType, ValueType> = ObjectType extends string
|
||||
: ObjectType extends ReadonlySet<unknown>
|
||||
? ValueType
|
||||
: ObjectType extends Array<infer U>
|
||||
? Array<Schema<U, ValueType>>
|
||||
? Options['recurseIntoArrays'] extends false | undefined
|
||||
? ValueType
|
||||
: Array<Schema<U, ValueType>>
|
||||
: ObjectType extends (...arguments_: unknown[]) => unknown
|
||||
? ValueType
|
||||
: ObjectType extends Date
|
||||
@ -58,14 +62,53 @@ export type Schema<ObjectType, ValueType> = ObjectType extends string
|
||||
: ObjectType extends RegExp
|
||||
? ValueType
|
||||
: ObjectType extends object
|
||||
? SchemaObject<ObjectType, ValueType>
|
||||
? SchemaObject<ObjectType, ValueType, Options>
|
||||
: ValueType;
|
||||
|
||||
/**
|
||||
Same as `Schema`, but accepts only `object`s as inputs. Internal helper for `Schema`.
|
||||
*/
|
||||
type SchemaObject<ObjectType extends object, K> = {
|
||||
[KeyType in keyof ObjectType]: ObjectType[KeyType] extends readonly unknown[] | unknown[]
|
||||
? Schema<ObjectType[KeyType], K>
|
||||
: Schema<ObjectType[KeyType], K> | K;
|
||||
type SchemaObject<
|
||||
ObjectType extends object,
|
||||
K,
|
||||
Options extends SchemaOptions,
|
||||
> = {
|
||||
[KeyType in keyof ObjectType]: ObjectType[KeyType] extends
|
||||
| readonly unknown[]
|
||||
| unknown[]
|
||||
? Options['recurseIntoArrays'] extends false | undefined
|
||||
? K
|
||||
: Schema<ObjectType[KeyType], K, Options>
|
||||
: Schema<ObjectType[KeyType], K, Options> | K;
|
||||
};
|
||||
|
||||
/**
|
||||
@see Schema
|
||||
*/
|
||||
export type SchemaOptions = {
|
||||
/**
|
||||
By default, this affects elements in array and tuple types. You can change this by passing `{recurseIntoArrays: false}` as the third type argument:
|
||||
- If `recurseIntoArrays` is set to `true` (default), array elements will be recursively processed as well.
|
||||
- If `recurseIntoArrays` is set to `false`, arrays will not be recursively processed, and the entire array will be replaced with the given value type.
|
||||
|
||||
@example
|
||||
```
|
||||
type UserMask = Schema<User, 'mask' | 'hide' | 'show', {recurseIntoArrays: false}>;
|
||||
|
||||
const userMaskSettings: UserMask = {
|
||||
id: 'show',
|
||||
name: {
|
||||
firstname: 'show',
|
||||
lastname: 'mask',
|
||||
},
|
||||
created: 'show',
|
||||
active: 'show',
|
||||
passwordHash: 'hide',
|
||||
attributes: 'hide'
|
||||
}
|
||||
```
|
||||
|
||||
@default true
|
||||
*/
|
||||
readonly recurseIntoArrays?: boolean | undefined;
|
||||
};
|
||||
|
||||
@ -126,3 +126,77 @@ expectType<ComplexOption>(complexBarSchema.readonlySet);
|
||||
expectType<readonly ComplexOption[]>(complexBarSchema.readonlyArray);
|
||||
expectType<readonly [ComplexOption]>(complexBarSchema.readonlyTuple);
|
||||
expectType<ComplexOption>(complexBarSchema.regExp);
|
||||
|
||||
// With Options and `recurseIntoArrays` set to `false`
|
||||
type FooSchemaWithOptionsNoRecurse = Schema<typeof foo, FooOption, {recurseIntoArrays: false | undefined}>;
|
||||
|
||||
const fooSchemaWithOptionsNoRecurse: FooSchemaWithOptionsNoRecurse = {
|
||||
baz: 'A',
|
||||
bar: {
|
||||
function: 'A',
|
||||
object: {key: 'A'},
|
||||
string: 'A',
|
||||
number: 'A',
|
||||
boolean: 'A',
|
||||
symbol: 'A',
|
||||
map: 'A',
|
||||
set: 'A',
|
||||
array: 'A',
|
||||
tuple: 'A',
|
||||
objectArray: 'A',
|
||||
readonlyMap: 'A',
|
||||
readonlySet: 'A',
|
||||
readonlyArray: 'A' as const,
|
||||
readonlyTuple: 'A' as const,
|
||||
regExp: 'A',
|
||||
},
|
||||
};
|
||||
|
||||
expectNotAssignable<FooSchemaWithOptionsNoRecurse>(foo);
|
||||
expectNotAssignable<FooSchemaWithOptionsNoRecurse>({key: 'value'});
|
||||
expectNotAssignable<FooSchemaWithOptionsNoRecurse>(new Date());
|
||||
expectType<FooOption>(fooSchemaWithOptionsNoRecurse.baz);
|
||||
|
||||
const barSchemaWithOptionsNoRecurse = fooSchemaWithOptionsNoRecurse.bar as Schema<typeof foo['bar'], FooOption, {recurseIntoArrays: false | undefined}>;
|
||||
expectType<FooOption>(barSchemaWithOptionsNoRecurse.function);
|
||||
expectType<FooOption | {key: FooOption}>(barSchemaWithOptionsNoRecurse.object);
|
||||
expectType<FooOption>(barSchemaWithOptionsNoRecurse.string);
|
||||
expectType<FooOption>(barSchemaWithOptionsNoRecurse.number);
|
||||
expectType<FooOption>(barSchemaWithOptionsNoRecurse.boolean);
|
||||
expectType<FooOption>(barSchemaWithOptionsNoRecurse.symbol);
|
||||
expectType<FooOption>(barSchemaWithOptionsNoRecurse.map);
|
||||
expectType<FooOption>(barSchemaWithOptionsNoRecurse.set);
|
||||
expectType<FooOption>(barSchemaWithOptionsNoRecurse.array);
|
||||
expectType<FooOption>(barSchemaWithOptionsNoRecurse.tuple);
|
||||
expectType<FooOption>(barSchemaWithOptionsNoRecurse.objectArray);
|
||||
expectType<FooOption>(barSchemaWithOptionsNoRecurse.readonlyMap);
|
||||
expectType<FooOption>(barSchemaWithOptionsNoRecurse.readonlySet);
|
||||
expectType<FooOption>(barSchemaWithOptionsNoRecurse.readonlyArray);
|
||||
expectType<FooOption>(barSchemaWithOptionsNoRecurse.readonlyTuple);
|
||||
expectType<FooOption>(barSchemaWithOptionsNoRecurse.regExp);
|
||||
|
||||
// With Options and `recurseIntoArrays` set to `true`
|
||||
type FooSchemaWithOptionsRecurse = Schema<typeof foo, FooOption, {recurseIntoArrays: true}>;
|
||||
|
||||
expectNotAssignable<FooSchemaWithOptionsRecurse>(foo);
|
||||
expectNotAssignable<FooSchemaWithOptionsRecurse>({key: 'value'});
|
||||
expectNotAssignable<FooSchemaWithOptionsRecurse>(new Date());
|
||||
expectType<FooOption>(fooSchema.baz);
|
||||
|
||||
const barSchemaWithOptionsRecurse = fooSchema.bar as Schema<typeof foo['bar'], FooOption, {recurseIntoArrays: true}>;
|
||||
expectType<FooOption>(barSchemaWithOptionsRecurse.function);
|
||||
expectType<FooOption | {key: FooOption}>(barSchemaWithOptionsRecurse.object);
|
||||
expectType<FooOption>(barSchemaWithOptionsRecurse.string);
|
||||
expectType<FooOption>(barSchemaWithOptionsRecurse.number);
|
||||
expectType<FooOption>(barSchemaWithOptionsRecurse.boolean);
|
||||
expectType<FooOption>(barSchemaWithOptionsRecurse.symbol);
|
||||
expectType<FooOption>(barSchemaWithOptionsRecurse.map);
|
||||
expectType<FooOption>(barSchemaWithOptionsRecurse.set);
|
||||
expectType<FooOption[]>(barSchemaWithOptionsRecurse.array);
|
||||
expectType<FooOption[]>(barSchemaWithOptionsRecurse.tuple);
|
||||
expectType<Array<{key: FooOption}>>(barSchemaWithOptionsRecurse.objectArray);
|
||||
expectType<FooOption>(barSchemaWithOptionsRecurse.readonlyMap);
|
||||
expectType<FooOption>(barSchemaWithOptionsRecurse.readonlySet);
|
||||
expectType<readonly FooOption[]>(barSchemaWithOptionsRecurse.readonlyArray);
|
||||
expectType<readonly [FooOption]>(barSchemaWithOptionsRecurse.readonlyTuple);
|
||||
expectType<FooOption>(barSchemaWithOptionsRecurse.regExp);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user