Skip to content

Commit

Permalink
feat: add partial modifier
Browse files Browse the repository at this point in the history
  • Loading branch information
lukeed committed Aug 14, 2024
1 parent ac25ad7 commit 56041e6
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 0 deletions.
64 changes: 64 additions & 0 deletions infer.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -674,3 +674,67 @@ assert<ONE1>(STRING);
assert<ONE1>(NUMBER);
assert<ONE1>(false);
assert<ONE1>(true);

// ---
// PARTIAL
// ---

let pa1 = t.partial(
t.object({
foo: t.string(),
bar: t.array(t.number()),
}),
);

type PA1 = t.Infer<typeof pa1>;
declare let PA1: PA1;

assert<{
foo?: string;
bar?: number[];
}>(PA1);

assert<PA1>({});
assert<PA1>({ foo: STRING });

let pa2 = t.partial(
t.object({
foo: t.string(),
bar: t.object({
name: t.string(),
age: t.number(),
}),
}),
);

type PA2 = t.Infer<typeof pa2>;
declare let PA2: PA2;

assert<{
foo?: string;
bar?: {
name: string;
age: number;
};
}>(PA2);

assert<PA2>({
foo: STRING,
});

assert<PA2>({
bar: {
name: STRING,
age: NUMBER,
},
});

assert<PA2>({
// @ts-expect-error missing keys
bar: {},
});

let _pa3 = t.partial(
// @ts-expect-error wrong type
t.string(),
);
54 changes: 54 additions & 0 deletions mod.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,60 @@ describe('Readonly', () => {
});
});

describe('Partial', () => {
it('should be a function', () => {
assert(typeof t.partial === 'function');
});

it('should be JSON schema', () => {
let output = t.partial(
t.object({
name: t.string(),
}),
);

assertEquals(output, {
type: 'object',
additionalProperties: false,
properties: {
name: {
type: 'string',
},
},
});
});

it('should not mutate input', () => {
let original = t.object({
name: t.string(),
});

assertEquals(original, {
type: 'object',
required: ['name'],
additionalProperties: false,
properties: {
name: {
type: 'string',
},
},
});

let _ = t.partial(original);

assertEquals(original, {
type: 'object',
required: ['name'],
additionalProperties: false,
properties: {
name: {
type: 'string',
},
},
});
});
});

// ---

describe('t.not', () => {
Expand Down
32 changes: 32 additions & 0 deletions mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,37 @@ function _readonly<T extends Type>(field: T): _readonly<T> {
};
}

/**
* Marks all properties within an {@link object} as optional.
*
* The `input` schema is not modified.
*
* > [!IMPORTANT]
* > Only accepts an {@link object} schema!
*
* @example
* ```ts
* let person = t.partial(
* t.object({
* name: t.string(),
* age: t.integer(),
* }),
* );
*
* type Person = t.Infer<typeof person>;
* //-> {
* //-> name?: string;
* //-> age?: number;
* //-> }
* ```
*/
function _partial<P extends Properties>(input: _object<P>) {
// deno-lint-ignore no-unused-vars
let { required, ...schema } = input;
// @ts-expect-error; needs distinct type else array overlap
return schema as _object<Partial<P>>;
}

/**
* The `null` type.
*
Expand Down Expand Up @@ -773,6 +804,7 @@ export {
// modifiers
_optional as optional,
_readonly as readonly,
_partial as partial,
// literals
_null as null,
_constant as constant,
Expand Down

0 comments on commit 56041e6

Please sign in to comment.