Skip to content

Instantly share code, notes, and snippets.

@sotnikov-link
Last active March 10, 2023 05:49
Show Gist options
  • Save sotnikov-link/61a7cf264b2637f063023f2700073c85 to your computer and use it in GitHub Desktop.
Save sotnikov-link/61a7cf264b2637f063023f2700073c85 to your computer and use it in GitHub Desktop.
Type PartialNullableDeep and tests
import { assertType, describe, expectTypeOf, it } from 'vitest';
import { type PartialNullableDeep } from './partial-nullable-deep';
describe('PartialNullableDeep', () => {
describe('RequiredStringKey', () => {
type RequiredStringKey = PartialNullableDeep<{ abc: string }>;
it('required key is in place', () => {
expectTypeOf({ abc: 'xyz' }).toEqualTypeOf<RequiredStringKey>();
});
it('required key cannot be null', () => {
expectTypeOf({ abc: null }).not.toEqualTypeOf<RequiredStringKey>();
});
it('deep required key is in place', () => {
expectTypeOf({
level1: { level2: { abc: 'abc' } },
}).toEqualTypeOf<
PartialNullableDeep<{ level1: { level2: { abc: string } } }>
>();
});
it('deep required key cannot be null', () => {
expectTypeOf({
level1: { level2: { abc: null } },
}).not.toMatchTypeOf<
PartialNullableDeep<{ level1: { level2: { abc: string } } }>
>();
});
});
describe('OptionalStringKey', () => {
type OptionalStringKey = PartialNullableDeep<{ abc?: string }>;
it('optional key can be null', () => {
expectTypeOf({ abc: null }).toMatchTypeOf<OptionalStringKey>();
});
it('optional key can be undefined', () => {
expectTypeOf({ abc: undefined }).toMatchTypeOf<OptionalStringKey>();
});
it('optional key can be missed', () => {
expectTypeOf({}).toMatchTypeOf<OptionalStringKey>();
});
});
it('required and missed optional keys', () => {
expectTypeOf({ xyz: 1 }).toMatchTypeOf<
PartialNullableDeep<{ abc?: string; xyz: number }>
>();
});
it('required 1 level key and optional 2 level key can be null', () => {
expectTypeOf({
obj: { abc: null },
}).toMatchTypeOf<PartialNullableDeep<{ obj: { abc?: string } }>>();
});
it(
'optional 1 level key can be null ' +
'when exists optional 2 level key exists',
() => {
assertType<PartialNullableDeep<{ obj?: { abc?: string } }>>({
obj: null,
});
}
);
describe('optional key is string or object', () => {
type OptionalKeyIsStringOrPartialObject = PartialNullableDeep<{
hmm?: string | { abc?: string; xyz: number };
}>;
it('missed key', () => {
expectTypeOf({}).toMatchTypeOf<OptionalKeyIsStringOrPartialObject>();
});
it('null', () => {
expectTypeOf({
hmm: null,
}).toMatchTypeOf<OptionalKeyIsStringOrPartialObject>();
});
it('string', () => {
expectTypeOf({
hmm: 'abc',
}).toMatchTypeOf<OptionalKeyIsStringOrPartialObject>();
});
it('partial object', () => {
expectTypeOf({
hmm: {
xyz: 123,
},
}).toMatchTypeOf<OptionalKeyIsStringOrPartialObject>();
});
});
});
import { type OptionalKeysOf } from 'type-fest';
type NullForOptionalKey<
Base extends object,
Key extends keyof Base
> = Key extends OptionalKeysOf<Base> ? null : never;
export type PartialNullableDeep<Base extends object> = {
[Key in keyof Base]: Extract<Base[Key], object> extends never
? // exactly it is not object
Base[Key] | NullForOptionalKey<Base, Key>
: // maybe it is object
| Exclude<Base[Key], object>
| NullForOptionalKey<Base, Key>
| PartialNullableDeep<Extract<Base[Key], object>>;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment